這是一些真實代碼的簡化,以及當我沒有意識到其他人已經實現Foo並從中派生出來時發生的真實錯誤。是否可以使用虛擬繼承來防止意外創建鑽石?
#include <iostream>
struct Base {
virtual ~Base() { }
virtual void print() = 0;
};
struct OtherBase {
virtual ~OtherBase() { }
};
struct Foo : public Base { // better to use virtual inheritance?
virtual void print() { std::cout << "Foo" << std::endl; };
};
struct Bar : public Base { // better to use virtual inheritance?
virtual void print() { std::cout << "Bar" << std::endl; };
};
// The design is only supposed to implement Base once, but I
// accidentally created a diamond when I inherited from Bar also.
class Derived
: public OtherBase
, public Foo
, public Bar // oops.
{
};
int main() {
Derived d;
OtherBase *pO = &d;
// cross-casting
if (Base *pBase = dynamic_cast<Base *>(pO))
pBase->print();
else
std::cout << "fail" << std::endl;
}
編輯:拯救你不必運行此代碼...
- 如果運行原來的樣子,它打印「失敗」(不理想的,很難調試)。
- 如果刪除標記爲「oops」的行,則會打印「Foo」(所需的行爲)。
- 如果你離開「oops」並使兩個繼承是虛擬的,它將不會編譯(但至少你知道要修復的東西)。
- 如果刪除「oops」並使它們變爲虛擬,它將編譯並打印「Foo」(所需行爲)。
使用虛擬繼承,結果要麼是好的,要麼是編譯器錯誤。如果沒有虛擬繼承,結果可能是好的或無法解釋的,難以調試的運行時故障。
當我實現了酒吧,基本上是複製什麼美孚已經在做,就引起了動態轉換失敗,這意味着不好的東西在真正的代碼。
起初我很驚訝沒有編譯器錯誤。然後我意識到沒有虛擬繼承,這會觸發GCC中的'沒有唯一的最終覆蓋'錯誤。我故意選擇不使用虛擬繼承,因爲在這種設計中不應該有任何鑽石。
但是當我從Base派生出來的時候使用了虛擬繼承,那麼代碼本來就可以工作(沒有我的oops),並且在編譯時我會被警告鑽石,運行。
所以問題是 - 你認爲使用虛擬繼承來防止未來犯同樣的錯誤是可以接受的嗎?在這裏使用虛擬繼承沒有很好的技術原因(我可以看到),因爲設計中永遠不應該有鑽石。它只會在那裏執行設計約束。
」這裏沒有鑽石!「 - 好吧,如果我將繼承性變爲虛擬,那麼它在技術上只是鑽石。好點子。問題仍然存在:爲了防止我犯的那種錯誤,使得繼承是虛擬的嗎? 「沒有辦法將OtherBase *的對象轉換爲Base *類型 - 」 - 你在這個錯誤的,嘗試它。可以使用dynamic_cast在層次結構之間進行投射。 – Dan 2009-09-09 22:44:16
@丹。它只在從Base繼承的Foo/Bar中有一個時有效。這是因爲dynamic_cast <>是一個運行時間轉換(沒有一個編譯時間信息可用(只有運行時信息))。所以動態的情況是將Derived類型的對象轉換爲類型Base。這顯然是允許的。 PS術語「跨層次鑄造」具有誤導性,您不會在標準中的任何位置找到該術語。您只需使用運行時信息來降低(或升級)一個層次結構。 – 2009-09-09 22:50:16
如果必須爲基礎定義一次Derived。那麼你應該看看Boosts靜態斷言。我相信你可以找到一個方法來添加一個編譯時檢查。 – 2009-09-09 22:52:53