深拷贝必须手动实现拷贝构造函数,因默认为浅拷贝;含裸指针时若不写,会导致双对象共享内存、析构后访问崩溃;核心是new分配+复制内容+更新指针,并同步重载operator=与析构函数。

深拷贝必须自己写拷贝构造函数
默认的拷贝构造函数只做浅拷贝,只要类里有指针成员(比如 int*、char*、std::string 以外的自定义缓冲区),不手动写就会出问题——两个对象共享同一块堆内存,一个析构后另一访问就 segmentation fault 或读到垃圾值。
关键不是“要不要写”,而是“不写一定错”。哪怕你用 std::vector 或 std::string,它们内部已管好深拷贝;但一旦出现裸指针、malloc / new 出来的资源,就必须接管。
- 拷贝构造函数签名必须是:
A(const A& other),不能漏const & - 不能在函数体里调用
operator=,那是赋值,不是构造 - 初始化列表里别直接写
ptr(other.ptr)——那还是浅拷贝
拷贝构造函数里怎么分配新内存
核心动作就三步:申请新空间 → 复制内容 → 更新指针。不是“把 other 的指针赋过来”,而是“用 new 开一块新地,再把数据一个个搬过去”。
常见错误是忘了 new,或者用了 memcpy 但没算对字节数;更隐蔽的是复制了指针却没复制它指向的对象(比如指针指向另一个自定义类,而那个类没写自己的拷贝构造)。
立即学习“C++免费学习笔记(深入)”;
- 原始指针 +
new:用new int[other.size]分配,再std::copy或循环赋值 - 避免
malloc+memcpy:C++ 里优先用new和构造语义,否则对象不调用构造函数 - 如果成员是另一个需要深拷贝的类,确保它的拷贝构造函数可用(非
delete、非private)
class Buf {
public:
Buf(const Buf& other) : size(other.size), data(new int[other.size]) {
std::copy(other.data, other.data + other.size, data);
}
private:
int* data;
size_t size;
};
为什么 operator= 也要同步改
拷贝构造函数只管“初始化时”的深拷贝;但对象已经存在后,比如 a = b;,走的是赋值运算符。如果不重载 operator=,默认行为仍是浅拷贝,照样崩。
而且 operator= 还得防自赋值(a = a;),否则先 delete data 再复制,就成野指针了。
- 必须检查自赋值:
if (this == &other) return *this; - 先释放旧资源,再分配新资源,顺序不能反
- 强烈建议用 copy-and-swap 惯用法,既安全又自动处理异常
- 如果写了拷贝构造和
operator=,通常也得写析构函数——这就是“三法则”(C++11 后叫“五法则”,加移动)
现代 C++ 里能绕过手写吗
能,但前提是彻底不用裸指针。用 std::vector、std::unique_ptr、std::shared_ptr 等 RAII 类型,编译器生成的默认拷贝构造函数就能正确工作。
比如把 int* data 换成 std::vector<int> data</int>,所有拷贝、赋值、析构都自动深拷贝且异常安全。这不是“偷懒”,而是把资源管理交给标准库。
-
std::unique_ptr默认禁止拷贝,只允许移动;要拷贝就得显式写逻辑 -
std::shared_ptr拷贝是“浅拷贝指针 + 深拷贝引用计数”,行为安全但语义不同 - 只要头文件里出现
new、malloc、free、delete,基本就逃不开手写三/五法则
真正容易被忽略的,是那些看似简单、实则隐含裸内存操作的场景:比如封装了一个 C 风格 API 的 wrapper,内部用 char* 接收 strdup 结果——这时深拷贝的责任,一点没少。









