虛擬基底類別
因為類別可能間接基底類別衍生的類別不只一次,C++ 會提供用來最佳化的方式,例如基底類別的工作。 虛擬基底類別會提供方法,以節省空間,並避免使用多重繼承的類別階層架構中的混淆。
每個非虛擬的物件都包含一份基底類別中定義的資料成員。 此重複情形會浪費空間,並要求您指定您希望每當您存取它們的基底類別成員的複本。
做為虛擬基底指定基底類別時,也可以當做間接基底一次以上而不須複製其資料成員。 將它當做虛擬基底的所有基底類別共用其資料成員的單一複本。
當宣告虛擬的基底類別中, 虛擬關鍵字出現在衍生類別的基底的清單。
請考慮下圖說明模擬的午餐線類別階層架構。
模擬的午餐線圖表
在圖中, Queue是基底類別,兩個CashierQueue和LunchQueue。 不過,當這兩個類別結合到表單LunchCashierQueue,發生下列問題: 新的類別包含兩個的子物件型別的Queue、 一個從CashierQueue ,另一個則從LunchQueue。 下圖是概念式的記憶體配置 (實際的記憶體配置最佳化方式可能是)。
模擬的午餐線物件
請注意有兩個Queue在子物件LunchCashierQueue物件。 下列程式碼宣告Queue虛擬基底類別:
// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};
virtual關鍵字可保證只有一個子物件的複本Queue包含 (請參閱下圖)。
模擬的午餐線物件具有虛擬基底類別
一個類別有虛擬的元件,並指定型別的非虛擬的元件。 此動作會在下圖所示的條件。
虛擬和非虛擬的相同類別的元件
在圖中, CashierQueue和LunchQueue使用Queue的虛擬基底類別。 不過, TakeoutQueue指定Queue的基底類別,而非虛擬基底類別。 因此, LunchTakeoutCashierQueue有兩個的子物件型別的Queue: 繼承路徑,其中包含從LunchCashierQueue ,而另一個路徑,其中包含從TakeoutQueue。 下圖將說明這一點。
虛擬和非虛擬繼承的物件版面配置
注意事項 |
---|
虛擬繼承優點顯著的大小比使用非虛擬繼承。不過,也可能導致額外的處理負荷。 |
如果在衍生的類別會覆寫虛擬基底類別,繼承的虛擬函式,而且建構函式或解構函式使用變數的指標,虛擬基底類別衍生的基底類別呼叫的函式,編譯器可能會引入與虛擬基底類別的其他隱藏"vtordisp 」 欄位。 /Vd0 的編譯器選項會抑制新增隱藏的 vtordisp 建構函式/解構函式的替代成員。 /Vd1 的編譯器選項,預設值,可讓他們身在何處需要它們。 只有當您不確定所有的類別建構函式和解構函式呼叫虛擬函式實際上,請關閉 vtordisps。
/Vd 的編譯器選項會影響整個編譯模組。 使用 vtordisp pragma,以隱藏並再重新啟用 vtordisp 欄位類別的類別為基礎:
#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )