使用std::unique_ptr管理抽象类子类实例的报错问题求解
问题描述
我尝试实现一个工厂方法,用于创建抽象类Shape的子类实例,代码如下:
Shape* makeChild(Type type) const { switch(type) { case QUAD: return new Quad(); case RECTANGLE: return new Rectangle(); case TRIANGLE: return new Triangle(); default: return new Rectangle(); } }
现在我希望用std::unique_ptr<Shape>自动管理这些实例,于是编写了ShapeTree结构体:
struct ShapeTree { int index_; std::vector<std::unique_ptr<Shape>> shapes_; void add(Shape* shape) {shapes_.push_back(std::make_unique<Shape>(shape));} void removeLast(){shapes_.pop_back();} ShapeTree(const int index) : index_{index}{} ShapeTree(const int index, std::vector<std::unique_ptr<Shape>>& shapes) : index_{index}, shapes_{std::move(shapes)}{}};
但编译报错:一是Shape为抽象类无法实例化,二是Shape的默认拷贝构造函数被删除。而使用裸指针的ShapeTree版本可正常运行:
struct ShapeTree { int index_; std::vector<Shape*> shapes_; void add(Shape* shape) {shapes_.push_back(shape);} void removeLast(){shapes_.pop_back();} ShapeTree(const int index) : index_{index}{} ShapeTree(const int index, std::vector<Shape*>& shapes) : index_{index}, shapes_{std::move(shapes)}{}};
请问我哪里出错了?如何用std::unique_ptr实现相同功能?
错误原因
- 抽象类无法实例化:
std::make_unique<Shape>(shape)试图直接构造抽象类Shape的实例,这是C++禁止的——抽象类包含纯虚函数,不能被直接实例化。你实际需要的是将已有的子类实例(Quad/Rectangle/Triangle)的所有权转移给unique_ptr,而非新建Shape对象。 - 拷贝构造函数被删除:即便
Shape不是抽象类,std::make_unique<Shape>(shape)会尝试调用Shape的拷贝构造函数,但抽象类因包含虚函数,默认拷贝构造通常会被编译器删除;且此处根本不需要拷贝,只需接管已有指针的所有权。
修正方案
1. 修复add方法核心问题
不需要用std::make_unique创建新对象,直接用std::unique_ptr的构造函数接管裸指针所有权:
void add(Shape* shape) { // 直接封装裸指针,转移所有权 shapes_.emplace_back(shape); // 等价写法:shapes_.push_back(std::unique_ptr<Shape>(shape)); }
2. 优化工厂方法(推荐)
让工厂方法直接返回std::unique_ptr<Shape>,彻底避免裸指针暴露,更符合RAII原则:
std::unique_ptr<Shape> makeChild(Type type) const { switch(type) { case QUAD: return std::make_unique<Quad>(); case RECTANGLE: return std::make_unique<Rectangle>(); case TRIANGLE: return std::make_unique<Triangle>(); default: return std::make_unique<Rectangle>(); } }
对应的add方法改成接收unique_ptr,明确所有权转移语义:
void add(std::unique_ptr<Shape> shape) { shapes_.push_back(std::move(shape)); }
调用时直接传入工厂方法的返回值:
shapeTree.add(makeChild(RECTANGLE));
3. 修正构造函数的引用限定
原构造函数的左值引用参数搭配std::move语义不清晰,改成右值引用更合理:
ShapeTree(const int index, std::vector<std::unique_ptr<Shape>>&& shapes) : index_{index}, shapes_{std::move(shapes)} {}
如果需要支持左值容器传入,可额外重载版本(注意unique_ptr不可拷贝,需按需处理所有权)。
完整修正后的ShapeTree示例
struct ShapeTree { int index_; std::vector<std::unique_ptr<Shape>> shapes_; // 兼容裸指针的add版本 void add(Shape* shape) { shapes_.emplace_back(shape); } // 推荐的add版本,明确所有权转移 void add(std::unique_ptr<Shape> shape) { shapes_.push_back(std::move(shape)); } void removeLast(){ if (!shapes_.empty()) { // 避免空容器操作导致未定义行为 shapes_.pop_back(); } } ShapeTree(const int index) : index_{index}{} // 转移容器所有权的构造函数 ShapeTree(const int index, std::vector<std::unique_ptr<Shape>>&& shapes) : index_{index}, shapes_{std::move(shapes)} {} };
内容的提问来源于stack exchange,提问作者Filippo Fracascia
相关产品推荐
相关产品推荐

