C++原型设计模式实现疑问:深拷贝与clone函数及类型转换建议
原型模式实现分析与优化建议
现有实现的核心问题
- 野指针风险:
otherNode默认构造函数未初始化成员ttNode* ttt,默认创建的对象中ttt是野指针,调用clone()时会触发崩溃等未定义行为。 dynamic_cast冗余:node.ttt是ttNode*类型,而ttNode是抽象类,其clone()返回的必然是它的子类实例,这里用dynamic_cast完全没必要,换成static_cast更高效,甚至可以通过调整clone的返回值彻底避免类型转换。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
相关产品推荐
相关产品推荐

