std::string自动深拷贝,但含裸指针的自定义类默认浅拷贝,易致双重释放、悬空指针;须重写拷贝构造函数、operator=和析构函数实现深拷贝。

std::string 会自动深拷贝,但自定义类里含 int<em></em>、char 这类裸指针时,默认拷贝就是浅拷贝——不处理指针指向的内存,只复制地址。这会导致双重释放、悬空指针或数据被意外覆盖。
默认拷贝构造函数和 operator= 都是浅拷贝
只要类里没写拷贝构造函数或赋值运算符重载,编译器生成的版本就逐字节复制所有成员。对 int、double 没问题;但遇到 int* _data,它只复制指针值,不 new 一块新内存。
常见错误现象:
- 程序运行到第二次
delete[] _data时崩溃(double free or corruption) - 修改对象 A 的内容,对象 B 的输出也变了
- 其中一个对象析构后,另一个再访问
_data就是野指针
使用场景:只有当明确需要共享底层数据(比如观察者模式中的只读缓存),才考虑保留浅拷贝语义;否则一律按需实现深拷贝。
实现深拷贝必须重写拷贝构造函数和 operator=
二者逻辑高度相似,都要做三件事:分配新内存 → 复制原数据 → 更新指针。漏掉任一环节都会出问题。
关键点:
- 拷贝构造函数签名必须是
ClassName(const ClassName& other),且参数要加const & -
operator=要检查自赋值:if (this == &other) return *this; - 赋值前必须先释放旧资源,再分配新内存,否则内存泄漏
- 别忘了在析构函数里
delete[] _data—— 浅拷贝类若没写析构,可能连这点都漏
示例片段(简化):
class Buffer {
char* _ptr;
size_t _size;
public:
Buffer(const Buffer& b) : _size(b._size) {
_ptr = new char[_size];
std::memcpy(_ptr, b._ptr, _size); // 深拷贝数据
}
Buffer& operator=(const Buffer& b) {
if (this == &b) return *this;
delete[] _ptr; // 先清理
_size = b._size;
_ptr = new char[_size];
std::memcpy(_ptr, b._ptr, _size);
return *this;
}
~Buffer() { delete[] _ptr; }
};
浅拷贝不是 bug,是默认行为
C++ 不强制你深拷贝,是因为:
- 性能:深拷贝要 malloc + memcpy,开销大;有些场景真不需要
- 语义明确:裸指针本就表示“我管内存,你别乱动”,默认不干涉是尊重原始意图
- 兼容性:C 风格结构体(POD)就靠浅拷贝工作,改了反而破坏 ABI
真正容易被忽略的是:一旦类里出现裸指针或 FILE*、socket fd 等资源句柄,就必须立刻补全“规则之三”(析构函数、拷贝构造、赋值运算符),缺一不可。很多人只写了拷贝构造,忘了 operator=,结果在 a = b 时又掉进浅拷贝坑里。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











