2012-06-28 50 views
1

我有以下代碼示例:多重繼承,選擇虛擬函數調用

class A 
{ 
public: 
    A(int a):AA(a) {}; 

    int AA; 

    virtual int Test() 
    { 
     return AA; 
    }; 
}; 

class B 
{ 
public: 
    B(int b):BB(b) {}; 

    int BB; 

    virtual int Test() 
    { 
     return BB; 
    }; 
}; 

class C:public A, public B 
{ 
public: 
    C(int a, int b, int c) :A(a),B(b),CC(c) {}; 

    int CC; 
}; 

int main() 
{ 
    A *a = new C(1,2,3); 
    B *b = new C(1,2,3); 
    C *c = new C(1,2,3); 


    int x = a->Test() ; // this is 1 
    int y = b->Test() ; // this is 2 
// int z = c->Test() ; // this does not compile 

    return 0; 
} 

我期待呼叫A->試驗()和B->試驗()是不明確的太如對象a是C並因此繼承自A和B,兩者具有相同的Test()函數。但是,它們都調用與delcared類型相對應的實現,而不是實際對象的類型。

任何人都可以解釋爲什麼這些調用不含糊? C++總是以這種方式表現嗎?

回答

2

事實上,一個C實例既是滿甲實例和全乙實例(所以持有的方法副本&乙方法)

由於是A *,編譯器將使用A由於b是B *,編譯器將使用C虛擬表副本,它位於C 實例內

由於編譯器現在不會使用C *,因此編譯器將不會使用C *您要調用A或B的T​​est()方法(因爲C類同時擁有A :: Test & B ::測試符號)

如果實現一個C :: test()方法,那麼它會被稱爲既代替A ::測試()& B ::測試()方法,因爲是虛擬的對於A & B.

+0

所以它的設計行爲是在這種情況下,編譯器總是從聲明的類型中調用函數(假設它不會被一個後代覆蓋),也就是說我可以依賴它來始終發生在編譯器上嗎? – Stefan

+1

請參見http://en.wikipedia.org/wiki/Virtual_method_table#Multiple_inheritance_and_thunks,它表示多個虛擬表副本是多重繼承的「最常見」實現。我想如果你不使用異國情調的編譯器,這可能是一個可靠的行爲 – dweeves

+0

這是完美的,謝謝。 – Stefan

0

因爲A不知道C是否存在。

考慮一個稍微不同的情景:

foo.h中

class A { public: virtual void Test() {} }; 

void myFunction(A *a); 

Foo.cpp中

#include "foo.h" 

void myFunction(A *a) { 
    a->Test(); 
} 

你希望它可以編譯,我猜?但是如果我後來獨立繼承自A,那麼應該影響這些代碼是否編譯?

+0

我知道我正處於我看不到樹木的階段,但我沒有看到問題。通常我會希望你的代碼能夠被編譯。所有與我的大腦混淆的事實是,Test是虛擬的,調用應該取決於對象的實際類型,不需要聲明的類型,但是在實際的對象中調用是不明確的。顯然,它不是因爲它的工作,但我的大腦爲什麼阻止! – Stefan

+0

對不起,我只發佈了部分回覆(詛咒的iPhone界面)。如果你從A繼承了一個指向後代的指針(其中test是虛擬的),我期望它調用後代的Test版本,但是如果它也從其他地方繼承了Test,編譯器如何知道該調用哪個? – Stefan