(1):當在多條繼承路徑上有一個公共的基類,在這些路徑的某幾條匯合處,這個公共的基類就會產生多個實例(或多個副本),若只想保存這個基類的一個實例,可以將這個公共基類說明為虛基類、
class CBase { };
class ChildA1:virtual public CBase{ };
class ChildA2:virtual public CBase{ };
class ChildB:public ChildA1,ChildA2{ };
則在類ChildB的對象中,僅有類CBase的一個對象數據
(2):虛基類的初始化如果在虛基類中定義了帶參數的構造函數,而且沒有定義默認構造函數,則在其所有派生類(包括直接派生或間接派生的派生類)中,通過構造函數的初始化表對虛基類進行初始化。例如
class A//定義基類A
{
A(int i){ } //基類構造函數,有一個參數
};
class B :virtual public A //A作為B的虛基類
{
B(int n):A(n){ } //B類構造函數,在初始化表中對虛基類初始化
};
class C :virtual public A //A作為C的虛基類
{
C(int n):A(n){ }
//C類構造函數,在初始化表中對虛基類初始化
};
class D :public B,public C
//類D的構造函數,在初始化表中對所有基類初始化
{
D(int n):A(n),B(n),C(n){ }
};
注意:
在定義類D的構造函數時,與以往使用的方法有所不同。規定:
在最后的派生類中不僅要負責對其直接基類進行初始化,還要負責對虛基類初始化。C++編譯系統只執行最后的派生類對虛基類的構造函數的調用,而忽略虛基類的其他派生類(如類B和類C)
對虛基類的構造函數的調用,這就保證了虛基類的數據成員不會被多次初始化。
虛基類的特點:
(1):虛基類構造函數的參數必須由最新派生出來的類負責初始化(即使不是直接繼承).
(2)虛基類的構造函數先於非虛基類的構造函數執行。
下面看一段程序的輸出結果:
1 #include "stdafx.h" 2 #include<iostream> 3 using namespace std; 4 5 class CBase{ 6 protected: 7 int a; 8 public: 9 CBase(int na) 10 { 11 a = na; 12 cout << "CBase constructor!" << endl; 13 } 14 ~CBase() 15 { 16 cout << "CBase deconstructor!" << endl; 17 } 18 }; 19 20 class ChildA1: virtual public CBase 21 { 22 public: 23 ChildA1(int na):CBase(na) 24 { 25 cout << "ChildA1 constructor!" << endl; 26 } 27 ~ChildA1() 28 { 29 cout << "ChildA1 deconstructor!" << endl; 30 } 31 int GetA() 32 { 33 return a; 34 } 35 }; 36 37 class ChildA2 : virtual public CBase 38 { 39 public: 40 ChildA2(int na):CBase(na) 41 { 42 cout<< " ChildA2 constructor!" << endl; 43 } 44 ~ChildA2() 45 { 46 cout<< "ChildA2 deconstructor!" << endl; 47 } 48 int GetA() 49 { 50 return a; 51 } 52 }; 53 54 class ChildB:public ChildA1,public ChildA2 55 { 56 public: 57 ChildB(int a1,int a2,int a3):ChildA1(a1),ChildA2(a2),CBase(a3) 58 { 59 cout << "ChildB constructor!!" << endl; 60 } 61 ~ChildB() 62 { 63 cout << "ChildB deconstructor!" << endl; 64 } 65 }; 66 67 int main() 68 { 69 ChildB childb(100,200,300); 70 //得到從ChildA1繼承的值 71 cout<<" from ChildA1 : a = "<<childb.ChildA1::GetA(); 72 //得到從ChildA2繼承的值 73 cout<<" from ChildA2 : a = "<<childb.ChildA2::GetA()<<endl<<endl; 74 return 0; 75 }
程序輸出的結果:
從上例中可以看出來,在類ChildB的構造函數初始列表中,調用了間接基類CBase的構造函數,這對於非基類是非法的,但對於虛基類則是合法而且是必要的。
從輸出的結果可以看出來,其公共基類的構造函數只調用了一次,並且優先於非虛基類的構造函數調用,並且發現,子派生類的對象childb的成員變量的值只有一個,所以當公共基類CBase被聲明為虛基類,雖然它成為ChildA1和ChildA2的公共基類,但子派生類ChildB中也只有它的一個備份.