深井蛙
72.70M · 2026-03-07
深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是处理对象复制时非常重要的概念,尤其是在对象内部包含指针或引用指向动态分配的内存时。它们的主要区别在于如何处理这些内部资源。
情景: 你想把笔记本里的信息给你的朋友。
操作: 你没有把整个笔记本复制一份,而是只复制了笔记本的“目录” ,或者说,你只告诉了你的朋友:“这些信息在我的笔记本的第几页、第几行。”
结果:
你的朋友也“拥有”了这些信息,但实际上,他只是知道去哪里看你的笔记本。
问题来了:
总结: 浅拷贝就像是共享一个链接。大家看到的是同一个内容,内容变了,大家看到的都变。如果源头没了,链接就失效了。(类似于我们经常做的配钥匙,而不是复制房子)
int, char, double),那么它们的值会被直接复制。但如果成员变量是指针或引用,那么新对象会复制这个指针或引用的地址,而不是它所指向的数据。例子 (C++ 伪代码):
class MyClass {
public:
int* data;
MyClass(int val) {
data = new int(val); // 动态分配内存
}
// 默认的拷贝构造函数或赋值运算符会执行浅拷贝
// MyClass(const MyClass& other) {
// data = other.data; // 只是复制了指针地址
// }
~MyClass() {
delete data; // 析构时释放内存
}
};
MyClass obj1(10); // obj1.data 指向一块内存,里面是 10
MyClass obj2 = obj1; // 浅拷贝,obj2.data 和 obj1.data 指向同一块内存
// 此时 obj1.data 和 obj2.data 都指向同一个地址
// 如果通过 obj2 修改数据:
*obj2.data = 20;
// 那么 *obj1.data 也会变成 20
// 当 obj1 和 obj2 销毁时,会尝试对同一块内存 delete 两次,导致错误。
情景: 你想把笔记本里的信息给你的朋友。
操作: 你不是只告诉朋友目录,而是把你的笔记本里的所有信息都一字不差地抄写了一份,然后用一个全新的笔记本把这些抄写出来的信息装好,再把这个新笔记本给了你的朋友。
结果:
现在,你有一个笔记本,你的朋友也有一个笔记本。
这两个笔记本里的信息内容一模一样,但它们是完全独立的两份信息。
好处:
总结: 深拷贝就像是复制一份实体文件。大家都有自己的一份,互不影响。
例子 (C++ 伪代码):
class MyClass {
public:
int* data;
MyClass(int val) {
data = new int(val);
}
// 深拷贝构造函数
MyClass(const MyClass& other) {
data = new int(*other.data); // 为新对象重新分配内存,并复制数据
}
// 深拷贝赋值运算符
MyClass& operator=(const MyClass& other) {
if (this != &other) { // 防止自赋值
delete data; // 释放旧资源
data = new int(*other.data); // 分配新资源并复制数据
}
return *this;
}
~MyClass() {
delete data;
}
};
MyClass obj1(10); // obj1.data 指向一块内存,里面是 10
MyClass obj2 = obj1; // 深拷贝,obj2.data 指向另一块内存,里面也是 10
// 此时 obj1.data 和 obj2.data 指向不同的地址
// 如果通过 obj2 修改数据:
*obj2.data = 20;
// 那么 *obj1.data 仍然是 10,不受影响。
// 当 obj1 和 obj2 销毁时,它们各自释放自己的内存,不会冲突。
| 特性 | 浅拷贝 (Shallow Copy) | 深拷贝 (Deep Copy) |
|---|---|---|
| 复制内容 | 复制对象本身的值和内部指针/引用的地址。 | 复制对象本身的值,并为内部指针/引用指向的资源重新分配内存并复制数据。 |
| 资源共享 | 原始对象和新对象共享动态分配的资源。 | 原始对象和新对象拥有独立的动态分配资源。 |
| 修改影响 | 修改其中一个对象会影响另一个对象。 | 修改其中一个对象不会影响另一个对象。 |
| 内存释放 | 容易导致二次释放问题。 | 安全,每个对象管理自己的资源。 |
| 实现方式 | 默认的拷贝构造函数/赋值运算符通常是浅拷贝。 | 需要手动实现拷贝构造函数和赋值运算符,以处理动态资源。 |
| 效率 | 相对高效,因为只复制指针地址。 | 相对低效,因为需要分配新内存并复制所有数据。 |
在 C++ 中,如果你的类管理着动态内存或其他资源,通常需要遵循“三/五/零法则”(Rule of Three/Five/Zero),即如果需要自定义析构函数,那么通常也需要自定义拷贝构造函数和拷贝赋值运算符(或者移动构造函数和移动赋值运算符),以确保正确的深拷贝行为,避免资源泄漏和二次释放问题。