2015-07-20 57 views
5

手頭的問題很難描述,所以代碼被放在前面以獲得更好的清晰度。多繼承虛擬呼叫模糊

struct Base 
{ 
    int b; 
    virtual void foo(){cout << b << endl;} 
    Base(int x) : b(x){} 
}; 

struct Derived1 : Base //not virtual 
{ 
    virtual void foo(){/*Derived2's code*/} 
    Derived1() : Base(1){} 
}; 

struct Derived2 : Base //not virtual 
{ 
    virtual void foo(){/*Derived2's code*/} 
    Derived2() : Base(2){} 
}; 

struct MultiInheritance : Derived1, Derived2 
{ 
    void bar1() 
    { 
     //needs to access Derived1's Base foo() 
    } 
    void bar2() 
    { 
     //needs to access Derived2's Base foo() 
    } 
}; 

假設在有些怪異離奇的情況下,我想一個基類MultiInheritance有兩個基類Derived1Derived2有一個共同的非虛基類Base

有兩個BaseMultiInheritance,我怎麼指定我想在MultiInheritance訪問哪些Base類?

上面的代碼似乎很好地通過投射多次,但我不知道這是否定義的行爲。如果是這樣,編譯器如何實現它以滿足多態性的需求?一方面virtual調用應該都產生相同的virtual函數表,但另一方面,如果它不會輸出不同的答案。

編輯

我想強調的是,Base類必須是非虛擬

EDIT2

深深的歉意,我嚴重歪曲了自己。上面的代碼更新更好地反映了我原來的問題。

+0

哪一個你認爲的正確* *'Base'?或者是你的問題如何使它只有一個'Base'? – Barry

+0

你的分析是正確的,編譯器會建立一個反映兩個基類存在的正確的vtable。爲了避免這種情況,您需要虛擬繼承,並且vtable結構將變得更加複雜。 –

+1

您的示例中沒有任何歧義,它的行爲與書中的完全相同。 「虛擬表」是一個實現細節。沒有人在任何地方說每個實現必須每個類有一個vtbl,或者每個vtable中的每個函數簽名都必須有一個條目。實際上,對於使用vtables的任何實現來說,上述至少一個是不正確的。 –

回答

0

以下是一個說明性的例子。

#include <iostream> 
using namespace std; 

template <typename Res, typename Arg> 
Res& as(Arg& arg) 
{ 
    return arg; 
} 

struct Base 
{ 
    virtual void foo() = 0; 
}; 

struct Derived1 : Base {}; 
struct Derived2 : Base {}; 

struct MoreDerived1 : Derived1 
{ 
    void foo() { cout << "Derived1\n"; } 
}; 

struct MoreDerived2 : Derived2 
{ 
    void foo() { cout << "Derived2\n"; } 
}; 

struct MultiInheritance : MoreDerived1, MoreDerived2 
{ 
    void bar1() { as<Derived1>(*this).foo(); } 
    void bar2() { as<Derived2>(*this).foo(); } 
}; 

int main() 
{ 
    MultiInheritance m; 
    m.bar1(); 
    m.bar2(); 
} 

這個例子說明:

  1. 你並不需要指定其基礎則需要明確使用一個完整的繼承路徑,這就夠了,深入到有一個明確的基地的子對象子對象
  2. 虛擬功能機制在此處起作用。如果您嘗試致電Derived1::foo(),則不起作用。

as輔助函數只是一個語法糖,你也可以同樣說

Derived1& d = *this; 
d.foo(); 
2

這被稱爲鑽石問題。

http://www.cprogramming.com/tutorial/virtual_inheritance.html

如果你想保底非虛,讓你現在正在尋找的行爲,你可以做到這裏面MultipleInheritance以下方式以確保您從正確的基類

調用 foo()功能
struct MultiInheritance : Derived1, Derived2 
{ 
    void bar1() 
    { 
     Derived1::foo(); 
    } 
    void bar2() 
    { 
     Derived2::foo(); 
    } 
}; 
0

有兩個基地MultiInheritance,我怎麼指定基地 類我希望在MultiInheritance訪問?

你必須在基礎對象,你在呼喚在

void MultiInheritance::bar1(){ 
    foo(); 
} 

的方式來解決,這是告訴編譯器到哪裏尋找FOO歧義。

void MultiInheritance::bar1(){ 
    Derived1::foo(); // The same foo() as in your question. 
} 

這就是你的

void MultiInheritance::bar1() 
{ 
    Derived1& d = *this; 

這是由§10.2.12標準中所述完成。這是明確的。至於你的連鎖

void MultiInheritance::bar1() 
{ 
    Derived1& d = *this; 
    Base& b = *this; 

在同一段落。

不幸的是範圍解析運營商不能讓你從MultiInhteritance直接跳轉到基地

MultiInheritance::foo(){ 
    Derived1::Base::foo(); 

被描述嵌套Base

要到把foo()屬於基地使用範圍解析語法MultiInheritance,以及在Derived1和Derived2的。

Derived1()::foo(){ 
    Base::foo; 

如果這不合適,那麼您建議的選項是剩餘選項。

如果是這樣,編譯器如何實現多態的需求 ?一方面虛擬通話應該都會導致 同樣的虛擬功能表,但另一方面,如果它不會, 輸出不同的答案。

編譯器實現因編譯器而異,正如一位評論者所說:虛擬函數使用vtables是一個實現細節。如果一個實現使用虛擬功能的虛擬表,實施需要考慮到這種情況。