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

C++原型设计模式实现疑问:深拷贝与clone函数及类型转换建议

原型模式实现分析与优化建议

现有实现的核心问题

  1. 野指针风险:otherNode默认构造函数未初始化成员ttNode* ttt,默认创建的对象中ttt是野指针,调用clone()时会触发崩溃等未定义行为。
  2. dynamic_cast冗余:node.ttt是ttNode*类型,而ttNode是抽象类,其clone()返回的必然是它的子类实例,这里用dynamic_cast完全没必要,换成static_cast更高效,甚至可以通过调整clone的返回值彻底避免类型转换。
  3. ttNode的clone设计缺失:作为中间抽象类,ttNode可以利用C++协变返回类型特性,将clone返回值改为ttNode*,让子类返回更具体的指针类型,既符合原型语义又简化后续代码。

优化后的完整实现

1. 调整基类与抽象中间类的clone函数

class base {
public:
    explicit base() {}
    virtual ~base() = default;
    virtual base* clone() const = 0;
};

class ttNode : public base {
public:
    explicit ttNode() : base() {}
    ~ttNode() = default;
    // 协变返回类型:将clone改为返回ttNode*,子类可返回更具体的类型
    virtual ttNode* clone() const = 0;
};

class SonNode : public ttNode {
public:
    SonNode() : ttNode() {}
    ~SonNode() = default;

    // 协变返回SonNode*,直接返回自身类型的拷贝
    SonNode* clone() const override {
        return new SonNode(*this);
    }
private:
    int c_{0};
    int a_{0};
};

class FatherNode : public ttNode {
public:
    FatherNode() : ttNode() {}
    ~FatherNode() = default;

    FatherNode(const FatherNode& node) : ttNode(node) {
        // 利用SonNode的clone返回SonNode*,直接完成深拷贝
        for (const auto &son : node.sons_) {
            sons_.emplace_back(son->clone());
        }
    }

    // 协变返回FatherNode*
    FatherNode* clone() const override {
        return new FatherNode(*this);
    }

private:
    std::vector<SonNode*> sons_;
};

2. 修复otherNode的深拷贝与资源管理

class otherNode : public base {
public:
    // 默认构造初始化ttt为nullptr,避免野指针
    otherNode() : base(), ttt(nullptr) {}
    ~otherNode() {
        delete ttt; // 手动释放资源,避免内存泄漏
    }

    // 深拷贝构造:处理ttt的非空情况
    otherNode(const otherNode& node) : base(node), ttt(nullptr) {
        if (node.ttt != nullptr) {
            // 现在ttNode::clone返回ttNode*,直接赋值无需类型转换
            ttt = node.ttt->clone();
        }
    }

    // 重载赋值运算符(遵循Rule of Three)
    otherNode& operator=(const otherNode& node) {
        if (this != &node) {
            delete ttt;
            ttt = nullptr;
            if (node.ttt != nullptr) {
                ttt = node.ttt->clone();
            }
        }
        return *this;
    }

    // 可选:实现移动语义优化
    otherNode(otherNode&&) noexcept = default;
    otherNode& operator=(otherNode&&) noexcept = default;

    base* clone() const override {
        return new otherNode(*this);
    }

    // 可选:添加setter用于测试时初始化ttt
    void setTtt(ttNode* node) {
        delete ttt;
        ttt = node;
    }

private:
    ttNode* ttt;
};

3. 完善测试代码

TEST_F(tt, base1) {
    otherNode* o = new otherNode();
    o->setTtt(new FatherNode()); // 初始化ttt为有效对象

    base *f = new FatherNode();
    base *o1 = o->clone();
    base *f1 = f->clone();

    delete o1;
    delete f1;
    delete o;
}

关键知识点梳理

  • 协变返回类型:派生类重写虚函数时,可返回基类虚函数返回类型的派生类指针/引用,既符合原型模式“克隆自身类型”的语义,又能避免不必要的类型转换。
  • Rule of Three/Five:类拥有指针成员时,必须手动实现拷贝构造、赋值运算符(可选移动语义),确保资源正确释放,避免内存泄漏和浅拷贝问题。
  • 抽象类的原型实现:抽象类无法直接实例化,但通过纯虚clone函数让子类实现具体拷贝逻辑,完美契合原型模式“通过克隆现有实例创建新对象”的设计思想。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 02:17:52