浅拷贝是编译器默认的按成员复制,仅复制指针地址;深拷贝需手动实现,为指针分配新内存并复制内容,否则易致double free或悬空指针;含动态资源时必须遵循Rule of Three/Five,优先使用std::string或智能指针。

浅拷贝是编译器默认生成的拷贝行为,只复制成员变量的值;深拷贝必须手动实现,要为指针成员分配新内存并复制所指内容。不加区分地使用默认拷贝,只要类里有 char*、int* 或其他裸指针,几乎必然导致 double free 或悬空指针崩溃。
编译器默认的拷贝构造函数就是浅拷贝
只要你没写 ShallowString(const ShallowString&),编译器就给你生成一个“按成员复制”的版本:基本类型照搬,指针变量只复制地址值,不碰它指向的堆内存。
- 现象:两个对象的
m_data指针值相同,print()显示同一地址 - 后果:析构时两次
delete[] m_data,触发未定义行为(程序崩溃或静默损坏) - 适用场景:仅当类中不含任何动态资源(比如纯
int、std::string、std::vector等已管理好内存的类型)时才安全
深拷贝必须显式定义拷贝构造函数和赋值运算符
只重写拷贝构造函数不够——operator= 也得同步实现,否则 a = b 还是浅拷贝。两者逻辑高度一致,核心就一条:对每个指针成员,new 一块新内存,再用 strcpy、memcpy 或循环把原数据搬过去。
- 示例关键行:
data = new char[other.length + 1]; strcpy(data, other.data); - 别漏掉异常安全:如果
new失败抛出异常,已有成员不能处于半初始化状态(建议先分配再赋值,或用 RAII 封装) - 现代 C++ 更推荐用
std::string替代char*,用std::unique_ptr替代裸指针——它们自带深拷贝语义,省去手写逻辑
Rule of Three / Rule of Five 不是建议,是强制契约
一旦你写了自定义的析构函数(比如释放 new 出来的内存),就必须同时提供拷贝构造函数和拷贝赋值运算符——否则编译器生成的默认版本会破坏你的资源管理逻辑。
立即学习“C++免费学习笔记(深入)”;
- 如果你还支持移动语义,就得补上移动构造函数和移动赋值运算符(Rule of Five)
- 用
= default显式声明某操作为默认行为是可以的,但前提是确认它真的安全(例如所有成员都支持深拷贝) - 最省事的规避方式:彻底避免裸指针,改用标准容器或智能指针
真正容易被忽略的不是“怎么写深拷贝”,而是“什么时候需要写”。只要类里出现 new、malloc、fopen、pthread_create 这类资源获取操作,就该立刻检查拷贝和赋值是否被正确覆盖——哪怕当前测试没崩,只是还没触发双重释放的时机。


















