CPP学习之——深层复制构造函数(10.13)

一 概述

为了解决浅层复制导致的迷途指针问题,我们必须创建自己的复制构造函数,并且在函数里为我们的成员变量分配内存,这样,在分配完内存后,旧对象的成员变量就可以复制到新的内存区域中,两个对象的成员变量都各自又有自己的内存区域,一个对象在析构后不再会影响到另一个,我们把这种复制方式叫做深层复制。

二 过程分析

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