浅拷贝是按字节复制对象内存,深拷贝需手动实现以避免指针共享;当类含裸指针、自定义深拷贝成员或独占资源时必须重写拷贝构造函数、赋值运算符并遵循三/五法则。

浅拷贝就是按字节复制对象内存,深拷贝要自己重写拷贝构造函数
默认情况下,C++ 的拷贝构造函数和赋值运算符只做浅拷贝:它把源对象的每个字节原样复制到新对象里。如果类里有 char*、int* 这类裸指针成员,浅拷贝会让两个对象指向同一块堆内存——后续一个对象析构时 delete 了,另一个再访问或再次 delete 就会崩溃或未定义行为。
什么时候必须手动实现深拷贝
当类中存在以下任一情况时,编译器自动生成的拷贝构造函数就不够用了:
- 成员变量是裸指针(如
char*、int*),且指向的是new出来的堆内存 - 成员变量是自定义类对象,而该类本身已定义了深拷贝逻辑(此时你得在当前类中调用它的拷贝构造)
- 资源需要独占(比如文件句柄、socket 描述符),不能共享
典型例子:
class String {
private:
char* data_;
size_t len_;
public:
String(const char* s) : len_(strlen(s)) {
data_ = new char[len_ + 1];
strcpy(data_, s);
}
// ❌ 缺少深拷贝构造函数 → 浅拷贝导致 double free
String(const String& other) : len_(other.len_) {
data_ = other.data_; // 危险!仅复制指针值
}
~String() { delete[] data_; }
};
深拷贝的正确写法:三步走
实现深拷贝的核心是「分配新内存 + 复制内容 + 独立管理」。以 String 类为例:
- 在拷贝构造函数里用
new为data_分配等长新内存 - 用
strcpy或std::copy把原内容逐字节复制过去 - 务必同步更新所有相关成员(如
len_),别漏掉 - 如果类还支持赋值运算符(
operator=),也得同样处理,并注意自我赋值检查(if (this == &other) return *this;)
修正后的构造函数:
String(const String& other) : len_(other.len_) {
data_ = new char[len_ + 1];
strcpy(data_, other.data_);
}
现代 C++ 更推荐用 RAII 容器替代裸指针
手动管理深拷贝容易出错,而且要同时写拷贝构造、赋值、析构(即“三法则”,C++11 后是“五法则”)。更安全的做法是直接用标准库类型:
立即学习“C++免费学习笔记(深入)”;
- 用
std::string替代char*,它内部已实现深拷贝 - 用
std::vector替代int*+size_t计数 - 用
std::unique_ptr表示独占所有权,移动语义天然避免浅拷贝歧义
这样连拷贝构造函数都不用写,编译器生成的版本就完全安全。但如果你正在封装底层资源、写基础库、或面试被问到原理,那就绕不开手动深拷贝的细节。
最容易被忽略的一点:深拷贝不是只改构造函数,还要同步检查 operator= 和移动操作是否破坏了资源独占性——尤其当类开始支持移动语义后,浅拷贝逻辑可能意外残留在移动路径里。










