0

我正在研究與多繼承相關的主題。我想出了下面的代碼,並可能不能完全弄清楚機制背後:多態如何工作涉及多重繼承?

struct root 
{ 
    virtual void vfunction(){ /* root version */ } 
}; 

struct mid1:public root 
{ 
    virtual void vfunction(){ /* mid1 version */ } 
}; 

struct mid2:public root 
{ 
    virtual void vfunction(){ /* mid2 version */ } 
}; 

struct inheritMulti:public mid1, public mid2 
{ 
    void ambiguityMethod(){ 
     vfunction(); // error: ambiguous 
    } 
    void method1(){ 
     mid1& t = *this; 
     t.vfunction(); 
    } 
    void method2(){ 
     mid2& t = *this; 
     t.vfunction(); 
    } 
}; 

ambiguityMethod是錯誤的,很明顯。但是,method1method2中的函數調用讓我感到困惑。它們也是虛函數調用,並且t實際上是inheritMulti類型。所以他們應該叫inheritMulti版本vfunction,但由於inheritMulti沒有自己的版本,我不知道會發生什麼。並且原來method1中的呼叫調用mid1版本,而method2中的呼叫調用mid2版本。它是未定義的東西,只發生在我的編譯器上?如果沒有,爲什麼它會這樣工作? vtable如何處理這種情況?謝謝!


感謝您的幫助。我已經搜索了關於自己的相關主題,所以是的,我知道「鑽石問題」。但我認爲我的問題與此不同。我主要關心的是method1method2中的「虛函數調用」行爲是否由標準定義好或未定義。在編譯器中,它的行爲與我上面提到的一樣,但是標準所承諾的行爲是什麼?如果它是明確的,爲什麼它分別稱爲mid1和'mid2'版本? (直觀的想法是調用inheritMulti版本,因爲t的類型實際上是inheritMulti)而且,大多數編譯器如何處理這種情況?奇怪的是,method1method2中的虛函數調用調用了不同的函數。再次感謝!

+1

你認爲應該在這裏發生什麼? – Creris

回答

1
void method1(){ 
    mid1& t = *this; 
    t.vfunction(); 
} 

在這裏,您正在調用哪一個不是在inheritMulti定義,因此它將搜索的vfunction最近的定義vfunction,這是目前在mid1。查看層次結構。

根 - > Mid1-> inheritMulti

根 - > Mid2-> inheritMulti

在相同的

`void method2(){ 
    mid2& t = *this; 
    t.vfunction(); 
} 

這裏的情況下也最近的定義被發現在MID2,因此輸出。這些調用並不含糊,因爲兩個結構都創建了自己的vfunction副本。

inheritMulti將有兩個名爲mid1mid2的子對象,並且這些子對象中的每一個都保留它們自己的vtable指針。雖然你可能會認爲,當你做

mid1& t1 = *this; 

mid2& t2 = *this;

T1和T2是相同的,但嘗試這樣做...

bool same = ((void*)t1) == ((void*)t2); // Result false! 

因爲編譯器生成背後的一些代碼來調整指向適當對象的指針。

+0

謝謝!但還有一個問題。如果涉及這樣的多重繼承,vtable是如何工作的? – Asu

+0

更新了我的答案 – Nishant

1

虛擬關鍵字不會在這裏播放。

當兩個類具有相同名稱的函數,並且第三個類從它們繼承時,您需要手動指定在您使用作用域操作符::調用該函數時您引用了哪個基類。

void ambiguityMethod(){ 
     mid1::vfunction(); 
    } 

void ambiguityMethod(){ 
     mid2::vfunction(); 
    } 

void ambiguityMethod(){ 
     root::vfunction(); 
    } 

與T:(警告:未來奇怪的語法!)
t.mid1::vfunction();

您也可以覆蓋此功能,並調用一些基類函數,因此僅指定一個函數離子是在調用:

void inheritMulti::vfunction() override { 
    return mid1::vfunction(); 
} 

調用vfunction時,編譯器會尋找具有相同簽名最近的功能,並會調用這個函數這反過來 - 將調用mid1功能

1

您的代碼描述「鑽石問題」多重繼承中的衆所周知的問題(對於vfunction)。對於method1method2一切正常,因爲您正在從this製作mid1mid2對象,並且從其名稱空間調用vfunction

+0

不是真的鑽石,也不是問題。如果有多個MI,甚至多個使用名稱空間,或者在多次導入時使用帶有「導入」的語言,則可能會產生歧義。 – curiousguy