拷贝构造函数

  在C++的类中,拷贝构造函数是非常有用的。

  为便于调试和查看内存状态,本代码使用VS2015来调试。

  1.默认拷贝构造函数

  有下面一段代码:

 1 #include "stdafx.h"
 2 #include <iostream>
 3 
 4 using namespace std;
 5 
 6 class A
 7 {
 8 public:
 9     int n;
10     char c;
11     int data[10];
12 protected:
13 private:
14 };
15 
16 
17 int _tmain(int argc, _TCHAR* argv[])
18 {
19     A a;
20     for (int i = 0; i < 10; i++)
21     {
22         a.data[i] = i;
23     }
24     a.n = 123;
25     a.c = 'A';
26     
27     A b = a;
28 
29     return 0;
30 }

  在这段代码中,声明一个类A,定义一个A的对象a,再定义一个A的对象b,我们想让b与a相等。将断点设置到第29行,得到下面的结果。

  对象a和b的变量n,c和数组data的值完全一致,且a.data与b.data的地址不相同,这正是我们期待的结果。

  在这段代码中,我们并没有写有关拷贝构造函数的代码,C++会根据它内部的规则为我们隐式创建一个拷贝构造函数,我们管它叫做默认拷贝构造函数。但是很多时候我们并不能依赖默认的东西,看下边的例子。

  2.默认拷贝构造函数的局限性

  有下面一段代码(这是一段错误的代码):

 1 #include "stdafx.h"
 2 #include <iostream>
 3 
 4 using namespace std;
 5 
 6 class Matrix
 7 {
 8 public:
 9     Matrix()
10     {
11         pData = NULL;
12     }
13     ~Matrix()
14     {
15         if (pData)
16         {
17             delete[] pData;
18             pData = NULL;
19         }
20     }
21     int w;
22     int h;
23     int* pData;
24 protected:
25 private:
26 };
27 
28 int main()
29 {
30     Matrix a;
31     a.w = 3;
32     a.h = 3;
33     int size = a.w*a.h;
34     a.pData = new int[size];
35     for (int i = 0; i < size; i++)
36     {
37         a.pData[i] = i;
38     }
39 
40     Matrix b = a;
41 
42     return 0;
43 }

  在这段代码中,声明一个矩阵类Matrix,因为矩阵的大小是不一定的,所以只能定义指针动态申请内存了。定义一个Matrix的对象a,再定义一个Matrix的对象b,我们想让b与a相等。将断点设置到第29行,得到下面的结果。

  此时对象a和b的w,h和pData的地址是相同的。所以这是一段有问题的代码,因为这段代码并不能正确结束。因为当main()函数结束时,会先调用b的析构函数,此时b.pData已被释放,析构完b之后,紧接着析构a,释放a.pData内存时,就会出问题。

  当然有些人可能会觉得“我不这么写就可以了”,那么我们看下面这段代码(这是一段错误的代码)。

 1 #include "stdafx.h"
 2 #include <iostream>
 3 #include <list>
 4 
 5 using namespace std;
 6 
 7 class Matrix
 8 {
 9 public:
10     Matrix()
11     {
12         pData = NULL;
13     }
14     ~Matrix()
15     {
16         if (pData)
17         {
18             delete[] pData;
19             pData = NULL;
20         }
21     }
22     int w;
23     int h;
24     int* pData;
25 protected:
26 private:
27 };
28 
29 void CreateMatrixList(list<Matrix>& myList)
30 {
31     Matrix a;
32     a.w = 3;
33     a.h = 3;
34     int size = a.w*a.h;
35     a.pData = new int[size];
36     for (int i = 0; i < size; i++)
37     {
38         a.pData[i] = i;
39     }
40     myList.push_back(a);
41 }
42 
43 int main()
44 {
45     list<Matrix> aList;
46     CreateMatrixList(aList);
47 
48     auto pNode = aList.begin();
49     int m = (*pNode).pData[2];
50 
51     return 0;
52 }

  在这段代码中,我想创建一个矩阵链表,在第51行设置断点,得到如下结果。

  此时m的值并不是我们之前设置的2,而是一个不确定的数。我们在16行,40行,51行都设置断点进行调试。测得断点的停留顺序为40,16,51,16。也就是说析构函数走了两次,这说明在CreateMatrixList函数中,myList中的矩阵,并不是刚刚定义好的a,那只是一个副本,a已经被默默的干掉了。

  这个问题也纠结了我很长时间,为什么我的list对别人的类进行push_back都没什么问题,对我自己的类就行。后来我知道就是默认拷贝构造函数惹的祸。

  3.拷贝构造函数

  在上面的代码中,增加拷贝构造函数Matrix(const Matrix& m),如下:

 1 #include "stdafx.h"
 2 #include <iostream>
 3 #include <list>
 4 
 5 using namespace std;
 6 
 7 class Matrix
 8 {
 9 public:
10     Matrix()
11     {
12         pData = NULL;
13     }
14     Matrix(const Matrix& m)
15     {
16         w = m.w;
17         h = m.h;
18         if (m.pData)
19         {
20             pData = new int[w*h];
21             memcpy(pData, m.pData, w*h * sizeof(int));
22         }
23     }
24     ~Matrix()
25     {
26         if (pData)
27         {
28             delete[] pData;
29             pData = NULL;
30         }
31     }
32     int w;
33     int h;
34     int* pData;
35 protected:
36 private:
37 };
38 
39 void CreateMatrixList(list<Matrix>& myList)
40 {
41     Matrix a;
42     a.w = 3;
43     a.h = 3;
44     int size = a.w*a.h;
45     a.pData = new int[size];
46     for (int i = 0; i < size; i++)
47     {
48         a.pData[i] = i;
49     }
50     myList.push_back(a);
51 }
52 
53 int main()
54 {
55     list<Matrix> aList;
56     CreateMatrixList(aList);
57 
58     auto pNode = aList.begin();
59     int m = (*pNode).pData[2];
60 
61     return 0;
62 }

  在第61行的位置设置断点,得如下结果:

  得到m的值为2,程序能正常退出,运行成功!

转载于:https://www.cnblogs.com/songliduo/p/9313019.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值