《深度探索C 物件模型》讀書筆記(5)

2021-04-09 09:35:23 字數 3397 閱讀 7913

***純虛函式***

在設計抽象基類時,需要注意以下幾點:

(1)不要將destructor宣告為pure virtual function;

如果將destructor宣告為pure virtual function,則設計者一定得定義它。因為每乙個derived class destructor會被編譯器加以擴充套件,以靜態呼叫得方式呼叫其「每乙個virtual base class」以及「上一層base class」的destructor。

(2)不要將那些函式定義內容並不與型別有關的函式設計為virtual function,因為其幾乎不會被後繼的derived class改寫。

(3)對於其derived class可能修改某乙個data member的函式,不應被宣告為const。

***「無繼承」情況下的物件構造***

先定義class point:

class

point 

...virtual

float

z();

protected

:float

_x,_y;

} ;

你可不能小看z()這個virtual function給class point帶來的巨大變化。virtual function的引入促使每乙個class point擁有乙個vtpr,這樣一來,編譯器在constructor中新增了對vptr進行初始化的**,而copy constructor和copy assignment operator也會對vptr進行設定,而不再是原先簡單的bitwise操作了。

請看以下的**:

point foobar()

...

將被內部轉化為:

point foobar(point 

&_result)

...

從以上**的轉化可以看出:一般而言,如果你的設計之中有很多函式都需要以傳值方式(by value)傳回乙個local class object,那麼提供乙個copy constructor就比較合理。

***繼承體系下的物件構造***

假設class point3d虛擬繼承於class point,但由於class point僅存在乙份實體,因而class point3d的constructor需要注意乙個問題。

請看下面的繼承關係圖:

class

point3d : 

virtual

public

point 

... ;

class

vertex : 

virtual

public

point 

... ;

class

vertex3d : 

public

point3d, 

public

vertex 

... ;

通常來說,class point3d和class vertex的constructor均需呼叫point的constructor,然而,當point3d和vertex同為vertex3d的subobject時,它們對point constructor的呼叫操作一定不可以發生,而是交由vertex3d來完成。

那麼如何做到這一點呢?其實只需在constructor中新增乙個引數就可以了。例如,class vertex3d在呼叫point3d和vertex的constructor之前,總是會把引數_most_derived設為false,於是就壓制了兩個constructors中對point constructor的呼叫操作。

//在virtual base class情況下的constructor擴充內容

vertex3d

*vertex3d::vertex3d(vertex3d 

*this

, bool

_most_derived, 

float

x, float

y, float

z)...

為了控制乙個class中有所作用的函式,編譯器只要簡單地控制住vptr的初始化和設定操作即可。

vptr初始化操作應該如何處理?實際情況是:應該在base class constructors(具體來說,是所有的virtual base classes和上一層的base classes)呼叫操作之後,但是在程式設計師**的碼或是「member initialization list中所列的members初始化操作」之前。為什麼是這樣呢?

如果每乙個constructor都一直等待到其base class constructors執行完畢之後才設定其物件的vptr,那麼每次它都能夠呼叫正確的virtual function實體。

constructor的執行演算法通常如下:

(1)在derived class constructor中,所有virtual base classes的constructor會被呼叫;

(2)在derived class constructor中,上一層base class的constructor會被呼叫;

(3)上述完成之後,物件的vptr(s)被初始化,指向相關的virtual table(s);

(4)如果class有member class object,而後者擁有constructor,那麼它們會以其宣告順序的相反順序被呼叫;

(5)使用者所定義的**。

下面是vptr必須被設定的兩種情況:

(1)當乙個完整的物件被構造起來時,如果我們宣告乙個point物件,point constructor必須設定其vptr;

(2)當乙個subobject constructor呼叫了乙個virtual function(無論是直接呼叫或間接呼叫)時。

在明確了哪些情況下vptr必須被設定,我們在宣告乙個pvertex時,各個vptr不再需要在每乙個base class constructor中被設定。解決之道是把constructor**為乙個完整的object實體和乙個subobject實體。在subobject實體中,vptr的設定可以忽略。

如果我們不對point**乙個copy assignment operator,而光是依賴預設的memberwise copy,編譯器通常不會產生出乙個實體,除非class不表現出bitwise語意。關於哪些情況class不表現出bitwise語意,請參見讀書筆記(2)。

由於在virtual base class的拷貝操作將造成subobject的多重拷貝,並且該問題至今難以解決。因此筆者的建議是:盡量不要允許乙個virtual base class的拷貝操作,甚至建議:不要在任何virtual base class中宣告資料。

***解構語意學***

如果class沒有定義destructor,那麼只有在class內含的member object(或是class自己的base class)擁有destructor的情況下,編譯器才會自動合成出乙個來。

其解構順序與建構順序正好相反。

《深度探索C 物件模型》讀書筆記(5)

純虛函式 在設計抽象基類時,需要注意以下幾點 1 不要將destructor宣告為pure virtual function 如果將destructor宣告為pure virtual function,則設計者一定得定義它。因為每乙個derived class destructor會被編譯器加以擴充...

《深度探索C 物件模型》讀書筆記(6)

物件的構造和解構 一般而言,我們會把object盡可能放置在使用它的那個程式區段附近,這樣做可以節省不必要的物件產生操作和銷毀操作。全域性物件 全域性物件的靜態初始化策略包括以下幾個步驟 1 為每乙個需要靜態初始化的物件產生乙個 sti 函式,內含必要的constructor呼叫操作或inline ...

讀書筆記 《深度探索c 物件模型》 (3)

第四章 function語意學 4.2 虛擬成員函式 a 單一繼承下的virtual function 乙個class只會有乙個virtual table 這個class自己定義的virtual function,它override了乙個可能存在的base class virtual functio...