在C++中,浅拷贝(Shallow Copy)和深拷贝(Deep Copy)是对象复制的两种不同方式,它们的核心区别在于如何处理对象内部的动态资源管理(如指针、文件句柄等)。
一、浅拷贝(Shallow Copy)
定义:
浅拷贝仅复制对象的成员值(包括指针的值),但不复制指针指向的实际内容。拷贝后的对象和原对象会共享同一块动态分配的内存。
特点:
- 默认行为:C++默认的拷贝构造函数和拷贝赋值运算符是浅拷贝。
- 速度快:仅复制指针地址,不复制实际数据。
- 风险高:如果多个对象共享同一块内存,可能导致双重释放(double-free)或悬空指针(dangling pointer)。
示例:
class ShallowString {
public:
char* data;
// 构造函数
ShallowString(const char* s) {
data = new char[strlen(s) + 1];
strcpy(data, s);
}
// 默认拷贝构造函数是浅拷贝:ShallowString(const ShallowString& other) : data(other.data) {}
};
int main() {
ShallowString s1("Hello");
ShallowString s2 = s1; // 浅拷贝:s2.data 和 s1.data 指向同一内存
delete s1.data; // s2.data 现在指向已释放的内存(悬空指针)
}
二、深拷贝(Deep Copy)
定义:
深拷贝会复制对象的所有内容,包括指针指向的动态分配资源。拷贝后的对象和原对象拥有独立的资源副本。
特点:
- 安全性高:每个对象管理自己的资源,避免共享内存问题。
- 需手动实现:需要自定义拷贝构造函数、拷贝赋值运算符和析构函数(遵循 Rule of Three)。
- 性能开销:复制大量数据时可能较慢。
示例:
class DeepString {
public:
char* data;
// 构造函数
DeepString(const char* s) {
data = new char[strlen(s) + 1];
strcpy(data, s);
}
// 深拷贝构造函数
DeepString(const DeepString& other) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
// 深拷贝赋值运算符
DeepString& operator=(const DeepString& other) {
if (this != &other) { // 防止自赋值
delete[] data; // 释放旧资源
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
return *this;
}
// 析构函数
~DeepString() {
delete[] data;
}
};
int main() {
DeepString s1("World");
DeepString s2 = s1; // 深拷贝:s2.data 是独立副本
delete s1.data; // 不影响 s2.data
}
三、关键区别
特性 | 浅拷贝 | 深拷贝 |
---|---|---|
复制内容 | 仅复制指针地址 | 复制指针指向的实际数据 |
资源所有权 | 共享资源 | 独立资源 |
默认行为 | 是 | 否(需手动实现) |
性能 | 快(仅复制指针) | 慢(复制数据) |
安全性 | 低(可能双重释放) | 高 |
四、何时需要深拷贝?
当类满足以下条件时,必须实现深拷贝:
- 包含动态分配的资源(如
new
分配的指针)。 - 需要确保对象的独立性(避免共享资源导致的副作用)。
五、现代C++的替代方案
- 使用智能指针(如
std::unique_ptr
、std::shared_ptr
)自动管理资源。 - 遵循 Rule of Zero:通过依赖智能指针等工具,避免手动实现拷贝控制函数。
#include <memory>
class SafeString {
std::unique_ptr<char[]> data; // 自动管理资源
public:
SafeString(const char* s) : data(std::make_unique<char[]>(strlen(s) + 1)) {
strcpy(data.get(), s);
}
// 无需手动实现拷贝构造函数、赋值运算符和析构函数!
};
总结
- 浅拷贝适合无动态资源的简单对象(如仅包含基本类型的类)。
- 深拷贝是管理动态资源时的必要手段。
- 现代C++中,优先使用智能指针和RAII(Resource Acquisition Is Initialization)来避免手动深拷贝。