拷贝构造函数
拷贝构造函数详解C++
分类: C/C++ 2011-02-23 13:3932890人阅读评论(76)收藏举报
c++funclass编译器deletec
一. 什么是拷贝构造函数
首先对于普通类型的对象来说,它们之间的复制是很简单的,例如:
[c-sharp]view plaincopy
, int a = 100;
, int b = a;
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。
下面看一个类对象拷贝的简单例子。 [c-sharp]view plaincopy
, #include
, using namespace std;
,
, class CExample {
, private:
, int a;
? public:
,, //构造函数
,, CExample(int b)
,, { a = b;}
,,
,, //一般函数
,, void Show ()
,, {
,, cout<
,? using namespace std;
,,
,, class CExample {
,, private:
,, int a;
,, public:
,, //构造函数
,, CExample(int b)
,, { a = b;}
,,
,? //拷贝构造函数
,, CExample(const CExample& C)
,, {
,, a = C.a;
,, }
,,
,, //一般函数
,, void Show ()
,, {
,, cout<题,那就错了,让我们来考虑以下一段代码:[c-sharp]view plaincopy
,,, class Rect
,,, {
,,, public:
,,, Rect() // 构造函数,计数器加1 ,,, {
,,, count++;
,,, }
,,, ~Rect() // 析构函数,计数器减1 ,,, {
,,? count--;
,,, }
,,, static int getCount() // 返回计数器的值 ,,, {
,,, return count;
,,, }
,,, private:
,,, int width;
,,, int height;
,,, static int count; // 一静态成员做为计数器 ,,? };
,,,
,,, int Rect::count = 0; // 初始化计数器 ,,,
,,, int main()
,,, {
,,, Rect rect1;
,,, cout<<"The count of Rect: "<
内容 没有进行正确的操作。我们来分析一下:
在运行定义rect1对象后,由于在构造函数中有一个动态分配的语句,因此执行后的内存情况大致如下:
在使用rect1复制rect2时,由于执行的是浅拷贝,只是将成员的值进行赋值,这时 rect1.p= rect2.p,也即这两个指针指向了堆里的同一个空间,如下图所示:
当然,这不是我们所期望的结果,在销毁对象时,两个对象的析构函数将对同一个内存空间释放两次,这就是错误出现的原因。我们需要的不是两个p有相同的值,而是两个p指向的空间有相同的值,解决
就是使用“深拷贝”。 3. 深拷贝
在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间,如上面的例子就应该按照如下的方式进行处理: [c-sharp]view plaincopy
,,, class Rect
,,, {
,,, public:
,,, Rect() // 构造函数,p指向堆中分配的一空间
,,? {
,,, p = new int(100);
,,, }
,,, Rect(const Rect& r)
,,, {
,,, width = r.width;
,,, height = r.height;
,,, p = new int; // 为新对象重新动态分配空间
,,, *p = *(r.p);
,,, }
,,? ~Rect() // 析构函数,释放动态分配的空间
,,, {
,,, if(p != NULL)
,,, {
,,, delete p;
,,, }
,,, }
,,, private:
,,, int width;
,,, int height;
,,? int *p; // 一指针成员
,,, };
此时,在完成对象的复制后,内存的一个大致情况如下:
此时rect1的p和rect2的p各自指向一段内存空间,但它们指向的空间具有相
同的内容,这就是所谓的“深拷贝”。
3. 防止默认拷贝发生
通过对对象复制的分析,我们发现对象的复制大多在进行“值传递”时发
生,这里有一个小技巧可以防止按值传递——声明一个私有拷贝构造函数。甚至
不必去定义这个拷贝构造函数,这样因为拷贝构造函数是私有的,如果用户试图
按值传递或函数返回该类对象,将得到一个编译错误,从而可以避免按值传递或
返回对象。
[c-sharp]view plaincopy
,,, // 防止按值传递
,,, class CExample
,,, {
,,, private:
,,, int a;
,,,
,,, public:
,,, //构造函数
,,? CExample(int b)
,,, {
,,, a = b;
,,, cout<<"creat: "<