信息发布→ 登录 注册 退出

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

发布时间:2026-01-08

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

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

默认的拷贝构造函数和赋值运算符执行的是浅拷贝——它把对象里的每个字节原样复制过去。如果类里有 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 优化,可能跳过拷贝构造。但逻辑上仍需正确实现,否则关掉优化或换编译器就崩。

必须同时重载拷贝构造函数和 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 就万事大吉。

标签:#   # 万事大吉  # 还会  # 什么时候  # 句柄  # 就能  # 就会  # 几个  # 这是  # 的是  # bug  # 对象  # delete  # len  # 值传递  # operator  # 字节  # 指针  # void  # double  # int  # char  # 字符串  # 析构函数  # 构造函数  # 赋值运算符  # 运算符  # Float  # String  # red  # 区别  # c++  
在线客服
服务热线

服务热线

4008888355

微信咨询
二维码
返回顶部
×二维码

截屏,微信识别二维码

打开微信

微信号已复制,请打开微信添加咨询详情!