C++深拷贝和浅拷贝区别 C++拷贝构造函数编写注意事项【必看】

9次阅读

浅拷贝是位拷贝,仅复制指针值而非内容,易致double free;深拷贝需自定义拷贝构造函数(A(const A&))和赋值运算符,用拷贝-交换法确保异常安全与自赋值防护,并遵循五法则实现资源管理。

C++深拷贝和浅拷贝区别 C++拷贝构造函数编写注意事项【必看】

浅拷贝就是位拷贝,深拷贝要自己写

默认的拷贝构造函数和赋值运算符只做浅拷贝:它逐字节复制对象内存,对指针成员来说,只是复制了指针值(地址),而不是指针指向的内容。如果原对象析构时 delete 了那块内存,副本再访问或析构就会触发 double freeuse-after-free——这是最典型的崩溃根源。

深拷贝必须手动分配新内存、逐个复制数据。核心判断标准就一条:只要类里有指针成员(尤其是指向堆内存的)或资源句柄(如文件描述符、socket),就必须自定义拷贝构造函数和 operator=

拷贝构造函数里别漏掉 const 引用参数

合法签名只能是 A(const A& other)。写成 A(A& other) 会导致无法接受临时对象(比如函数返回值),写成 A(A other) 会无限递归调用自身——因为传参会再次触发拷贝构造。

常见错误示例:

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

A(A other) {  // ❌ 错误:传值导致无限递归     data = other.data; }

正确写法必须带 const &

A(const A& other) : data(nullptr) {     if (other.data) {         data = new int[other.size];         std::copy(other.data, other.data + other.size, data);         size = other.size;     } }

赋值运算符要处理自赋值和异常安全

浅拷贝的默认 operator= 同样只复制指针,而且不检查自赋值(a = a)。直接 delete data; data = other.data; 会导致 data 被删后又去读已释放内存。

推荐用“拷贝-交换”惯用法(copy-and-swap),天然规避自赋值和部分异常风险:

A& operator=(A other) {  // 注意:这里传值,触发拷贝构造     swap(*this, other);     return *this; } void swap(A& a, A& b) noexcept {     std::swap(a.data, b.data);     std::swap(a.size, b.size); }

关键点:

  • 参数用值传递,让编译器自动调用你的拷贝构造函数
  • swap 必须是 noexcept,否则异常时可能破坏强异常保证
  • 别在 operator= 里手动 deletenew,容易出错

移动语义出现后,深/浅拷贝逻辑依然不能省

c++11 后有了移动构造函数和移动赋值,但它们不会替代拷贝逻辑——只是提供更高效的资源转移方式。如果你没声明移动操作,编译器可能生成默认版本(对指针成员仍是浅移动),也可能因存在用户定义的拷贝操作而抑制移动函数生成。

真正安全的做法是:显式定义全部五个特殊成员函数(俗称“五法则”):

  • A(const A&)(深拷贝)
  • A& operator=(const A&)(深赋值)
  • A(A&&)(移动构造,通常把指针置为 nullptr
  • A& operator=(A&&)(移动赋值)
  • ~A()(确保 delete 堆内存)

最容易被忽略的是:即使你写了移动操作,拷贝构造函数仍需保持正确——因为有些场景(如容器扩容时的元素复制)仍会调用它。

text=ZqhQzanResources