c++深拷贝与浅拷贝区别_c++拷贝构造函数详解

14次阅读

浅拷贝仅复制指针导致共享内存,深拷贝需手动分配并复制数据;拷贝构造函数对象初始化、值传递参数、返回局部对象时调用;必须同时重载拷贝构造函数和operator=以避免行为不一致,并注意自赋值、异常安全及元信息同步。

c++深拷贝与浅拷贝区别_c++拷贝构造函数详解

浅拷贝只是复制指针,深拷贝才真正复制数据

默认的拷贝构造函数和赋值运算符执行的是浅拷贝——它把对象里的每个字节原样复制过去。如果类里有 char*int* 或其他裸指针成员,浅拷贝后两个对象会指向同一块内存。一旦其中一个析构时调用 delete,另一方再访问就是野指针;若两次析构,还会触发 double free 错误。

深拷贝必须在拷贝构造函数和 operator= 中手动分配新内存,并把原始数据逐字节或按逻辑复制过去。这是资源管理的基本守则,不写就会出问题。

拷贝构造函数什么时候被调用

它不是只在 A b = a; 这种写法里触发。以下场景都会调用:

  • 用一个已存在对象初始化新对象:A b(a);A b = a;
  • 函数传参时以值传递方式接收对象:void func(A x) { ... } 调用时 func(a)
  • 函数返回局部对象(且未被编译器优化掉):A create() { A x; return x; }

注意:现代编译器普遍启用 RVO/NRVO 优化,可能跳过拷贝构造。但逻辑上仍需正确实现,否则关掉优化或换编译器就崩。

立即学习C++免费学习笔记(深入)”;

必须同时重载拷贝构造函数和 operator=

只写拷贝构造函数而忽略赋值运算符,或反过来,会导致行为不一致。比如:

A a; A b; b = a; // 调用 operator=,若没重载,就是浅拷贝 A c = a; // 调用拷贝构造函数,若重载了,是深拷贝

这种不对称极易引发隐性 bug。标准做法是遵循“三法则”(c++11 后为“五法则”):只要写了析构函数、拷贝构造函数、拷贝赋值运算符中的任一个,另外几个通常也得自己写。

常见疏漏点:

  • operator= 忘记处理自赋值:a = a; 会导致先 delete 再访问已释放内存
  • 拷贝构造函数里调用 new 失败未检查,抛出异常后对象处于半构造状态
  • 没有把源对象的非指针成员(如 size_t len)一并复制,导致新对象元信息错乱

用 std::String 和 std::vector 可以绕过手写深拷贝

它们内部已经实现了正确的深拷贝语义。如果你的类里原来用 char*字符串,换成 std::string;用 int* + size_t 管理数组,换成 std::vector,那么默认生成的拷贝构造函数就能安全工作。

但这不等于可以忽视原理——当涉及文件句柄、socket、shared memory 等系统资源时,依然要手动管理。而且,有些老项目或嵌入式环境禁用 STL,这时深拷贝逻辑逃不掉。

真正容易被忽略的是:即使用了 std::vector,若类中还有裸指针成员(比如缓存用的 Float* m_cache),那依然得自己写深拷贝。别以为加了 STL 就万事大吉。

text=ZqhQzanResources