Semantics of Construction,Destruction,and copy
考慮如下abstract base class聲明: class Abstract_base { public: virtual ~Abstract_base() = 0; virtual void interface() const = 0; virtual const char* mumble() const { return _mumble; } protected: char* _mumble; }; 雖然這個class被設計爲一個抽象的base class(其中有純虛函數,不可能擁有實體),但它仍然需要一個明確的構造函數以初始化其data member _mumble。
純虛擬函數的存在
我們可以靜態調用一個純虛函數(如Abstract_base::interface())。
關於純虛析構函數,因爲每一個派生類的析構函數會被編譯器擴展,以靜態的方式調用每一個虛基類以及上一層基類的析構函數,因此只要缺乏任何一個基類析構函數的定義,就會導致鏈接失敗。一個比較好的方案是,不要將虛析構函數聲明爲純虛。
虛擬規格的存在
如果將Abstract_base::mumble()設計爲一個虛函數,那將是一個糟糕的設計。因爲它幾乎不會被後繼派生類改寫。一般而言,把所有的成員函數聲明爲虛函數,然後由編譯器的優化操作把非必要的virtual invocation去除,並不是好的設計觀念。
虛擬規則中const的存在
決定一個virtual function是否需要const,是一件瑣屑的事情。當你面對一個abstract base class時,卻不容易做決定,聲明一個函數爲const,然後才發現其派生類必須修改一個data member。簡單的做法是,不用const就是了。
重新考慮class的聲明
class Abstract_base { public: virtual ~Abstract_base(); virtual void interface() const = 0; const char* mumble() const { return _mumble; } protected: Abstract_base(char *pc = 0);//新增一個帶唯一參數的構造函數 char* _mumble; };
1、“無繼承”情況下的對象構造
對比無繼承情況下Point結構體和class(添加默認構造函數)trivial 類函數(構造,析構,拷貝)的情況,可複習此前的筆記即可。
2、繼承體系下的對象構造
(略)vptr初始化
如果在集成體系中每一個constructor內調用一個virtual函數,那麼每一次調用的都是什麼函數呢?C++規則告訴我們,在Point3d constructor中調用的size()(virtual函數),必須爲Point3d::size(),即由構造函數(析構函數)中的對象調用一個virtual function,其函數實體應該是在此class中有作用的那個。由於構造順序是:由根源末端(bottom up),由內而外,base class constructor執行時,derived實體還沒有構造出來。因此如果調用操作必須在constructor和destructor中直接調用,那麼將每一個調用操作以靜態方式決議之,千萬不要用到虛擬機制。另一種方法是在constructor和destructor內設置一個標誌,以標誌確定是否以靜態調用方式進行。
vptr在constructor中應該在何時被初始化,我們有三種選擇:
答案是2。
- 在任何操作之前
- 在base class constructors調用操作之後,但是在程序員的代碼或者是member initialization list中所列的members被初始化之前
- 在每一件事情發生之後
3、Object copy Semantics
當我們以一個class object指定給另一個class object時,我們有三種選擇:
- 什麼都不做,因此得以實施默認行爲
- 提供一個explicit copy assignment operator
- 明確的拒絕把一個class object指定給另一個class object
如果選擇第三點,那麼只要將copy assignment operator聲明爲private,並且不提供定義即可。(餘下部分內容前面筆記有記錄,主要是在什麼情況在位拷貝會無效,虛擬繼承部分不做記錄)專家建議:不要在任何virtual base class 中聲明數據
4、Semantics of Destruction
若class未定義destructor,那麼只有在class內的member object或者是base class擁有destructor的情況下,編譯器纔會合成出一個否則就算class擁有virtual function,也不會合成。爲了決定class是否需要程序層面的destructor或是constructor,想想一個class的生命在哪裏結束或開始,需要什麼操作才能保證對象的完整?這也是constructor和destructor什麼時候起作用的關鍵。例如:對象在使用前必須初始化爲某些特定值,這時候需要一個constructor,當我們明確的delete一個對象時,如果沒有理由先將其內容清除乾淨,也不需要歸還任何資源,那麼不一定需要一個destructor。