C++ 中的浅拷贝和深拷贝
本文字数: 6.4k 阅读时长 ≈ 6 分钟
介绍浅拷贝和深拷贝的含义以及在 C++ 中的实现
(导读)短文一篇。主要介绍浅拷贝和深拷贝的含义以及在 C++ 中的实现
问题引入
先来看一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class ShallowCopyObject { public: int basicType; int *refType; ShallowCopyObject() { basicType = 10; refType = new int[10]; for (int i = 0; i < 10; i++) { refType[i] = i; } } };
|
我定义了一个 ShallowCopyObject
类,它包括两个成员变量
basicType
:类型为语言内置基本类型的 int
refType
:类型为指针,指向 int
类型数据
与此同时,我还定义了一个无参构造函数,用于初始化 basicType
和 refType
的值。其中 basicType
初始化为 10;refType
初始化为一个大小为 10 的数组,依次存储 0~9 的整数。
问题一
假设主函数 main
中有如下语句,那么终端会输出什么呢?
1 2 3 4 5 6 7 8 9
| ShallowCopyObject a; ShallowCopyObject b = a;
cout << "Before changing value of b.basicType" << endl; cout << "b.basicType " << b.basicType << endl; b.basicType = 233; cout << "After changing value of b.basicType" << endl; cout << "a.basicType " << a.basicType << endl; cout << "b.basicType " << b.basicType << endl;
|
先自己分析一下,再上机敲一敲,看看自己的思路对不对。
问题二
类似的,下面的语句会输出什么呢?
1 2 3 4 5 6 7 8 9
| ShallowCopyObject a; ShallowCopyObject b = a;
cout << "Before changing value of b.refType[6]" << endl; cout << "b.refType[6] " << b.refType[6] << endl; b.refType[6] = 666; cout << "After b.refType[6]" << endl; cout << "a.refType[6] " << a.refType[6] << endl; cout << "b.refType[6] " << b.refType[6] << endl;
|
再试着自己分析一下,上机敲一敲,看看自己的思路对不对。
输出结果
1 2 3 4 5 6 7 8 9
| ShallowCopyObject a; ShallowCopyObject b = a;
cout << "Before changing value of b.basicType" << endl; cout << "b.basicType " << b.basicType << endl; b.basicType = 233; cout << "After changing value of b.basicType" << endl; cout << "a.basicType " << a.basicType << endl; cout << "b.basicType " << b.basicType << endl;
|
第一个问题,也就是上面的代码,会输出如下结果
Before changing value of b.basicType b.basicType 10 After changing value of b.basicType a.basicType 10 b.basicType 233
由此可见,两个对象基本数据类型的成员变量是相互独立的,不会相互影响
1 2 3 4 5 6 7 8 9
| ShallowCopyObject a; ShallowCopyObject b = a;
cout << "Before changing value of b.refType[6]" << endl; cout << "b.refType[6] " << b.refType[6] << endl; b.refType[6] = 666; cout << "After b.refType[6]" << endl; cout << "a.refType[6] " << a.refType[6] << endl; cout << "b.refType[6] " << b.refType[6] << endl;
|
而第二个问题,它的结果可能出乎意料:
Before changing value of b.refType[6] b.refType[6] 6 After b.refType[6] a.refType[6] 666 b.refType[6] 666
我们惊奇的发现,对 b
对象的成员变量修改,竟然影响到了 a
对象中的值!
这一切的原因,都是由于在 C++ 中,默认对象之间的拷贝(包括默认复制构造函数和默认赋值语句)是浅拷贝。
什么是浅拷贝?
那么,什么是浅拷贝呢?
简单的来说,浅拷贝就是逐个字节的拷贝。也就是说,拷贝后每一个成员变量的值都相同。如果该值是基本数据类型,那么该值被拷贝;如果该值是引用数据类型(如对象、指针等),那么该值(注意:这里的值是指地址)也会被拷贝。
由此可知,针对第一个问题,由于改变的是基本类型的数据,它是独立的一份拷贝,因而另一个对象值的修改并不会影响被拷贝的对象;然而,在第二个问题中,由于浅拷贝,b
中 refType
指向了和 a
对象 refType
相同的位置(因为拷贝了地址),因而在 b
中修改 refType
数组中的值,会影响到对象 a
。
一句话描述
浅拷贝会共享引用数据类型成员变量(指针指向同一个地址),而不共享原始数据类型的成员变量
什么是深拷贝?
有时候,我们并不希望拷贝对象时,其引用成员变量指向同一个引用数据类型的数据对象,而希望它们指向不同的位置,但是这些位置存储的值是相同的。这就需要用到深拷贝。
一句话描述
深拷贝不会共享引用数据类型成员变量(它们的指针指向不同地址,但是拷贝后指针指向地址所存储的值是相等的),也不共享原始数据类型的成员变量
实现深拷贝
在 C++ 中可以自定义复制构造函数、重载赋值运算符,实现深拷贝。对于本文开头提出的问题,可以做如下改进。
1 2 3 4 5 6 7 8 9 10
| // 自定义复制构造函数 DeepCopyObject(const DeepCopyObject &obj) { basicType = obj.basicType; refType = new int[10]; // 引用类型成员变量重新申请空间 for (int i = 0; i < 10; i++) // 将值逐个拷贝到新申请的空间中 { refType[i] = obj.refType[i]; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| // 重载赋值运算符 DeepCopyObject &operator=(const DeepCopyObject &obj) { basicType = obj.basicType; if (this == &obj) // obj = obj; 情况 return *this; delete[] refType; refType = new int[10]; for (int i = 0; i < 10; i++) { refType[i] = obj.refType[i]; }
return *this; }
|
总结
- 浅拷贝会共享引用数据类型成员变量(指针指向同一个地址),而不共享原始数据类型的成员变量
- 深拷贝不会共享引用数据类型成员变量(它们的指针指向不同地址,但是拷贝后指针指向地址所存储的值是相等的),也不共享原始数据类型的成员变量
- 在 C++ 中可以自定义复制构造函数、重载赋值运算符,实现深拷贝
附:实现深拷贝完整代码
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
| #include <iostream>
using namespace std;
class DeepCopyObject { public: int basicType; int *refType;
DeepCopyObject() { basicType = 10; refType = new int[10]; for (int i = 0; i < 10; i++) { refType[i] = i; } }
DeepCopyObject(const DeepCopyObject &obj) { basicType = obj.basicType; refType = new int[10]; for (int i = 0; i < 10; i++) { refType[i] = obj.refType[i]; } }
~DeepCopyObject() { if (refType) delete[] refType; }
DeepCopyObject &operator=(const DeepCopyObject &obj) { basicType = obj.basicType; if (this == &obj) // obj = obj; 情况 return *this; delete[] refType; refType = new int[10]; for (int i = 0; i < 10; i++) { refType[i] = obj.refType[i]; }
return *this; } };
int main() {
DeepCopyObject a; DeepCopyObject b = a; // 调用 DeepCopyObject(const DeepCopyObject & obj)
// b = a; // 调用 DeepCopyObject & operator=(const DeepCopyObject & obj)
cout << "Before changing value of b.basicType" << endl; cout << "b.basicType " << b.basicType << endl; b.basicType = 233; cout << "After changing value of b.basicType" << endl; cout << "a.basicType " << a.basicType << endl; cout << "b.basicType " << b.basicType << endl;
cout << "Before changing value of b.refType[6]" << endl; cout << "b.refType[6] " << b.refType[6] << endl; b.refType[6] = 666; cout << "After b.refType[6]" << endl; cout << "a.refType[6] " << a.refType[6] << endl; cout << "b.refType[6] " << b.refType[6] << endl;
return 0; }
|