我正在使用公開各種純虛擬接口的外部庫。我試圖用自己的實現來包裝它們,以便擴展它們的功能。我要進入的問題是ChildClass
實際上並不來自BaseClass
。我寫了一個小的應用程序,說明了這一點:覆蓋C++中的非虛擬繼承
///
/// External Library
///
class BaseClass {
public:
virtual ~BaseClass() { }
virtual int Foo(void) = 0;
};
class ChildClass
: public BaseClass {
public:
virtual int Bar(void) = 0;
};
///
/// Internal code
///
class BaseClassImpl
: public virtual BaseClass {
public:
virtual int Foo(void) {
return 5;
}
};
class ChildClassImpl
: public virtual ChildClass
, public virtual BaseClassImpl {
public:
virtual int Bar(void) {
return 12;
}
};
int main(int, char* []) {
ChildClass* impl = new ChildClassImpl;
printf("%d, %d\n", impl->Foo(), impl->Bar());
return 0;
}
,編譯器給我的輸出是這樣的:
1>main.cpp(41): error C2259: 'ChildClassImpl': cannot instantiate abstract class
1> main.cpp(41): note: due to following members:
1> main.cpp(41): note: 'int BaseClass::Foo(void)': is abstract
1> main.cpp(9): note: see declaration of 'BaseClass::Foo'
現在概念我理解這個問題。編譯器會看到來自兩個不同父母的ChildClassImpl
繼承自BaseClass
的「鑽石問題」。有兩個解決方案,我可以使用的工作:
- 讓
ChildClass
幾乎從BaseClass
(class ChildClass : public virtual BaseClass
)獲得。這將消除編譯器錯誤並將其變成警告(main.cpp(38): warning C4250: 'ChildClassImpl': inherits 'BaseClassImpl::BaseClassImpl::Foo' via dominance)
。 - 在
ChildClassImpl
範圍內執行我自己的Foo()
版本並停止從BaseClassImpl
派生。
我曾嘗試修改外部庫以添加虛擬關鍵字,並且工作得很好。不幸的是,這個庫真的是不可修改的,所以這不是一個選項。選項2的作品(這是我現在正在做的),但它最終在一些重複的代碼。我不想重新實現一個我已經覆蓋的函數。
我知道虛擬繼承告訴編譯器確保在繼承樹中只有一個BaseClass
副本,這就是它的工作原理。但是,我不明白在這種情況下這是否是一個限制。外部類是純虛擬的,應該只有一個vtable,每個函數只有一個版本。傳統的鑽石問題(編譯器選擇Child1::Foo
還是Child1::Foo
?)在這裏並不存在,因爲沒有重複的功能。
我知道在這種情況下,正確的答案是「你做錯了」,但我無法控制外部庫,我需要使用我的Impl
,就好像它們是原件一樣。有無論如何覆蓋編譯器,讓它看到Foo
已被覆蓋在我的父母?
爲什麼你使用虛擬繼承? –
正如R Sahu所說,由於您沒有提供關於您試圖解決的問題的詳細信息,因此很難提出替代方案;但一般來說,'ChildClassImpl'應該從'ChildClass'派生出來,並且'ChildClassImpl'中應該定義'BaseClass'方法的任何重寫。 –