簡體   English   中英

多重繼承,虛方法沖突和基類指針

[英]Multiple inheritance, virtual methods collision and pointers from base classes

我有一個結果,我沒想到多重繼承, virtual方法和指向基類的指針。


使用d.getStr() ,當dderived實例時,將base_2版本,正如我所期望的那樣。

使用p->getStr() ,當p是指向derived實例的指針(或指向指向derived實例的base_2指針)時,會調用base_2版本,正如我所期望的那樣。

但隨着p->getStr()p是一個指向base_1指向derived例如, base_1版本被稱為,我確信會被稱為base_2版本(感謝using ,事實上, getStr()virtual方法)。

以下是一個簡單的例子:

#include <iostream>

struct base_1
{
   virtual std::string getStr () const
    { return "string from base 1"; }
};

struct base_2
{
   virtual std::string getStr () const
    { return "string from base 2"; }
};

struct derived : public base_1, public base_2
{ 
   using base_2::getStr;
};


int main ()
{
   derived  d;

   derived *  dp  = &d;
   base_1 *   bp1 = &d;
   base_2 *   bp2 = &d;

   std::cout << "from derived:         " << d.getStr() << std::endl;
   std::cout << "from derived pointer: " << dp->getStr() << std::endl;
   std::cout << "from base_1 pointer:  " << bp1->getStr() << std::endl;
   std::cout << "from base_2 pointer:  " << bp2->getStr() << std::endl;
}

輸出如下

from derived:         string from base 2
from derived pointer: string from base 2
from base_1 pointer:  string from base 1
from base_2 pointer:  string from base 2

我知道,為了強制調用base_2版本,我可以添加derived以下方法

std::string getStr () const
 { return base_2::getStr(); }

但我的問題是:

1)為什么指向base_1 (指向派生實例)的指針忽略using指令並調用getStr()base_1版本?

2)有沒有辦法強加給base_2的版本getStr()derived實例使用由base_1指針,而無需再定義getStr()

---編輯---

謝謝你的回答。

我知道你在描述正在發生的事情,但我懷疑的是:語言(標准)是否描述了這方面的內容? 或者它是一個未定義的部分?

我的意思是:如果我刪除using指令,我從d.getStr()dp->getStr()得到編譯錯誤( error: request for member getStr is ambiguous dp->getStr() ,因為編譯器不知道哪個版本選擇getStr()

但是getStr()virtual方法。 所以(我確信)一個基指針應該使用它們的派生版本。 但我們有幾種相互碰撞的方法。

從語言(標准)的角度來看, base_1 (或base_2 )是被授權(或有義務)選擇忽略另一個的碰撞方法的兩個版本之一的指針?

也許我錯了,但在我看來,通過這種方式, virtual方法作為非virtual方法進行管理。

您希望以下列方式using關鍵字時:

struct derived : public base_1, public base_2
{ 
   using base_2::getStr;
};

這與以下相同:

struct derived : public base_1, public base_2
{
   void getStr()
   {
        base_2::getStr();
   }
};

在這種情況下,您期望的行為 - 當p是指向base_1的指針時,調用p->getStr() - 實際上最終會調用base_2::getStr() derived覆蓋了base_1getStr() ,因此通過普通指針調用base_1getStr ()會導致derivedgetStr ()被調用,從而調用base_2 getStr()方法。

然而,這不是發生的事情。 using關鍵字不是以這種方式轉發方法調用的別名。 using關鍵字不會在derived 'd類中創建方法,因此類繼承不受影響,並且derived_1getStr()不會在subclsas中被覆蓋。 這就是為什么調用derived_1getStr()不會最終調用derived_2getStr()

這是因為派生類必須為getStr()提供2個vtable條目,每個基類一個,因此它可以正確解析base_1::getStr()base_2::getStr() using指令不創建derived::getStr() vtable條目或替換基類類,它只選擇將使用哪個基類條目。 當通過指向base_1的指針時,編譯器只從derivedbase_1 “看到”虛函數的vtable條目,因此它將getStr()解析為base_1::getStr() 您在derived中添加顯式getStr()解決方案可能是最干凈的解決方案,但為了清楚起見,建議將其設置為虛擬以匹配基類。

1)似乎你的代碼正在完成它應該做的事情。 您指向base_1,因此您從base_1(或其任何基類)獲取函數。 該對象由base_1和base_2組成,此時未知,因為您指向基類,而不是派生類。

2)不,這根本不可能。 您必須確實在derived重載getStr()。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM