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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 12:05:20