深入探索C++對象模型之五 --- 析構、構造、拷貝語意學

深入探索C++對象模型之五 — 析構、構造、拷貝語意學

一般而言,class的data member應該被初始化,並且只在constructor中或是在class的其它member functions中指定初值,其它任何操作都將破壞封裝性質,使class的維護和修改更加困難。

純虛函數

我們可以定義和調用一個pure virtual function:不過它只能被靜態的調用,不能經由虛擬機制調用。

而對於pure virtual destructor,class設計者必須定義它。因爲每一個derived class destructor都會被編譯器加以擴展,以靜態調用的方式調用其“每一個virtual base class”以及“上一層base class”的destructor。如果缺少任何一個base class destructor的定義,都會導致鏈接失敗。

因此最好不要把virtual destructor聲明爲pure。

無繼承情況下的對象構造

在C之中,全局性的定義被視爲一個“臨時性的定義”,因爲它沒有明確的初始化操作。一個“臨時性的定義”可以在程序中發生多次,那些實例會被鏈接器摺疊起來,只留下一個單獨實體,被放在data segment中的BSS段中。而C++不支持“臨時性定義”,C++中所有全局對象都被當作“初始化過的數據”來對待。

如果要對class中的所有成員都設定常量初值,那麼給予一個explicit initialization list會比較高效。

Explicit Initialization list 帶來三項缺點:
1. 只有當class members都是public時,此法纔有效。
2. 只能指定常量,因此它們在編譯時期就可以被評估求值。
3. 由於編譯器沒有自動施行,所以初始化行爲的失敗可能性會比較高一些。

繼承體系下的對象構造

編譯器會擴充每一個constructor,一般而言擴充操作如下:

  1. 在memeber initialization list中的data members初始化操作會被放進constructor的函數裏面,以member聲明順序爲順序。
  2. 如果有member沒有出現在member initialization list當中,而這個member有一個default constructor的話那麼該default constructor也必須被調用。
  3. 在那之前,如果class object有virtual table pointers,那麼必須設定vptr的初值以指向正確的virtual tables
  4. 在那之前,所有上一層的base class constructor必須被調用,以base class的聲明順序爲順序。
  5. 在那之前,所有的virtual base class constructors必須被調用,從左到右,從最深到最淺。

虛擬繼承

虛擬繼承中因爲“base class subobject”共享性之故,那麼在繼承體系中間層面的class一定不能調用virtual base class的constructor,取而代之的是,作爲最底層的class來負責完成“被共享之virtual base subobject”的構造。

那麼編譯器是如何實現上述行爲的呢?我們注意到,“virtual base calss constructor被調用”有着明確的定義,即只有當一個完整的class object被定義出來的時候,它纔會被調用;如果object只是某個完整object的subobject的話那麼它就不會被調用

vptr的初始化

在一個class的constructor中,經由構造中的對象來調用一個virtual function,其函數實體應該是在此class中有作用的那個。爲了能夠找到正確的virtual function, 必須在合適的時機來初始化vptr的內容。
通常constructor的執行算法如下:

  1. 在derived class constructor中,“所有virtual base classes”以及“上一層base class”的constructor會被調用
  2. 對象vptr會被初始化,指向相關的virtual tables
  3. 如果有member initialization list的話,將在constructor中擴展開來,這必須在vptr被設定之後,以免又一個virtual member function被調用。
  4. 最後執行程序員所提供的代碼

對象複製語意學

當以一個class object 指定另一個class object的時候,如果class沒有表現出bitwise copy的話,那麼編譯器就自動合成一個copy asssignment operator。

class在以下情況不會表現出bitwise語意:

  1. 當class內帶一個member object,而其class有一個copy assignment operator時。
  2. 當一個class的base class有一個copy assignment operator時。
  3. 當一個class生命了任何virtual functions(這種情況下我們不能直接拷貝右端的object的vptr,因爲後者可能是一個derived class object)
  4. 當class繼承自一個virtual base class時。

析構語意學

如果class沒有定義destructor,那麼只有在class內袋的member object或者自己的base class擁有destructor的情況下,編譯器纔會自動合成一個出來。

編譯器通過合成一個destructor,來保證調用base class destructor,如果class的設計者定義了一個destructor,那麼編譯器也會擴展它。擴展方式和constructor被擴展的方式順序相反:

  1. destructor函數本體先被執行,也就是說vptr會在程序員的代碼執行前被重設。
  2. 如果class擁有member class objects, 而後者擁有destructors, 那麼它們會以其聲明順序的相反順序被調用。
  3. 如果object內帶一個vptr,那麼首先重設相關的virtual table。
  4. 如果有任何直接的nonvirtual base classes擁有destructor,他們會以聲明順序的相反順序被調用。
  5. 如果有任何virtual base classes擁有destructor,而當前討論的這個class時最尾端(most-derived)的class,那麼它們會以原來構造順序的相反順序被調用。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章