1.虛基類的概念
在C++語言中,一個類不能被多次說明為一個派生類的直接基類,但可以不止一次地成為間接基類。這就導致了一些問題。為了方便 說明,先介紹多繼承的“類格”表示法。
派生類及其基類可用一有向無環圖(DAG)表示,其中的箭頭表示“由派生而來”。類的DAG常稱為一個“類格”。復雜類格畫出來通常更容易理解。例如:
例 5-19
class L
{ public:
int next;
…
};
class A : public L
{ };
class B : public L
{ };
class C : public A, public B
{ public :
void f()
{
next=0;
}
};
這時,next有兩個賦值語句next=0; 具有二義性,它是將A::next置為零,還是將B::next置為零,或者將兩者都置為0,需要在函數f()中被顯式的說明.
如果希望間接基類L與其派生類的關系是如下圖
當在多條繼承路徑上有一個公共的基類,在這些路徑中的某幾條路經匯合處,這個公共基類就會產生多個實例。
如果只想保存這個基類的一個實例,可以將這個公共基類說明為虛擬基類或稱虛基類。
它僅是簡單地將關鍵字virtual加到基類的描述上,例如改寫上述例子為例5-20
注意!!!
一個派生類的對象的地址可以直接賦給虛基類的指針,例如:
C obj;
L * ptr=&obj;
這時不需要強制類型轉換,並且,一個虛基類的引用可以引用一個派生類的對象,例如:
C obj2;
L &ref=obj2;
反之則不行,無論在強制類型轉換中指定什么路徑,一個虛基類的指針或引用不能轉換為派生類的指針或引用。例如:
C * P=(C*)(A*)ptr;
將產生編譯錯誤。
2. 虛基類對象的初始化
虛基類的初始化與多繼承的初始化在語法上是一樣的,但隱含的構造函數的調用次序有點差別。
虛基類構造函數的調用次序是這樣規定的:
1. 虛基類的構造函數在非虛基類之前調用。
2. 若同一層次中包含多個虛基類,虛基類構造函數按它們說明的次序調用。
3. 若虛基類由非虛基類派生,則遵守先調用基類構造函數,再調用派生類構造函數的規則。
例如 :
class X : public Y, virtual public Z
{ }
X one;
將產生如下調用次序:
Z()
Y()
X()
這里Z是X的虛基類,故先調用Z的構造函數,再調用Y的構造函數,最后才調用派生類X自己的構造函數。
# include "iostream.h"
class base
{
public:
base()
{cout<<"Base"<<endl;}
};
class base2
{
public:
base2()
{cout<<"Base2"<<endl;}
};
class level1 : public base2, virtual public base
{
public:
level1()
{cout<<"level1"<<endl;}
};
class level2 : public base2, virtual public base
{
public:
level2()
{cout<<"level2"<<endl;}
};
class toplevel : public level1, virtual public level2
{
public:
toplevel()
{cout<<"toplevel"<<endl;}
};
當建立對象view時,將產生如下調用次序:
level2()
level1()
toplevel()
而level2()要求:
base()
base2()
level2()
level1()要求
base2()
level1()
toplevel()要求
toplevel()
所以,構造函數的調用順序為:
base()
base2()
level2()
base2()
level1()
toplevel()
