首先看一个深拷贝的例子
view plaincopy to clipboardprint?
01.#include <iostream>
02.using namespace std;
03.const int SIZE = 9;
04.const int DEFAULT_NUM = 6;
05.class A
06.{
07.private:
08. int *p;
09. int size;
10.public:
11. A(A&);
12. A();
13. ~A();
14. void set(); //设置A的各个元素的值
15. void showEle(); //输出p的各个元素
16. void showAdd(); //输出p的地址
17. int *getP();
18. int getSize();
19.};
20.
21.A::A()
22.{
23. p = NULL;
24. size = 0;
25.}
26.
27.A::~A()
28.{
29. delete []p;
30. p = NULL;
31. size = 0;
32.}
33.
34.A::A(A &right)
35.{
36. size = right.getSize();
37. p = new int[size];
38.
39. memcpy(p,right.getP(), sizeof(int)*size);
40.}
41.
42.void A::set()//设置A的各个元素的值
43.{
44. p = new int[SIZE];
45. size = SIZE;
46.
47. for(int n=0; n<size; n++)
48. {
49. p[n] = DEFAULT_NUM;
50. }
51.}
52.
53.void A::showEle()
54.{
55. for(int n=0; n<size; n++)
56. {
57. cout<<p[n]<<endl;
58. }
59.}
60.
61.void A::showAdd()
62.{
63. cout<<p<<endl;
64.}
65.
66.int *A::getP()
67.{
68. return p;
69.}
70.
71.int A::getSize()
72.{
73. return size;
74.}
75.
76.int main()
77.{
78. A a;
79. a.set();
80. a.showEle();
81. a.showAdd();
82.
83. A b(a);
84. b.showEle();
85. b.showAdd();
86.
87. return 0;
88.}
#include <iostream>
using namespace std;
const int SIZE = 9;
const int DEFAULT_NUM = 6;
class A
{
private:
int *p;
int size;
public:
A(A&);
A();
~A();
void set(); //设置A的各个元素的值
void showEle(); //输出p的各个元素
void showAdd(); //输出p的地址
int *getP();
int getSize();
};
A::A()
{
p = NULL;
size = 0;
}
A::~A()
{
delete []p;
p = NULL;
size = 0;
}
A::A(A &right)
{
size = right.getSize();
p = new int[size];
memcpy(p,right.getP(), sizeof(int)*size);
}
void A::set()//设置A的各个元素的值
{
p = new int[SIZE];
size = SIZE;
for(int n=0; n<size; n++)
{
p[n] = DEFAULT_NUM;
}
}
void A::showEle()
{
for(int n=0; n<size; n++)
{
cout<<p[n]<<endl;
}
}
void A::showAdd()
{
cout<<p<<endl;
}
int *A::getP()
{
return p;
}
int A::getSize()
{
return size;
}
int main()
{
A a;
a.set();
a.showEle();
a.showAdd();
A b(a);
b.showEle();
b.showAdd();
return 0;
} 可能我写的有点乱,成员变量包括一个指针,其实是一个数组,想要深拷贝。
所以我们需要重载拷贝构造函数。
初学C++的时候,直接记住了参数是引用类型,当时对语言没有深入的理解(虽然现在也不深入),也就没有往心里去,这些天模拟STL,遇到了好多关于深拷贝的问题,而且这问题出的很怪异(我会在下午提出),因此想到了拷贝构造函数,所以在这里需要深究一下。
拷贝构造函数的参数是引用类型。
我们可以试想一下不是引用的情况,也就是说,拷贝构造函数是下边这个样子:
view plaincopy to clipboardprint?
01.A::A(A right)
02.{
03. size = right.getSize();
04. p = new int[size];
05.
06. memcpy(p,right.getP(), sizeof(int)*size);
07.}
A::A(A right)
{
size = right.getSize();
p = new int[size];
memcpy(p,right.getP(), sizeof(int)*size);
} 参数传递的是一个对象,按值传递,那么这个对象就会被复制到right,复制的时候又会调用拷贝构造函数,那又会复制一个对象到第二次调用的这个拷贝构造函数里边,因此又需要第三次调用拷贝构造函数。。。想一下,是不是无限循环?
简单地说:传值需要调用拷贝构造函数,而不是引用类型的拷贝构造函数又需要调用拷贝构造函数。因此无限循环。
Ok,把引用的这个问题说明白,下边说一下我这几天遇到的这个问题:
请先看一段代码:
view plaincopy to clipboardprint?
01.#include <iostream>
02.#include <vector>
03.using namespace std;
04.
05.class A
06.{
07.private:
08. int *p;
09. int size;
10.public:
11. void setSize(int);
12. void setP(int*);
13. int getSize() const;
14. int *getP() const;
15. void fun(); //设置相应的值,为拷贝做准备
16. void show();
17. ~A(); //析构函数
18. A();
19.
20. A operator = (A right) //重载的等号
21. {
22. int *rightP = right.getP();
23.
24. size = right.getSize();
25. p = new int[size];
26. memcpy(p, rightP, size*sizeof(int));
27.
28. return *this;
29. }
30.
31.;
32.};
33.void A::setSize(int newSize)
34.{
35. size = newSize;
36.}
37.
38.void A::setP(int *newP)
39.{
40. p = newP;
41.}
42.
43.void A::show()
44.{
45. for(int n=0;n<size;n++)
46. {
47. cout<<p[n]<<endl;
48. }
49.}
50.
51.int *A::getP() const
52.{
53. return p;
54.}
55.
56.int A::getSize() const
57.{
58. return size;
59.}
60.
61.A::A()
62.{
63. p = NULL;
64. size = 0;
65.}
66.
67.void A::fun()
68.{
69. p = new int [9];
70. for(int n=0;n<9;n++)
71. {
72. p[n] = n+1;
73. }
74. size = 9;
75.}
76.
77.//关键是下边的析构函数,如果进行delete操作,该程序就会出错
78.A::~A()
79.{
80. delete []p;
81. p = NULL;
82. size = 0;
83.}
84.
85.int main()
86.{
87. A a;
88. a.fun();
89. cout<<"a.show():"<<endl;
90. a.show();
91.
92. A b;
93. b = a;
94. cout<<"b.show()"<<endl;
95. b.show();
96.
97. cout<<"a.show()"<<endl;
98. a.show();
99.
100. return 0;
101.}
#include <iostream>
#include <vector>
using namespace std;
class A
{
private:
int *p;
int size;
public:
void setSize(int);
void setP(int*);
int getSize() const;
int *getP() const;
void fun(); //设置相应的值,为拷贝做准备
void show();
~A(); //析构函数
A();
A operator = (A right) //重载的等号
{
int *rightP = right.getP();
size = right.getSize();
p = new int[size];
memcpy(p, rightP, size*sizeof(int));
return *this;
}
;
};
void A::setSize(int newSize)
{
size = newSize;
}
void A::setP(int *newP)
{
p = newP;
}
void A::show()
{
for(int n=0;n<size;n++)
{
cout<<p[n]<<endl;
}
}
int *A::getP() const
{
return p;
}
int A::getSize() const
{
return size;
}
A::A()
{
p = NULL;
size = 0;
}
void A::fun()
{
p = new int [9];
for(int n=0;n<9;n++)
{
p[n] = n+1;
}
size = 9;
}
//关键是下边的析构函数,如果进行delete操作,该程序就会出错
A::~A()
{
delete []p;
p = NULL;
size = 0;
}
int main()
{
A a;
a.fun();
cout<<"a.show():"<<endl;
a.show();
A b;
b = a;
cout<<"b.show()"<<endl;
b.show();
cout<<"a.show()"<<endl;
a.show();
return 0;
} 可以试着运行一下,在我电脑上边出现了下边这个错误
很恐怖的错误。呵呵,让我们详细分析一下。
第一次a.show()成功,说明对象a建立没有问题;b.show()失败,说明没有复制成功;第二次a.show()失败,说明a被修改了,可以猜想一下,是不是被调用了析构函数?
现在重载 = 的时候是按值传递,那么,我们传进去的是地址p和size。
之后是执行拷贝的函数,这个没啥问题,把right中的相关数据拷贝。
然后就开始执行析构函数了,首先析构的是传进来的参数,也就是right,由于right的p和主函数中a的p相等,因此right的p被delete了也就是a的p被delete了,在这里,我们把主函数中的a不小心析构了一点点(把p给delete了)。因为函数的返回的是值,所以第二步析构的是(*this),这样,就把复制后的p给delete了。到这里,我们悲剧地把内存中所有的p都delete了。
所以,当再次输出a和b的时候,全都是随机值。
细心的朋友可能会发现,当第二次输出a和b的时候仍然是输出了10个,也就是开始的size值。a的size是10不难理解。由于析构函数是在函数返回后调用的,所以返回的b的size是不改变的(返回是在改变以前)。
我们试着改一下:
下边是我在参数上边加了&,这样,当再次输出a的时候没有错误
view plaincopy to clipboardprint?
01.A operator = (A &right) //重载的等号
02.{
03. int *rightP = right.getP();
04. size = right.getSize();
05. p = new int[size];
06. memcpy(p, rightP, size*sizeof(int));
07. return *this;
08.}
A operator = (A &right) //重载的等号
{
int *rightP = right.getP();
size = right.getSize();
p = new int[size];
memcpy(p, rightP, size*sizeof(int));
return *this;
} 下边是在参数right和返回值都加了&,这样就没有错误了。
view plaincopy to clipboardprint?
01.A &operator = (A &right) //重载的等号
02.{
03. int *rightP = right.getP();
04. size = right.getSize();
05. p = new int[size];
06. memcpy(p, rightP, size*sizeof(int));
07. return *this;
08.}
A &operator = (A &right) //重载的等号
{
int *rightP = right.getP();
size = right.getSize();
p = new int[size];
memcpy(p, rightP, size*sizeof(int));
return *this;
} 正好符合我们的分析。
本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/xiaobo68688/archive/2010/06/12/5666960.aspx