1. 程式人生 > >複製建構函式(copy constructor)

複製建構函式(copy constructor)

定義

      只有單個形參,該形參是對本類型別物件的引用(常用const修飾),這樣的建構函式成為複製建構函式。

使用方式:

(1)顯示使用----用一個同類型的物件初始化該物件時;

(2)隱式使用----將該型別的物件傳遞給函式或從函式返回該型別物件時。

三種類型的複製建構函式:

*bitwise copy constructor :逐位複製-----預設方式

*合成的 copy constructor :編譯器合成----執行逐個成員初始化(memberwise initialize),只完成必要的工作,

*自定義的copy constructor:由類設計者定義------因為有些類必須對複製物件加以控制,如資料成員是指標的情況或者資料成員表示在建構函式中分配的其他資源,或類在建立新物件時必須要做一些特定工作,這時候就需要定義自己的複製建構函式。

關於淺拷貝與深拷貝:

淺拷貝:shllow copy= bitwise copy-----逐位(位元組)拷貝(預設情況)

例子:

class Base{
public:
         int a;
         char ch;
         char* str;
}
Base b1;
b1.a=15;
b1.ch='c';
b1.str="string";
b2=b2;

現在看看b1和b2的記憶體佈局(不考慮對齊):


可以看到:b1.str和b2.str指向了同一記憶體空間,即當一方撤銷時,另一方將受到影響,故應極力避免這種情況-----深度拷貝

深度拷貝:deep copy = memberwise copy-----挨個成員拷貝

再看b1和b2的記憶體佈局:


此時b1.str和b2.str指向了不同的記憶體空間,但內容(string)還是一樣的。

由於編譯器預設是bitwise copy semantics

即如果類中沒有明確定義或被編譯器合成copy constructor(即memberwise copy),則預設使用bitwise copy形式。

注:以上編譯器合成copy constructor和bitwise copy constructor兩種情況,均不需要類設計的參與!

那麼什麼 情況下bitwise copy semantics會失效呢?

四種情況

(1)當class內含一個class member object ,而後者的class宣告有一個copy constructor 時----被class設計者明確宣告或被編譯器合成-------遞迴呼叫;

(2)當class繼承自一個base class,而後者存在一個copy constructor時-----被class設計者明確宣告或被編譯器合成-------遞迴呼叫;

(3)當class聲明瞭一個或多個virtual functions時-------因為此時要考慮類物件中的虛擬函式表指標vptr的值。

當在同層物件之間進行初始化時,bitwise copy已經夠用了(為簡化,此時不考慮含有指標成員的情況);

例子:

class ZooAnimal{
public:
      ZooAnimal();
      virtual ~ZooAnimal();
      
      virtual void animate();
       virtual void draw();
.....
};
class Bear : public ZooAnimal{
public:
      Bear();
      void animate();
      void draw();
      virtual void dance();
......
};
Bear yogi;
Bear winnie = yogi;  //bitwise copy

其記憶體佈局如下:

但是如果存在:當一個base class object以其derived class 的object內容做初始化操作時,此時若bitwise copy顯然會出錯,因為其vptr將指向不同的虛表,故此時編譯器需要合成copy constrctor,以保證vptr複製操作的安全。合成出來的base class object copy constructor會明確是定object的vptr指向base class object 的vitrual table,而不是直接從右手邊的class object 中將其vptr現值拷貝過來。

如:

void draw(const ZooAnimal& zoey )
{
     zoey.draw();
}
ZooAnimal franny = yogi;//發生切割(sliced)行為
draw(yogi); //呼叫Bear::draw();
draw(franny); //呼叫ZooAnimal::draw()

其記憶體佈局如下(此時由編譯器合成copy constructor):


(4)當class派生自一個繼承串鏈時,其中有一個或多個virtual base classes時。