You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在类中正确使用std::list存储抽象类派生对象?

问题分析与解决方案

问题背景

示例代码:

#include <list>
#include <cstdlib>
#include <cstdio>

class cont;

class elem {
public:
    virtual void do_something(int *buff) = 0;
};

class myelem1 : public elem {
public:
    virtual void do_something(int *buff) {
        buff[1] = 42;
    }
};

class myelem2 : public elem {
public:
    virtual void do_something(int *buff) {
        buff[2] = 33;
    }
};

class cont {
private:
    int *buff, size;
    std::list<elem> sub;

public:
    cont(int size) : buff((int*)malloc(size*sizeof(int))), size(size) {}
    ~cont() { if (buff) free(buff); }
    void add_worker(const elem &e) {sub.push_back(e);}
    void work() {
        for (elem & e : sub)
            e.do_something(buff);
    }
    void print() {
        for (int i=0; i<size; i++)
            printf("%03d ", buff[i]);
        printf("\n");
    }
};

int main() {
    cont c(10);
    c.add_worker(myelem1());
    c.add_worker(myelem2());
    c.work();
    c.print();
}

需求总结:

  • cont类内部管理待填充的数据集
  • 多个elem抽象类的派生类,各自负责填充数据集的特定部分
  • 编译时无法确定使用哪些派生类,需运行时按需创建
  • 实际数据集并非简单数组

遇到的问题:

  1. 原代码编译失败,报错:
/usr/include/c++/12/ext/aligned_buffer.h:54:25: error: cannot declare field ‘__gnu_cxx::__aligned_membuf<elem>::_Tp2::_M_t’ to be of abstract type ‘elem’
  1. 将elem的纯虚函数改为空实现后,代码可编译,但cont::buff未被填充。

核心问题解析

1. 编译失败的原因

elem是抽象类(包含纯虚函数),C++不允许直接实例化抽象类。而cont类中用std::list<elem> sub;存储的是elem对象,当尝试将派生类对象放入列表时,需要创建elem类型的实例,这违反了抽象类不能实例化的规则,因此编译报错。

2. 编译通过但无效果的原因

把纯虚函数改成空实现后,elem不再是抽象类,但出现了对象切片问题:当你把myelem1/myelem2对象传递给add_worker(参数是const elem&)并调用push_back时,只会复制基类elem的部分,派生类特有的虚函数表等信息会被“切掉”。后续遍历列表调用do_something时,实际调用的是基类elem的空实现,而非派生类的重载方法,因此buff没有被填充。


解决方案:使用指针/智能指针实现多态

C++的多态必须通过指针或引用来实现,因此需要将容器改为存储基类指针。为了避免手动管理内存泄漏,推荐使用智能指针(如std::unique_ptr)。

修改后的代码示例:

#include <list>
#include <memory>
#include <cstdlib>
#include <cstdio>
#include <cstring>

class elem {
public:
    virtual void do_something(int *buff) = 0;
    // 抽象类必须声明虚析构函数,避免派生类对象内存泄漏
    virtual ~elem() = default;
};

class myelem1 : public elem {
public:
    void do_something(int *buff) override {
        buff[1] = 42;
    }
};

class myelem2 : public elem {
public:
    void do_something(int *buff) override {
        buff[2] = 33;
    }
};

class cont {
private:
    int *buff, size;
    // 存储智能指针,自动管理内存
    std::list<std::unique_ptr<elem>> sub;

public:
    cont(int size) : buff(static_cast<int*>(malloc(size*sizeof(int)))), size(size) {
        // 初始化buff为0,避免垃圾值干扰结果
        if (buff) memset(buff, 0, size*sizeof(int));
    }
    ~cont() { 
        if (buff) free(buff); 
    }

    // 接收unique_ptr并转移所有权
    void add_worker(std::unique_ptr<elem> e) {
        sub.push_back(std::move(e));
    }

    void work() {
        for (auto & e : sub) {
            e->do_something(buff);
        }
    }

    void print() {
        for (int i=0; i<size; i++)
            printf("%03d ", buff[i]);
        printf("\n");
    }
};

int main() {
    cont c(10);
    // 创建派生类对象的智能指针,转移给cont
    c.add_worker(std::make_unique<myelem1>());
    c.add_worker(std::make_unique<myelem2>());
    c.work();
    c.print();
}

关键修改点:

  • 给抽象类elem添加虚析构函数:确保销毁基类指针时,派生类的析构函数能被正确调用,避免内存泄漏。
  • 将容器改为std::list<std::unique_ptr<elem>>:存储基类智能指针,支持多态,同时自动管理内存。
  • 修改add_worker接口:接收std::unique_ptr<elem>并通过std::move转移所有权,避免拷贝。
  • 初始化buff为0:避免打印垃圾值,方便观察填充结果。

内容的提问来源于stack exchange,提问作者ZioByte

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 10:48:23