C++C++拷贝构造函数(深拷贝与浅拷贝)

 转自http://blog.csdn.net/lwbeyond/article/details/6202256/

一. 呀是拷贝构造函数

 

对普通档次的目标的话,它们之间的复制是挺简单的,例如:
int a=88;
int b=a; 

 

而类对象同平常对象不同,类对象内部结构一般比较复杂,存在各种成员变量。下面看一个接近对象拷贝的概括例子。 

 

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class CExample {
 5 private:
 6      int a;
 7 public:
 8       //构造函数
 9      CExample(int b)
10      { a = b;}
11 
12       //一般函数
13      void Show ()
14      {
15         cout<<a<<endl;
16       }
17 };
18 
19 int main()
20 {
21      CExample A(100);
22      CExample B = A; //注意这里的对象初始化要调用拷贝构造函数,而非赋值
23       B.Show ();
24      return 0;
25 }

 

运作程序,屏幕输出100。

 

从以上代码的运行结果好看来,系统为对象B分配了内存并做到了和对象A的复制过程。就象是对象而言,相同类别的近乎对象是由此拷贝构造函数来形成全套复制过程的。

 

下面举例说明拷贝构造函数的干活历程:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class CExample {
 5 private:
 6     int a;
 7 public:
 8     //构造函数
 9     CExample(int b)
10     { a = b;}
11     
12     //拷贝构造函数
13     CExample(const CExample& C)
14     {
15         a = C.a;
16     }
17 
18     //一般函数
19     void Show ()
20     {
21         cout<<a<<endl;
22     }
23 };
24 
25 int main()
26 {
27     CExample A(100);
28     CExample B = A; // CExample B(A); 也是一样的
29      B.Show ();
30     return 0;
31 } 

 

CExample(const CExample&
C)就是咱们于定义之正片构造函数。可见,拷贝构造函数是一致栽非常之构造函数,函数的名称必须跟相近名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变的。
例如:类X的正片构造函数的形式为X(X&
x)。

 

 

二. 拷贝构造函数的调用时机

当用一个曾经初始化过了之自定义类类型对象去初始化另一个初结构之对象的时候,拷贝构造函数就会为活动调用。也就是说,当类的对象要拷贝时,拷贝构造函数将会给调用。以下情况尚且见面调用拷贝构造函数:

 在C++中,下面三种植对象需要调用拷贝构造函数!


一个靶为价值传递的章程传入函数体

 1 class CExample 
 2 {
 3 private:
 4  int a;
 5 
 6 public:
 7  //构造函数
 8  CExample(int b)
 9  { 
10   a = b;
11   cout<<"creat: "<<a<<endl;
12  }
13 
14  //拷贝构造
15  CExample(const CExample& C)
16  {
17   a = C.a;
18   cout<<"copy"<<endl;
19  }
20  
21  //析构函数
22  ~CExample()
23  {
24   cout<< "delete: "<<a<<endl;
25  }
26 
27      void Show ()
28  {
29          cout<<a<<endl;
30      }
31 };
32 
33 //全局函数,传入的是对象
34 void g_Fun(CExample C)
35 {
36  cout<<"test"<<endl;
37 }
38 
39 int main()
40 {
41  CExample test(1);
42  //传入对象
43  g_Fun(test);
44 
45  return 0;
46 }

 

 

运行结果:

1 creat: 1
2 copy
3 test
4 delete: 1
5 delete: 1
6 
7 Process returned 0 (0x0)   execution time : 0.498 s
8 Press any key to continue.

 

 

调用g_Fun()时,会出以下几只根本步骤:
(1).test对象传入形参时,会事先会产生一个即变量,就被 C 吧。
(2).然后调用拷贝构造函数把test的值给C。 整个这点儿独步骤有点像:CExample
C(test);
(3).等g_Fun()执行完毕晚, 析构掉 C 对象。


一个靶为价传递的计从函数返回:

 

 1 class CExample 
 2 {
 3 private:
 4  int a;
 5 
 6 public:
 7  //构造函数
 8  CExample(int b)
 9  { 
10   a = b;
11  }
12 
13  //拷贝构造
14  CExample(const CExample& C)
15  {
16   a = C.a;
17   cout<<"copy"<<endl;
18  }
19 
20      void Show ()
21      {
22          cout<<a<<endl;
23      }
24 };
25 
26 //全局函数
27 CExample g_Fun()
28 {
29  CExample temp(0);
30  return temp;
31 }
32 
33 int main()
34 {
35  g_Fun();
36  return 0;
37 }

 

当g_Fun()函数执行到return时,会起以下几单重点步骤:
(1). 先会出一个现变量,就深受XXXX吧。
(2).
然后调用拷贝构造函数把temp的值给XXXX。整个这片个步骤有点像:CExample
XXXX(temp);

(3). 在函数执行及终极先析构temp局部变量。
(4). 等g_Fun()执行完后再次析构掉XXXX对象。

 
一个目标要经另外一个靶进行初始化:

 

1 CExample A(100);
2 CExample B = A; 
3 // CExample B(A); 

 

继少句子都见面调用拷贝构造函数。

 

 

 

三. 浅拷贝和深拷贝

 

