2015-10-20 113 views
0

我正在使用公開各種純虛擬接口的外部庫。我試圖用自己的實現來包裝它們,以便擴展它們的功能。我要進入的問題是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的「鑽石問題」。有兩個解決方案,我可以使用的工作:

  1. ChildClass幾乎從BaseClassclass ChildClass : public virtual BaseClass)獲得。這將消除編譯器錯誤並將其變成警告(main.cpp(38): warning C4250: 'ChildClassImpl': inherits 'BaseClassImpl::BaseClassImpl::Foo' via dominance)
  2. ChildClassImpl範圍內執行我自己的Foo()版本並停止從BaseClassImpl派生。

我曾嘗試修改外部庫以添加虛擬關鍵字,並且工作得很好。不幸的是,這個庫真的是不可修改的,所以這不是一個選項。選項2的作品(這是我現在正在做的),但它最終在一些重複的代碼。我不想重新實現一個我已經覆蓋的函數。

我知道虛擬繼承告訴編譯器確保在繼承樹中只有一個BaseClass副本,這就是它的工作原理。但是,我不明白在這種情況下這是否是一個限制。外部類是純虛擬的,應該只有一個vtable,每個函數只有一個版本。傳統的鑽石問題(編譯器選擇Child1::Foo還是Child1::Foo?)在這裏並不存在,因爲沒有重複的功能。

我知道在這種情況下,正確的答案是「你做錯了」,但我無法控制外部庫,我需要使用我的Impl,就好像它們是原件一樣。有無論如何覆蓋編譯器,讓它看到Foo已被覆蓋在我的父母?

+0

爲什麼你使用虛擬繼承? –

+0

正如R Sahu所說,由於您沒有提供關於您試圖解決的問題的詳細信息,因此很難提出替代方案;但一般來說,'ChildClassImpl'應該從'ChildClass'派生出來,並且'ChildClassImpl'中應該定義'BaseClass'方法的任何重寫。 –

回答

0

添加到您的ChildClassImpl

virtual int foo(void) { 
    return BaseClassImpl::foo(); 
} 

這將重定向到BaseClassImpl對ChildClass的FOO(或其BaseClass的複印件)任何電話。

2

有無論如何覆蓋編譯器,讓它看到Foo已被覆蓋在我的父母?

對於您所具有的類層次結構,該語句在繼承層次結構的一個分支中不是真實的。

我懷疑你知道如何解決編譯器錯誤。以防萬一你不...

添加

virtual int Foo(void) { 
    return BaseClassImpl::Foo(); 
} 

INT ChildClassImpl解決您的編譯器的問題。

類名通常表示抽象。像BaseChild這樣的通用名稱不會提供您正在處理的抽象的線索。如果不瞭解這些抽象是什麼,我很難建議一個合理的類層次結構。