C++中的深拷贝和浅拷贝有什么区别?(堆内存资源所有权复制)

13次阅读

浅拷贝仅复制指针值导致双释放,深拷贝需在拷贝构造函数赋值运算符中手动分配内存并复制数据;现代c++推荐用std::vector等RaiI容器替代裸指针,必要时须完整实现Rule of Five。

C++中的深拷贝和浅拷贝有什么区别?(堆内存资源所有权复制)

浅拷贝只是复制指针值,不复制内存

当你没写自定义拷贝构造函数时,C++ 默认执行的是浅拷贝:它把 obj1 中的指针成员(比如 int* data)的值(即地址)直接赋给 obj2.data。两个对象的指针指向同一块堆内存。

这会导致后续析构时两次调用 delete 同一地址,触发未定义行为(常见表现是程序崩溃或 double free or corruption 错误)。

典型场景:类中包含 new 出来的资源,比如动态数组、文件句柄、网络连接等。

深拷贝必须在拷贝构造函数和赋值运算符里手动分配新内存

要真正实现资源隔离,必须在拷贝构造函数中用 new 申请新内存,并用 std::copy循环把原数据复制过去;同时重载 operator= 防止自我赋值和资源泄漏。

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

关键点:

  • 拷贝构造函数参数必须是 const T&,避免递归调用
  • 赋值运算符要先检查 this == &rhs
  • 旧资源必须先释放再 new,否则内存泄漏
  • 记得同步更新所有相关成员(比如长度字段 size
class Buffer { public:     Buffer(size_t s) : size(s), data(new int[s]) {}     Buffer(const Buffer& other) : size(other.size), data(new int[other.size]) {         std::copy(other.data, other.data + size, data);     }     Buffer& operator=(const Buffer& other) {         if (this == &other) return *this;         delete[] data;         size = other.size;         data = new int[other.size];         std::copy(other.data, other.data + size, data);         return *this;     }     ~Buffer() { delete[] data; } private:     size_t size;     int* data; };

现代 C++ 更推荐用 RAII 容器替代裸指针

手写深拷贝容易出错,且不符合“零成本抽象”原则。绝大多数情况下,应该用 std::vectorstd::Stringstd::unique_ptr 等——它们内部已正确实现深拷贝语义(或移动语义)。

例如把 int* data 换成 std::vector data,就不再需要自己写拷贝构造函数,编译器生成的默认版本就能安全工作。

例外情况:封装底层系统资源(如 GPU buffer、自定义内存池)时,仍需手动管理,此时必须严格遵循 Rule of Three / Rule of Five。

容易被忽略的:移动语义会绕过深拷贝逻辑

如果只实现了深拷贝但没实现移动构造/赋值,C++11 及以后可能因隐式移动(比如返回局部对象)而调用默认移动函数——它其实是浅搬指针,接着析构源对象时就会释放资源,导致目标对象悬空。

所以一旦你写了自定义析构函数或拷贝控制成员,就必须显式声明移动操作(或 = default),否则可能引发静默错误。

最稳妥的做法:要么全用标准容器,要么完整实现 Rule of Five(~T(), T(const T&), T(T&&), operator=(const T&), operator=(T&&))。

text=ZqhQzanResources