如果一个类拥有资源,该类的对象进行复制时,如果资源重新分配,就是深拷贝,否则就是浅拷贝。

深拷贝:该对象和原对象占用不同的内存空间,即拷贝存储在栈空间中的内容,又拷贝存储在堆空间中的内容。
浅拷贝:该对象和原对象占用同一块内存空间,仅拷贝类中位于栈空间的内容。

浅拷贝和深拷贝

当类的成员变量中有指针变量时,最好使用深拷贝。因为当两个对象指向同一块内存空间,如果使用浅拷贝,当其中一个对象资源释放后(调用析构函数),对象指针指向的内存空间也被释放,此时另一个对象指向的就是垃圾内存。

浅拷贝实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
using namespace std;

class Test {
int *p;
public:
Test(int temp) {
this->p = new int(temp);
cout << "Test(int temp)" << endl;
}
~Test() {
if (p != nullptr) delete p;
cout << "~Test()" << endl;
}
};

int main() {
Test ex1(10);
Test ex2 = ex1; // 调用了默认的拷贝构造函数(浅拷贝)
return 0;
}

/*
运行结果:
Test(int tmp)
~Test()
*/

上述代码中,类对象 ex1、ex2 实际上指向同一块内存空间,对象析构时,ex2 先将内存释放了一次,之后析构对象 ex1 时又将这块已经释放过的内存再释放一次。对同一块内存空间释放了两次,会导致程序崩溃。

深拷贝实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
using namespace std;

class Test {
int *p;
public:
Test(int temp) {
this->p = new int(temp);
cout << "Test(int temp)" << endl;
}
// 定义拷贝构造函数
Test(const Test& temp) {
p = new int(*temp.p);
cout << "Test(const Test& temp)" << endl;
}
~Test() {
if (p != nullptr) delete p;
cout << "~Test()" << endl;
}
};

int main() {
Test ex1(10);
Test ex2 = ex1;
return 0;
}

/* 运行结果:
Test(int temp)
Test(const Test& temp)
~Test()
~Test()
*/

编译器生成的默认拷贝构造函数大部分都是浅拷贝,所以再特定场景下需要禁止编译器生成默认拷贝构造函数。再遇到需要使用堆内存的构造函数中,我们需要特别注意浅拷贝和深拷贝的使用方式,防止两个不同的对象指向同一块内存区域。

参考

深拷贝和浅拷贝的区别