浅拷贝仅复制指针值导致共享堆内存,引发double free或数据误改;深拷贝需手动实现拷贝构造函数与赋值运算符并处理自赋值;现代c++推荐用std::string等raii容器替代裸指针。

浅拷贝就是默认的拷贝,但只复制指针不复制数据
当你没写拷贝构造函数时,C++ 会自动生成一个——它逐字节复制对象内存,对 int、double 这类内置类型没问题,但遇到 char* 或 std::vector* 这种指向堆内存的成员,就只复制了指针值,两个对象指向同一块内存。
常见错误现象:double free or corruption 或程序在析构第二次时崩溃;或者修改对象 A 的内容,对象 B 的数据也跟着变。
- 典型场景:类里有
char*成员手动管理字符串,或用new分配的数组 - 不是所有指针都危险:如果指针是只读的、生命周期由外部保证(比如传入的
const char*),那浅拷贝反而是安全且高效的 - 性能影响:浅拷贝快,深拷贝慢,尤其数据量大时要权衡
深拷贝必须自己写拷贝构造函数 + 赋值运算符重载
只写拷贝构造函数还不够。用户可能这样写:obj1 = obj2;,这触发的是赋值运算符,不是构造函数。两者都得实现,否则赋值操作仍走默认浅拷贝。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 拷贝构造函数签名必须是:
A(const A& other),不能漏const和引用 - 赋值运算符要处理自赋值:
if (this == &other) return *this; - 先释放当前对象持有的资源(如
delete[] data;),再分配新内存并拷贝内容 - 别忘了同步拷贝所有成员:不只是指针,还有
size、capacity等配套字段
示例关键片段:
MyString(const MyString& other) {
len = other.len;
data = new char[len + 1];
strcpy(data, other.data); // 深拷贝字符串内容
}
现代 C++ 更推荐用 RAII 容器替代裸指针
用 std::string、std::vector 等标准容器,它们内部已正确实现了深拷贝语义。你不用写拷贝构造函数,编译器生成的默认版本就能安全工作。
为什么这样做更可靠:
- 容器的拷贝构造函数和赋值运算符都做了深拷贝 + 异常安全处理
- 避免手动
new/delete匹配错误,比如忘了delete[]或误用delete - 兼容移动语义:C++11 后,
std::vector在返回局部变量时会自动移动,比深拷贝更快 - 如果你必须用裸指针(比如对接 C API),至少用
std::unique_ptr或std::shared_ptr封装,它们自带正确的拷贝/移动规则
容易被忽略的析构函数与 Rule of Three/Five
只要类里需要自定义析构函数(因为要 delete 堆内存),就几乎一定需要同时提供拷贝构造函数和赋值运算符——这就是 Rule of Three。C++11 后还要加上移动构造函数和移动赋值运算符,变成 Rule of Five。
常见疏漏点:
- 写了析构函数,但忘了重载
operator=,导致a = b;时发生浅拷贝泄漏 - 移动操作没禁用或没正确定义,导致对象被移动后又被析构(双重释放)
- 拷贝构造函数里调用了
this->data = other.data;而不是new分配,本质还是浅拷贝
最稳妥的做法:要么全用 RAII 容器,要么严格遵循 Rule of Five,缺一不可。










