forked from hengqiali/AwesomeCpp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
深拷贝和浅拷贝
82 lines (78 loc) · 4.57 KB
/
深拷贝和浅拷贝
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
https://blog.csdn.net/qq_29344757/article/details/76037255 复制构造函数和深拷贝、浅拷贝
https://blog.csdn.net/u010700335/article/details/39830425
C++中类的拷贝有两种:深拷贝,浅拷贝:当出现类的等号赋值时,即会调用拷贝函数
这次深拷贝的对象res1是个右值(ResourceOwner(“res1”)的返回值),其实它马上就要被回收了。所以本身是不需要独享资源的。我把问题描述再重复一次,这次应该就好理解了:&&用来区分右值,这样在这个右值 1)是一个构造函数或赋值函数的参数,和2)对应的类包含指针,并指向一个动态分配的资源(内存)时,就可以在函数内避免深拷贝。如果深拷贝右值的资源不合理,那什么操作是合理的呢?答案是Move继续讨论move语义。解决方法很简单,如果参数是右值,就不拷贝,而是直接“搬”资源。我们先把赋值函数用右值引用重载下:ResourceOwner& operator=(ResourceOwner&& other) {
theResource = other.theResource;
other.theResource = NULL;
}复制代码这个新的赋值函数就叫做move赋值函数。move构造函数也可以用差不多的办法实现,这里先不赘述了。如果不太好理解的话,可以这么来:比如你卖了个旧房子搬新家,搬家的时候不一定要把家具都丢掉再买新的对伐(我们在🌰3里面就丢掉了)。你也可以把家具“搬”到新家去。
一:两个的区别
1 在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的;但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象,所以,此时,必须采用深拷贝。
2 深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必须要用深拷贝。
二 带实例的解释
c++默认的拷贝构造函数是浅拷贝
浅拷贝就是对象的数据成员之间的简单赋值,如你设计了一个没有类而没有提供它的复制构造函数,当用该类的一个对象去给令一个对象赋值时所执行的过程就是浅拷贝,如:
class A
{
public:
A(int _data) : data(_data){}
A(){}
private:
int data;
};
int main()
{
A a(5), b = a; // 仅仅是数据成员之间的赋值
}
这一句b = a;就是浅拷贝,执行完这句后b.data = 5;
如果对象中没有其他的资源(如:堆,文件,系统资源等),则深拷贝和浅拷贝没有什么区别,
但当对象中有这些资源时,例子:
class A
{
public:
A(int _size) : size(_size)
{
data = new int[size];
} // 假如其中有一段动态分配的内存
A(){};
~A()
{
delete [] data;
} // 析构时释放资源
private:
int* data;
int size;
}
int main()
{
A a(5), b = a; // 注意这一句
}
这里的b = a会造成未定义行为,因为类A中的复制构造函数是编译器生成的,所以b = a执行的是一个浅拷贝过程。我说过浅拷贝是对象数据之间的简单赋值,比如:
b.size = a.size;
b.data = a.data; // Oops!
这里b的指针data和a的指针指向了堆上的同一块内存,a和b析构时,b先把其data指向的动态分配的内存释放了一次,而后a析构时又将这块已经被释放过的内存再释放一次。对同一块动态内存执行2次以上释放的结果是未定义的,所以这将导致内存泄露或程序崩溃。
所以这里就需要深拷贝来解决这个问题,深拷贝指的就是当拷贝对象中有对其他资源(如堆、文件、系统等)的引用时(引用可以是指针或引用)时,对象的另开辟一块新的资源,而不再对拷贝对象中有对其他资源的引用的指针或引用进行单纯的赋值。如:
class A
{
public:
A(int _size) : size(_size)
{
data = new int[size];
} // 假如其中有一段动态分配的内存
A(){};
A(const A& _A) : size(_A.size)
{
data = new int[size];
} // 深拷贝
~A()
{
delete [] data;
} // 析构时释放资源
private:
int* data;
int size;
}
int main()
{
A a(5), b = a; // 这次就没问题了
}
总结:深拷贝和浅拷贝的区别是在对象状态中包含其它对象的引用的时候,当拷贝一个对象时,如果需要拷贝这个对象引用的对象,则是深拷贝,否则是浅拷贝