c++如何拷贝构造和赋值 c++深拷贝与浅拷贝【核心】

拷贝构造与赋值本质不同:前者初始化新对象,后者为已存在对象赋值;深拷贝需独立复制堆内存以避免资源共享,浅拷贝仅复制指针值;现代C++应优先用RAII智能指针和容器替代裸指针。

拷贝构造和赋值在 C++ 中本质都是对象复制,但触发时机、语义责任和实现细节完全不同;深拷贝与浅拷贝的区别,核心在于是否对指针所指向的堆内存做独立副本 —— 这直接决定多个对象是否共享同一块动态资源,是资源管理正确性的分水岭。

拷贝构造:用一个已有对象初始化新对象

当定义新对象并用同类型对象初始化时调用,例如:T a; T b = a;func(a)(传值)、return a;(返回局部对象)。编译器默认生成的拷贝构造函数执行的是逐成员位拷贝(shallow copy)。

若类中含裸指针(如 int* p;),默认拷贝会让两个对象的 p 指向同一块堆内存 —— 后续析构时重复 delete 就会崩溃。

必须手动定义拷贝构造函数来实现深拷贝:

  • 为每个指针成员分配新内存(new
  • 将原对象指针所指内容逐字节或按逻辑复制过去
  • 确保不遗漏任何需要深拷贝的资源(如文件句柄、socket 等需 duplicate 的系统资源)

赋值运算符:已存在对象接收另一个对象的值

使用 = 对已构造对象赋值时调用,例如:a = b;。默认赋值运算符同样只做浅拷贝,且不处理自我赋值(a = a;)和原有资源释放问题。

手写赋值运算符需满足“三法则”(Rule of Three):只要写了拷贝构造、析构或赋值之一,通常三个都得自己写。典型实现模式:

  • 先检查自我赋值(if (this == &rhs) return *this;
  • 释放当前对象持有的资源(如 delete[] p;
  • 分配新内存并拷贝数据(深拷贝逻辑同拷贝构造)
  • 返回 *this 支持链式赋值(a = b = c;

深拷贝 vs 浅拷贝:关键在资源所有权

浅拷贝只是复制指针值,多个对象共用同一份堆内存 —— 简单但危险;深拷贝为每个对象创建独立副本 —— 安全但开销大、需手动管理。

判断是否需要深拷贝,看类是否拥有「独占性资源」:

  • 需要深拷贝:原始指针、自管理缓冲区(如 char* 数组)、非 RAII 封装的句柄
  • 无需深拷贝:智能指针(std::shared_ptr 自带引用计数)、std::stringstd::vector 等标准容器(它们内部已实现深拷贝或写时复制)
  • 可选浅拷贝:只读数据、缓存、大块只读内存(配合 const 和明确文档说明)

现代 C++ 的推荐做法:避免裸指针 + 遵循 RAII

真正解决深浅拷贝难题的根本方法,不是写更复杂的拷贝函数,而是从设计上消除裸指针:

  • std::vector 替代 int* + size_t size
  • std::string 替代 char* + 手动 strcpy
  • std::unique_ptr 表达独占所有权(移动语义自动禁用拷贝)
  • std::shared_ptr 表达共享所有权(拷贝即增加引用计数)

这样,默认生成的拷贝/赋值函数就天然安全,既无内存泄漏也无重复释放 —— 深浅拷贝之争自然消解。