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

C++17中含多态对象向量的树形结构深拷贝实现问题

C++17多态树形结构深拷贝实现问题

我受限于使用C++17,需要实现一种可深拷贝为两个独立副本的树形结构(不一定是二叉树)。目前定义了抽象基类Node,它有两个派生类:

  • Operation类:持有布尔操作枚举,以及存储子节点的std::vector<std::unique_ptr<Node>>,作为树的分支节点。
  • Condition类:持有求值用的比较数据,作为树的叶子节点。

另外还有Event类,用std::unique_ptr<Node>指向树的根节点,根节点可以是Operation或Condition类型。

我尝试通过多态clone()方法实现std::unique_ptr的深拷贝,但在Event和Operation的拷贝构造函数里遇到了编译错误,不清楚怎么维持多态性,也不确定这些拷贝构造函数是不是必须写的。

原代码(含编译错误)

#include <algorithm>
#include <memory>
#include <string>
#include <vector>

namespace tree
{
struct Node
{
    // 虚clone函数,返回指向当前对象拷贝的unique_ptr
    virtual std::unique_ptr<Node> clone() const = 0;

    std::string name;
};

enum class Boolean
{
    And,
    Or,
    Xor
};

struct Operation : public Node
{
    // 拷贝构造函数:尝试拷贝vector并克隆每个子节点
    Operation(const Operation &other) : op(other.op), nodes(other.nodes.size())
    {
        std::transform(other.nodes.cbegin(), other.nodes.cend(), nodes.begin(),
            [](const auto &old) {
                // 编译错误:Node是抽象类,无法实例化
                return op ? std::make_unique<Node>(old->clone()) : nullptr;
            });
    }
    
    virtual std::unique_ptr<Node> clone() const override
    {
        return std::make_unique<Operation>(*this);
    }
    
    Boolean op;
    std::vector<std::unique_ptr<Node>> nodes;
};

struct Condition : public Node
{
    virtual std::unique_ptr<Node> clone() const override
    {
        return std::make_unique<Condition>(*this);
    }
    
    int datum;
};

struct Event
{
    Event(std::string name) : name(name) {}
    // 编译错误:同样试图实例化抽象类Node
    Event(const Event &other) : name(other.name), root(std::make_unique<Node>(other.root->clone())) {}
    std::string name;
    std::unique_ptr<Node> root;
};
} // tree

问题分析与解决方案

错误根源

代码里的编译错误都是因为试图直接实例化抽象基类Node:

  • std::make_unique<Node>(...)是非法的,因为Node包含纯虚函数,属于抽象类,不能直接创建对象。
  • 而clone()方法本身已经返回了一个指向派生类对象的std::unique_ptr<Node>,完全不需要再用make_unique包装。

另外Operation拷贝构造函数里的lambda还存在逻辑错误:用op(布尔操作枚举)作为指针非空的判断条件,这完全是误用,应该判断的是old这个unique_ptr是否为空。

修正后的关键代码

  1. Operation拷贝构造函数修正
    要调用基类拷贝构造函数拷贝name成员,同时正确克隆每个子节点:
Operation(const Operation &other) : Node(other), op(other.op)
{
    std::transform(other.nodes.cbegin(), other.nodes.cend(), std::back_inserter(nodes),
        [](const auto &old) {
            return old ? old->clone() : nullptr;
        });
}

这里用std::back_inserter代替预先分配vector大小,更简洁安全。

  1. Event拷贝构造函数修正
    直接用clone()的返回值初始化root,同时增加空指针判断:
Event(const Event &other) : name(other.name), root(other.root ? other.root->clone() : nullptr) {}
  1. 补充基类虚析构函数
    为了避免多态场景下的内存泄漏,必须给Node添加虚析构函数:
virtual ~Node() = default;
  1. Condition拷贝构造函数补充
    虽然Condition没有复杂成员,但显式调用基类拷贝构造函数是良好的编码习惯:
Condition(const Condition &other) : Node(other), datum(other.datum) {}

拷贝构造函数的必要性

这些拷贝构造函数是必须实现的:

  • Operation和Event都包含std::unique_ptr,而unique_ptr是不可拷贝的,默认拷贝构造函数会被禁用。
  • 要实现深拷贝,必须手动调用每个子节点的clone()方法,确保树形结构的所有节点都被复制为独立对象。

完整修正代码

#include <algorithm>
#include <memory>
#include <string>
#include <vector>

namespace tree
{
struct Node
{
    virtual std::unique_ptr<Node> clone() const = 0;

    std::string name;
    
    // 虚析构函数,确保派生类对象能正确销毁
    virtual ~Node() = default;
};

enum class Boolean
{
    And,
    Or,
    Xor
};

struct Operation : public Node
{
    Operation(const Operation &other) : Node(other), op(other.op)
    {
        std::transform(other.nodes.cbegin(), other.nodes.cend(), std::back_inserter(nodes),
            [](const auto &old) {
                return old ? old->clone() : nullptr;
            });
    }
    
    virtual std::unique_ptr<Node> clone() const override
    {
        return std::make_unique<Operation>(*this);
    }
    
    Boolean op;
    std::vector<std::unique_ptr<Node>> nodes;
};

struct Condition : public Node
{
    Condition(const Condition &other) : Node(other), datum(other.datum) {}
    
    virtual std::unique_ptr<Node> clone() const override
    {
        return std::make_unique<Condition>(*this);
    }
    
    int datum;
};

struct Event
{
    Event(std::string name) : name(std::move(name)) {}
    Event(const Event &other) : name(other.name), root(other.root ? other.root->clone() : nullptr) {}
    
    std::string name;
    std::unique_ptr<Node> root;
};
} // tree

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 13:07:02