引言
- 在软件开发中,我们经常需要复制对象,以便在程序的不同部分或不同线程之间共享数据或进行操作,而深拷贝和浅拷贝是实现这一目标的两种常用方式。在C++中,大家常用拷贝构造函数和赋值运算符函数都是用来复制一个对象的数据成员的。很多时候大家因为不熟悉这两者本质的区别导致程序出错甚至崩溃。
两者的定义
深拷贝(Deep Copy)
- 深拷贝是指创建一个新的对象,并将原始对象的所有成员变量都复制到新对象中。这包括所有指针指向的动态分配内存。这意味着,当一个对象被复制时,它会创建自己的内存空间,而不是共享原始对象的内存空间。因此,对新对象的修改不会影响原始对象。
浅拷贝(Shallow Copy)
- 浅拷贝是指创建一个新的对象,并将原始对象的所有成员变量都复制到新对象中,但是对于指向动态分配内存的指针,只是复制指针本身,而不是指针指向的内存。这意味着,当一个对象被复制时,它会共享原始对象的内存空间。因此,对新对象或原始对象的任何修改都会影响到另一个对象。
代码
下面是一个简单易懂的例子,用于说明深拷贝和浅拷贝的区别:
#include <iostream>
using namespace std;
class ShallowCopy {
public:
int *data;
ShallowCopy(int d) {
data = new int;
*data = d;
}
// 实现浅拷贝构造函数
ShallowCopy(const ShallowCopy& other) {
data = other.data;
}
~ShallowCopy() {
delete data;
}
};
class DeepCopy {
public:
int *data;
DeepCopy(int d) {
data = new int;
*data = d;
}
// 实现深拷贝构造函数
DeepCopy(const DeepCopy& other) {
data = new int;
*data = *(other.data);
}
~DeepCopy() {
delete data;
}
};
int main() {
// 浅拷贝
ShallowCopy a(100);
ShallowCopy b = a;
*b.data = 200;
cout << *a.data << endl; // 输出200
// 深拷贝
DeepCopy c(100);
DeepCopy d = c;
*d.data = 200;
cout << *c.data << endl; // 输出100
return 0;
}
输出结果
200
100
小结
- 在这个例子中,我们定义了两个类 ShallowCopy 和 DeepCopy,它们都拥有一个 int 型成员变量 data,并分别实现了浅拷贝和深拷贝的构造函数。在主函数中,我们先分别用 a 和 c 创建了两个对象,它们的 data 值都是 100。然后我们用 b 和 d 分别对 a 和 c 进行拷贝。对于浅拷贝,b 和 a 共享同一个 data 指针,所以对 b 进行修改也会影响 a。对于深拷贝,d 和 c 拥有独立的 data 指针,所以对 d 进行修改不会影响 c。
拓展
-
那我们什么时候需要用到深拷贝,什么时候又需要用到浅拷贝?
-
以下是深拷贝的场景:
- 当我们需要在不同的对象之间共享数据,但是又需要这些对象拥有独立的内存空间时。例如,如果我们有一个包含大量数据的对象,而我们需要将其复制到另一个对象中进行处理,但不希望对原始对象进行更改,那么我们可以使用深拷贝来创建一个新的独立对象。
- 当我们需要对复杂的对象进行副本,包括指针,而且需要保证新对象的指针指向不同的内存地址时。在这种情况下,浅拷贝可能会出现问题,因为它只是复制指针而不是数据。使用深拷贝可以避免这种问题,因为它会复制指针指向的数据
- 当我们需要在对象之间共享指针,但是不希望它们共享同一块内存空间时,我们可以使用深拷贝来创建新的独立对象。
-
以下是浅拷贝的场景:
- 当对象拥有简单的数据类型或只有少量的指针时,使用浅拷贝比较简单且高效。
- 当我们需要创建对象的简单副本而不涉及对象的数据时,使用浅拷贝会更加方便和高效。
不是废话的废话
- 需要注意的是,对于包含指针成员变量的对象,在使用浅拷贝时,需要小心处理,以免发生意外的指针共享和内存泄漏等问题。因此,在实际应用中,我们需要根据实际情况来选择深拷贝或浅拷贝,以保证程序的正确性和效率。