一 概述
为了解决浅层复制导致的迷途指针问题,我们必须创建自己的复制构造函数,并且在函数里为我们的成员变量分配内存,这样,在分配完内存后,旧对象的成员变量就可以复制到新的内存区域中,两个对象的成员变量都各自又有自己的内存区域,一个对象在析构后不再会影响到另一个,我们把这种复制方式叫做深层复制。
二 过程分析
2.1 复制函数部分
1 2 3 4 5 6
| A(const A &a) { cout << "复制构造函数执行...\n" << endl; x = new int; *x = *(a.x); }
|
- 因为在这个函数体中不会修改别名a所引起的原始对象的值。所以将别名a定义为别名常量。这样假如我们试图修改传递进来的对象,程序就会报错
- new int;在堆中开辟一块新空间
- x = new int;用复制构造函数创建的新对象的指针成员x来指向它,这样两个对象的x指针就指向了不同的内存空间,我们将传递进来的对象的数据复制给新对象
- 传递进来的对象指的是别名a所引用的对象,而数据则指的是该对象的x指针指向的空间处的值
- *(a.x);因为括号的优先级最高,所以括号内的语句先执行。先通过对象a调用x指针成员后,再通过"*"读取x指针指向的空间处的值
- *x = *(a.x);我们将这个值再赋给由复制构造函数创建的新对象的x指针所指向的空间
- 这样复制的就不是两个对象的指针成员x,而是该指针指向的空间处的值
- 为了便于观察两个对象的指针成员x指向的空间处的值,我们再定义一个print()函数。该函数的作用是输出调用该函数的对象的x指针指向的空间处的值,因为print函数不会修改对象的成员指针x,所以在函数体前面加上const
- 然后再定义一个set函数,该函数用来设置调用set函数的对象的成员指针x指向处的值。这个set函数有个参数i,参数i的值即我们要设置的值
- A b = (*a);用一个对象的值初始化另一个对象,将会调用复制构造函数来完成两个对象之间成员的拷贝。由于执行的不是浅层复制,所以复制的不是两个对象的x指针,而是x指针指向的空间处的值
2.2 原理
- 为了实现这一点,我们先让b对象它的x指针指向一个新空间,然后将a对象的数据拷贝到这个新空间中来。这个a对象的数据指的是它的x指针指向的空间处的数据。
2.3 验证
- 完成了复制工作以后,我们可以查看一下两个对象的数据,检测一下复制是否正确,要查看对象的数据可以调用print函数
三 图例说明(深层拷贝)
- 我们将指针前面加上*号,如:*x=*(a.x);
- 这样的作用是将旧对象的成员指针x指向的空间处的数据赋给新对象的成员指针x指向的空间,由于两个对象的成员指针分别指向不同的内存空间,因为我们必须拷贝指针指向的空间处的数据而不是指针本身
四 示例及结果输出
4.1 代码
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
| #include<iostream> using namespace std; class A { private: int *x; public: A() { x = new int; *x = 5; } ~A() { delete x; x = 0; } A(const A &a) { cout << "复制构造函数执行...\n" << endl; x = new int; *x = *(a.x); } int print() const {return *x;} void set(int i) {*x = i;} }; int main() { A *a = new A(); cout << "a:" << a->print()<<endl; A b = (*a); cout << "a:" << a->print()<<endl; cout << "b:" << b.print()<<endl; b.set(32); cout << "a:" << a->print()<<endl; delete a; cout << "b:" << b.print()<<endl; return 0; }
|
4.2 输出结果
1 2 3 4 5 6 7
| a:5 复制构造函数执行...
a:5 b:5 a:5 b:32
|