派生類建構函式執行順序及虛基類的概念
派生類建構函式執行順序如下:
所有基類的建構函式,多個基類建構函式的執行順序取決於定義派生類時所指定的順序,與派生類建構函式中所定義的成員初始化列表的引數順序無關;
建構函式的顯示初值初始化,與初始化列表順序無關,而與類中成員定義順序有關。
如:
class D1 : public B1 ,public B2
{
public:
D1() : b2 (4),B1 (1),
b1(2) ,B2 (3)
{
}
B1 b1;
B2 b2;
};
D1 b;
很明顯D1中先執行B1、B2的建構函式然後是D1的建構函式。如果其中有虛基類,就需要先執行虛基類的建構函式。執行D1建構函式時,初始化成員初值,根據成員定義順序,可以看到,先初始化b1,再初始化b2.
1.虛基類的概念
在C++語言中,一個類不能被多次說明為一個派生類的直接基類,但可以不止一次地成為間接基類。這就導致了一些問題。為了方便 說明,先介紹多繼承的“類格”表示法。
派生類及其基類可用一有向無環圖(DAG)表示,其中的箭頭表示“由派生而來”。類的DAG常稱為一個“類格”。複雜類格畫出來通常更容易理解。例如:
例 5-19
class L
{ public:
int next;
…
};
class A : public L
{ };
class B : publicL
{ };
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()