深拷贝必须显式定义拷贝构造函数,因默认为浅拷贝;含指针成员时若不自定义,会导致双删崩溃;参数须为const引用;还需实现operator=,推荐拷贝-交换;移动构造函数不能替代拷贝构造函数。

深拷贝必须自己写拷贝构造函数
默认的拷贝构造函数只做浅拷贝,只要类里有指针成员(比如 int*、char* 或 std::vector 以外的手动管理内存),就一定会出问题——两个对象指向同一块堆内存,析构时重复 delete,程序崩溃。
你不能依赖编译器生成的版本,哪怕看起来“能跑”。只要涉及动态分配,就必须显式定义:
class Buffer {
char* data_;
size_t size_;
public:
Buffer(const char* s) : size_(strlen(s)) {
data_ = new char[size_ + 1];
strcpy(data_, s);
}
<pre class='brush:php;toolbar:false;'>// 必须写!否则是浅拷贝
Buffer(const Buffer& other) : size_(other.size_) {
data_ = new char[size_ + 1];
strcpy(data_, other.data_);
}
~Buffer() { delete[] data_; }};
拷贝构造函数参数必须是 const 引用
写成 Buffer(Buffer other) 或 Buffer(Buffer& other) 都错:
前者会触发无限递归(传值调用又需要拷贝);后者无法绑定临时对象或 const 对象,限制太死。
立即学习“C++免费学习笔记(深入)”;
-
Buffer(const Buffer& other)是唯一安全、通用的签名 - 即使你不改
other,也必须加const,否则func(Buffer b) { Buffer c = b; }这种常见调用会编译失败 - 别在函数体内对
other做非 const 操作(比如调用非常量成员函数),否则说明设计有问题
赋值运算符 ≠ 拷贝构造函数,漏写会导致隐式转换出错
只写了拷贝构造函数,没写 operator=?那 b = a; 仍会调用默认赋值——又是浅拷贝。更危险的是:如果类有 explicit 构造函数,但没禁用拷贝赋值,某些隐式转换可能绕过检查。
- 拷贝构造函数处理“初始化”:
Buffer b = a;、func(a)、return a; -
operator=处理“已有对象被赋值”:b = a;,此时b已存在,得先清理旧资源再复制 - 现代写法推荐“拷贝-交换”惯用法,避免自赋值检查和异常安全问题:
Buffer& operator=(Buffer other) { swap(*this, other); return *this; }
移动语义出现后,拷贝构造函数依然不能省
C++11 后有了移动构造函数,但编译器不会因为写了 Buffer(Buffer&&) 就自动禁用或优化掉拷贝逻辑。只要代码里出现拷贝场景(比如传 const 引用、容器存值、std::vector::push_back 值插入),还是走拷贝构造函数。
- 移动构造函数解决的是“临时对象”或“明确 std::move 的对象”,不是替代拷贝
- 如果你删了拷贝构造函数(比如加了
= delete),而某处又意外触发了拷贝(比如忘记const&参数),编译直接报错:use of deleted function ‘Buffer::Buffer(const Buffer&)’ - 真正想禁拷贝,得同时删拷贝构造 + 拷贝赋值,并接受它只能被移动或引用传递
最常被忽略的一点:深拷贝的“深”不是指多层指针,而是“资源所有权独立”。哪怕只有一级 new,也得自己管;哪怕有 std::unique_ptr,它已经帮你做了深拷贝语义——这时候你反而不该手写拷贝构造函数。









