[英]Multiple inheritance, virtual methods collision and pointers from base classes
我有一個結果,我沒想到多重繼承, virtual
方法和指向基類的指針。
使用d.getStr()
,當d
是derived
實例時,將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_1
的getStr()
,因此通過普通指針調用base_1
的getStr
()會導致derived
的getStr
()被調用,從而調用base_2
getStr()
方法。
然而,這不是發生的事情。 using
關鍵字不是以這種方式轉發方法調用的別名。 using
關鍵字不會在derived
'd類中創建方法,因此類繼承不受影響,並且derived_1
的getStr()
不會在subclsas中被覆蓋。 這就是為什么調用derived_1
的getStr()
不會最終調用derived_2
的getStr()
。
這是因為派生類必須為getStr()
提供2個vtable條目,每個基類一個,因此它可以正確解析base_1::getStr()
和base_2::getStr()
。 using
指令不創建derived::getStr()
vtable條目或替換基類類,它只選擇將使用哪個基類條目。 當通過指向base_1
的指針時,編譯器只從derived
和base_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.