C++智能指针与vector多态问题:插入unique_ptr触发C2664编译错误
解决
std::unique_ptr存入vector时的C2664编译错误 嘿,这个编译错误我太熟了——你这是踩了std::unique_ptr最核心的特性坑:它是独占式智能指针,不允许被拷贝,而你试图把它直接拷贝进vector,编译器当然要报错啦!
错误原因拆解
你看到的Error C2664本质是:std::unique_ptr的拷贝构造函数是被显式删除的(= delete),因为它设计的初衷就是确保同一时间只有一个unique_ptr拥有对象的所有权。如果你写了类似vec.push_back(my_ptr);这样的代码,编译器会尝试调用拷贝构造,这直接违反了unique_ptr的设计规则,所以必然报错。
针对你的场景的解决方案
你的需求是解析文件创建不同子类对象,存入vector<unique_ptr<基类>>,这里有几种安全且符合规范的写法:
1. 直接在push_back中构造并转移所有权
用std::make_unique直接创建智能指针,然后把它的所有权转移给vector——因为make_unique返回的是右值,push_back会自动调用移动构造(而不是拷贝):
std::vector<std::unique_ptr<Test>> obj_vec; // 解析到字符'A'时创建SubTestA对象 if (parsed_char == 'A') { obj_vec.push_back(std::make_unique<SubTestA>()); }
2. 用std::move显式转移所有权
如果你需要先对智能指针做一些初始化操作,再存入vector,可以用std::move把局部指针的所有权转移给容器元素:
auto ptr = std::make_unique<SubTestB>(); // 这里可以对ptr指向的对象做一些初始化操作 ptr->init_some_data(); // 显式转移所有权,之后ptr就变成空指针了,不能再使用它 obj_vec.push_back(std::move(ptr));
3. 用emplace_back直接在容器内构造对象
emplace_back可以直接在vector的内存空间里构造unique_ptr,比push_back更高效(减少一次移动操作):
// 直接在vector内构造SubTestA的unique_ptr obj_vec.emplace_back(std::make_unique<SubTestA>()); // 或者直接用new(但更推荐make_unique,避免内存泄漏风险) obj_vec.emplace_back(new SubTestA());
必加的关键细节:基类的虚析构函数
因为你用基类指针指向子类对象,一定要给基类Test添加虚析构函数,否则unique_ptr销毁对象时只会调用基类的析构函数,导致子类的资源无法正确释放,引发未定义行为:
class Test { public: // 虚析构函数,确保子类对象能被正确销毁 virtual ~Test() = default; // 其他纯虚接口函数... };
完整示例代码
#include <vector> #include <memory> #include <fstream> // 基类必须有虚析构 class Test { public: virtual ~Test() = default; virtual void process() = 0; // 纯虚接口 }; class SubTestA : public Test { public: void process() override { // SubTestA的处理逻辑 } }; class SubTestB : public Test { public: void process() override { // SubTestB的处理逻辑 } }; int main() { std::vector<std::unique_ptr<Test>> objects; std::ifstream input_file("your_file.txt"); char type; while (input_file >> type) { switch (type) { case 'A': objects.emplace_back(std::make_unique<SubTestA>()); break; case 'B': auto ptr = std::make_unique<SubTestB>(); // 可以在这里做初始化 objects.push_back(std::move(ptr)); break; default: // 处理未知类型 break; } } // 遍历调用子类方法 for (const auto& obj : objects) { obj->process(); } return 0; }
内容的提问来源于stack exchange,提问作者user9237024
相关产品推荐
相关产品推荐