1. 默认拷贝构造函数

   
很多上在咱们还非亮拷贝构造函数的情下,传递对象吃函数参数或者函数返回对象还能够十分好之进展,这是盖编译器会让我们自行出一个正片构造函数,这便是“默认拷贝构造函数”,这个构造函数很简单,仅仅使用“老对象”的数据成员的值对“新目标”的数成员相继进行赋值,它一般装有以下形式:

1 Rect::Rect(const Rect& r)
2 {
3     width = r.width;
4     height = r.height;
5 }

   
当然,以上代码不用我们编辑,编译器会为咱自动生成。但是要看这样即便可缓解对象的复制问题,那即便蹭了,让咱来设想以下一段代码:

 

 1 class Rect
 2 {
 3 public:
 4     Rect()      // 构造函数,计数器加1
 5     {
 6         count++;
 7     }
 8     ~Rect()     // 析构函数,计数器减1
 9     {
10         count--;
11     }
12     static int getCount()       // 返回计数器的值
13     {
14         return count;
15     }
16 private:
17     int width;
18     int height;
19     static int count;       // 一静态成员做为计数器
20 };
21 
22 int Rect::count = 0;        // 初始化计数器
23 
24 int main()
25 {
26     Rect rect1;
27     cout<<"The count of Rect: "<<Rect::getCount()<<endl;
28 
29     Rect rect2(rect1);   // 使用rect1复制rect2,此时应该有两个对象
30      cout<<"The count of Rect: "<<Rect::getCount()<<endl;
31 
32     return 0;
33 }

立马段代码对眼前的类,加入了一个静态成员,目的是开展计数。在主函数着,首先创建对象rect1,输出此时底靶子个数,然后利用rect1复制出目标rect2,再出口此时底靶子个数,按照理解,此时该有半点只目标在,但骨子里程序运行时,输出的还是1,反应发生仅来1单目标。此外,在销毁对象时,由于会调用销毁两个目标,类的析构函数会调用两浅,此时的计数器将改为负数。

说白了,就是拷贝构造函数没有处理静态数据成员。

出现这些题目最根本不怕在以复制对象时,计数器没有递增,我们重新编写拷贝构造函数,如下:

 

 1 class Rect
 2 {
 3 public:
 4     Rect()      // 构造函数,计数器加1
 5     {
 6         count++;
 7     }
 8     Rect(const Rect& r)   // 拷贝构造函数
 9     {
10         width = r.width;
11         height = r.height;
12         count++;          // 计数器加1
13     }
14     ~Rect()     // 析构函数,计数器减1
15     {
16         count--;
17     }
18     static int getCount()   // 返回计数器的值
19     {
20         return count;
21     }
22 private:
23     int width;
24     int height;
25     static int count;       // 一静态成员做为计数器
26 };

 

 

2. 浅拷贝

 

   
所谓浅拷贝,指的凡于目标复制时,只针对目标中的数据成员开展简要的赋值,默认拷贝构造函数执行之为是浅拷贝。大多情况下“浅拷贝”已经能充分好地劳作了,但是如果目标在了动态成员,那么浅拷贝就会见起问题了,让咱们着想如下一截代码:

 1 class Rect
 2 {
 3 public:
 4     Rect()      // 构造函数,p指向堆中分配的一空间
 5     {
 6         p = new int(100);
 7     }
 8     ~Rect()     // 析构函数,释放动态分配的空间
 9     {
10         if(p != NULL)
11         {
12             delete p;
13         }
14     }
15 private:
16     int width;
17     int height;
18     int *p;     // 一指针成员
19 };
20 
21 int main()
22 {
23     Rect rect1;
24     Rect rect2(rect1);   // 复制对象
25     return 0;
26 }

 

于运作定义rect1对象后,由于当构造函数中产生一个动态分配的讲话,因此实施后底内存情况大概如下:

 

 

   
在用rect1复制rect2常常,由于实施之是浅拷贝,只是以成员的价值进行赋值,这时 rect1.p=
rect2.p,也即这简单个指针指于了堆积里的以及一个上空,如下图所示:

 

本来,这不是咱所欲之结果,在销毁对象时,两只目标的析构函数将对同一个内存空间释放两差,这就是错出现的原委。我们要之莫是有限独p有同之价,而是简单单p指向的空间发出同样之价值,解决办法就是用“深拷贝”。

 

 

 

3. 深拷贝

 

  在“深拷贝”的气象下,对于目标中动态成员,就未能够单纯略地赋值了,而相应更动态分配空间,如上面的事例就是应遵照如下的方展开拍卖:

 1 class Rect
 2 {
 3 public:
 4     Rect()      // 构造函数,p指向堆中分配的一空间
 5     {
 6         p = new int(100);
 7     }
 8     Rect(const Rect& r)
 9     {
10         width = r.width;
11         height = r.height;
12         p = new int;    // 为新对象重新动态分配空间
13         *p = *(r.p);
14     }
15     ~Rect()     // 析构函数,释放动态分配的空间
16     {
17         if(p != NULL)
18         {
19             delete p;
20         }
21     }
22 private:
23     int width;
24     int height;
25     int *p;     // 一指针成员
26 };

这时,在形成目标的复制后,内存的一个盖情况如下:

 

 

这儿rect1的p和rect2的p各自指于同段子内存空间,但它对准的上空有所同等之情节,这就算是所谓的“深拷贝”。