1. 程式人生 > >複製建構函式與拷貝建構函式

複製建構函式與拷貝建構函式

對一個簡單變數的初始化方法是用一個常量或變數初始化另一個變數,例如:
  int m = 80;
  int n = m;
  我們已經會用建構函式初始化物件,那麼我們能不能象簡單變數的初始化一樣,直接用一個物件來初始化另一個物件呢?答案是肯定的。我們以前面定義的Point類為例:
  Point pt1(15, 25);
  Point pt2 = pt1;
後一個語句也可以寫成:
  Point pt2( pt1);
它是用pt1初始化pt2,此時,pt2各個成員的值與pt1各個成員的值相同,也就是說,pt1各個成員的值被複制到pt2相應的成員當中。在這個初始化過程當中,實際上呼叫了一個複製建構函式。

當我們沒有顯式定義一個複製建構函式時,編譯器會隱式定義一個預設的複製建構函式,它是一個內聯的、公有的成員,它具有下面的原型形式:
  Point:: Point (const Point &);
可見,複製建構函式與建構函式的不同之處在於形參,前者的形參是Point物件的引用,其功能是將一個物件的每一個成員複製到另一個物件對應的成員當中。
  雖然沒有必要,我們也可以為Point類顯式定義一個複製建構函式:
  Point:: Point (const Point &pt)
  {
   xVal=pt. xVal;
   yVal=pt. yVal;
  } 
  如果一個類中有指標成員,使用預設的複製建構函式初始化物件就會出現問題。為了說明存在的問題,我們假定物件A與物件B是相同的類,有一個指標成員,指向物件C。當用物件B初始化物件A時,預設的複製建構函式將B中每一個成員的值複製到A的對應的成員當中,但並沒有複製物件C。也就是說,物件A和物件B中的指標成員均指向物件C,實際上,我們希望物件C也被複制,得到C的物件副本D。否則,當物件A和B銷燬時,會對物件C的記憶體區重複釋放,而導致錯誤。為了使物件C也被複制,就必須顯式定義複製建構函式。下面我們以string類為例說明,如何定義這個複製建構函式。

例10-11
class String
{
 public:
  String(); //建構函式
  String(const String &s); //複製建構函式
  ~String(); //解構函式

  // 介面函式
  void set(char const *data);
  char const *get(void);

 private:
  char *str; //資料成員ptr指向分配的字串
};

String ::String(const String &s)
{
 str = new char[strlen(s.str) + 1];
 strcpy(str, s.str);
}

我們也常用無名物件初始化另一個物件,例如:
  Point pt = Point(10, 20);
  類名直接呼叫建構函式就生成了一個無名物件,上式用左邊的無名物件初始化右邊的pt物件。
  建構函式被呼叫通常發生在以下三種情況,第一種情況就是我們上面看到的:用一個物件初始化另一個物件時;第二種情況是當物件作函式引數,實參傳給形參時;第三種情況是程式執行過程中建立其它臨時物件時。下面我們再舉一個例子,就第二種情況和第三種情況進行說明:
  Point foo(Point pt) 
  { 
   … 
   return pt;
  }
  void main()
  {
   Point pt1 = Point(10, 20);
   Point pt2;
   …
   pt2=foo(pt);
   …
  }
  在main函式中呼叫foo函式時,實參pt傳給形參pt,將實參pt複製給形參pt,要呼叫複製建構函式,當函式foo返回時,要建立一個pt的臨時物件,此時也要呼叫複製建構函式。

預設的複製建構函式
  在類的定義中,如果沒有顯式定義複製建構函式,C++編譯器會自動地定義一個預設的複製建構函式。下面是使用複製建構函式的一個例子:

例10-12
#include <iostream.h>
#include <string.h>
class withCC
{
 public:
 withCC(){}
 withCC(const withCC&)
 {
  cout<<"withCC(withCC&)"<<endl;
 }
};

class woCC
{
 enum{bsz = 100};
 char buf[bsz];
public:
 woCC(const char* msg = 0)
 {
  memset(buf, 0, bsz);
  if(msg) strncpy(buf, msg, bsz);
 }
 void print(const char* msg = 0)const
 {
  if(msg) cout<<msg<<":";
  cout<<buf<<endl;
 }
};

class composite
{
 withCC WITHCC;
 woCC WOCC;
public:
 composite() : WOCC("composite()"){}
 void print(const char* msg = 0)
 {
  WOCC.print(msg);
 }
};

void main()
{
 composite c;
 c.print("contents of c");
 cout<<"calling composite copy-constructor"<<endl;
 composite c2 = c;
 c2.print("contents of c2");
}

  類withCC有一個複製建構函式,類woCC和類composite都沒有顯式定義複製建構函式。如果在類中沒有顯式定義複製建構函式,則編譯器將自動地建立一個預設的建構函式。不過在這種情況下,這個建構函式什麼也不作。
  類composite既含有withCC類的成員物件又含有woCC類的成員物件,它使用無參的建構函式建立withCC類的物件WITHCC(注意內嵌的物件WOCC的初始化方法)。
  在main()函式中,語句:
  composite c2 = c;
通過物件C初始化物件c2,預設的複製建構函式被呼叫。
  最好的方法是建立自己的複製建構函式而不要指望編譯器建立,這樣就能保證程式在我們自己的控制之下。