C++之深浅拷贝

C++在拷贝对象时默认做的浅拷贝。所谓浅拷贝,完成数据成员的一一复制。
当数据成员中没有指针时,浅拷贝是可行的;但当数据成员中有指针时,如果采用简单的浅拷贝,
则两类中的两个指针将指向同一个地址,这会产生两个问题:

  • 一个类对象释放了其指针指向的数据后,导致也指向这块数据的指针成为野指针(指针悬挂),产生潜在危险
  • 另一个对象析构时,又释放了一次这块内存,对同一块动态内存执行2次以上释放的结果是未定义的,所以这将导致错误或程序崩溃。

此时必须采用深拷贝。深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存指针指向的数据,
从而也就解决了指针悬挂和内存释放两次的问题。
简而言之,当数据成员中有指针时,复制构造函数必须要用深拷贝。

C++默认的拷贝构造函数是浅拷贝
浅拷贝就是对象的数据成员之间的简单赋值,如你设计了一个类而没有提供它的复制构造函数,
当用该类的一个对象去给另一个对象赋值时所执行的过程就是浅拷贝(默认的拷贝构造函数),如:

1
2
3
4
5
6
7
8
9
10
11
12
13
class A 
{
public:
A(int _data) : data(_data){}
A(){}
private:
int data;
};
int main()
{
A a(5);
b = a; // 默认copy构造函数为浅拷贝,仅仅是数据成员之间的赋值
}

如果对象中没有其他的资源(如:堆,文件,系统资源等),则深拷贝和浅拷贝没有什么区别,但当对象中有这些资源时,例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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对象的data指针指向同一块内存,会被释放两次
}

这里b的指针data和a的指针指向了堆上的同一块内存,a和b析构时,b先把其data指向的动态分配的内存释放了一次,
而后a析构时又将这块已经被释放过的内存再释放一次。对同一块动态内存执行2次以上释放的结果是未定义的,所以这将导致错误或程序崩溃。
此外a析构时将数据内存释放,导致b中指针成为野指针,可能使用了b中野指针,这会产生问题。

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
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; // 调用自定义的copy构造函数,实现深拷贝
}

总结:深拷贝和浅拷贝的区别是在对象状态中包含其它对象的引用时,拷贝一个对象,如果同时需要拷贝这个对象引用的对象,则是深拷贝,否则是浅拷贝。

参考:

------ 本文结束------
赞赏此文?求鼓励,求支持!
  • 本文标题: C++之深浅拷贝
  • 本文作者: Jiang.G.F
  • 创建于: 2019年12月21日 - 22时12分
  • 更新于: 2020年03月03日 - 11时03分
  • 本文链接: https://gfjiangly.github.io/C++/deep_copy.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
0%