默认拷贝构造函数仅执行浅拷贝,导致指针成员共享堆内存,引发use-after-free或double free;深拷贝必须手动为每个动态资源分配新内存并复制内容,且拷贝构造函数与operator=须成对实现。

为什么直接拷贝构造函数无法实现深层克隆
因为默认的拷贝构造函数和 operator= 只执行浅拷贝:若对象内含指针(比如 int*、std::string* 或自定义资源句柄),复制后两个对象会共享同一块堆内存。一旦其中一个析构释放资源,另一个再访问就会触发 use-after-free 或重复释放(double free)错误。
深层克隆必须为每个动态分配的子对象单独调用 new(或等价操作),并递归克隆其内容。这无法靠编译器自动生成,必须手动控制。
- 常见错误现象:程序在第二次析构时崩溃,或输出乱码/空值,尤其在容器中存放多态对象指针时更隐蔽
- 典型场景:基类指针指向派生类对象,需在运行时决定克隆哪一类;或对象图存在循环引用(需额外处理)
- 不推荐用
memcpy或std::bit_cast:它们不调用构造函数,对含虚表、非 POD 类型必然失败
如何用虚函数实现可扩展的 clone() 接口
核心是定义纯虚函数 virtual Base* clone() const = 0,强制所有派生类提供自己的克隆逻辑。这样通过基类指针也能安全生成同类型副本,避免类型擦除。
注意返回类型必须是基类指针(支持协变返回类型),且函数声明为 const —— 克隆不应改变原对象状态。
- 派生类实现必须显式调用自身拷贝构造函数:
return new Derived(*this); - 若派生类含裸指针成员,
Derived的拷贝构造函数里必须深拷贝那些指针,否则clone()仍是浅的 - 返回值是堆分配对象,调用方必须负责
delete;也可返回std::unique_ptr<base>避免内存泄漏(但需调整签名)
class Shape {
public:
virtual ~Shape() = default;
virtual Shape* clone() const = 0; // 协变返回允许派生类返回 Derived*
};
<p>class Circle : public Shape {
double<em> radius<em>;
public:
Circle(double r) : radius</em>(new double(r)) {}
Circle(const Circle& other) : radius_(new double(</em>other.radius_)) {} // 深拷贝关键
Circle<em> clone() const override { return new Circle(</em>this); }
};</p>
什么时候该用 std::clone_ptr 或智能指针替代手写 clone()
std::clone_ptr 并不存在于标准库(C++20 仍无),这是常见误解。有人误以为它类似 std::shared_ptr,但实际没有标准支持。真正可用的是组合策略:
- 用
std::unique_ptr<base>存储对象,配合工厂函数或虚clone()实现多态克隆 - 若对象图简单且无循环,可借助序列化(如
boost::serialization)反序列化出新实例,本质也是深拷贝 - 现代替代思路:避免裸指针,改用值语义(
std::vector<:string></:string>而非std::string*),让编译器自动生成正确拷贝逻辑
手写 clone() 最容易被忽略的点是异常安全:若拷贝构造过程中抛异常(如 new 失败),已分配的部分资源可能泄漏。建议在拷贝构造函数中使用 RAII 封装资源(如用 std::unique_ptr 管理指针成员),而非裸 new + 手动 delete。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











