clone()必须是虚函数且返回基类指针,否则多态克隆失效;正确做法是基类声明virtual base clone() const = 0,派生类用new derived(this)实现并返回base*。

clone() 函数必须是虚函数,且返回基类指针
否则派生类无法覆盖行为,多态克隆会失效。最常见错误是写成非虚的 clone() 或返回具体类型(如 Derived*),导致调用时静态绑定到基类实现,返回错误对象或编译失败。
正确做法是:在基类中声明纯虚函数 virtual Base* clone() const = 0;,所有派生类重写它,内部用 new Derived(*this) 构造并返回 Base*。
注意三点:
-
clone()一般设为const成员函数,语义上不修改原对象 - 返回类型必须是基类指针(或智能指针),不能是派生类指针,否则破坏接口统一性
- 若用
std::unique_ptr<base>,则声明为virtual std::unique_ptr<base> clone() const = 0;,C++14 起支持协变返回类型(但 raw pointer 更通用)
虚构造函数不存在,但 clone() 是它的替代方案
C++ 不允许虚构造函数,因为构造发生在对象内存布局确定之前,无法动态调度。所谓“虚构造”只是对 clone() 行为的通俗说法——它在运行时根据实际类型创建同类型新对象。
关键区别在于:
- 构造函数名固定、不可继承、不可虚化;
clone()是普通虚函数,可被继承和重写 - 构造函数需显式调用(如
Derived x;),而clone()可通过基类指针间接触发:Base* p = new Derived; Base* q = p->clone(); - 若忘记在派生类中实现
clone(),链接期报错(纯虚函数未定义)或运行时调用纯虚函数崩溃(取决于编译器)
深拷贝陷阱:成员含裸指针或资源时必须手动处理
默认拷贝构造函数只做浅拷贝,若类中有 int* data 或 FILE* 等资源句柄,clone() 返回的对象会与原对象共享同一份资源,后续析构极易 double-free 或悬空指针。
解决方式只有显式深拷贝:
- 在派生类
clone()实现里,不用new Derived(*this)(它调用默认拷贝构造),改用new Derived(this->x, this->y, new int[...])显式分配 - 更安全的做法是:把资源管理交给 RAII 类型(如
std::vector、std::unique_ptr),此时默认拷贝构造即满足深拷贝语义 - 若必须用裸指针,记得在类中同时正确定义拷贝构造、赋值运算符和析构函数(三法则/五法则)
std::shared_ptr 配合 clone() 更易管理生命周期
裸指针版 clone() 要求调用方严格负责 delete,容易泄漏。用智能指针可自动释放,且支持多处共享同一克隆体。
示例签名与实现:
virtual std::shared_ptr<base> clone() const = 0;
<p>// 派生类中:
std::shared_ptr<base> Derived::clone() const {
return std::make_shared<derived>(*this);
}</derived></p>
注意点:
-
std::make_shared效率高于std::shared_ptr<derived>(new Derived(...))</derived> - 若类有 protected 构造函数或删除了拷贝构造,
make_shared可能编译失败,此时需改用shared_ptr构造函数 +new - 不要在
clone()中返回shared_ptr到栈对象(如return std::shared_ptr<base>(&x);),这是野指针
真正麻烦的从来不是写几行 clone(),而是确保每个派生类都记得重写它,并且深拷贝逻辑跟析构逻辑严格同步——漏掉一个,就是运行时未定义行为。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











