1. 程式人生 > >派生類建構函式執行順序及虛基類的概念

派生類建構函式執行順序及虛基類的概念

派生類建構函式執行順序如下:

    所有基類的建構函式,多個基類建構函式的執行順序取決於定義派生類時所指定的順序,與派生類建構函式中所定義的成員初始化列表的引數順序無關;

建構函式的顯示初值初始化,與初始化列表順序無關,而與類中成員定義順序有關。

如:

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 : 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()