2009-07-28 56 views
8

爲什麼建議不要在虛擬基類中包含數據成員?虛擬基類數據成員

函數成員怎麼樣? 如果我有一個對所有派生類都通用的任務,那麼虛擬基類可以執行任務,還是派生自兩個分類的虛擬基類 - 從虛擬接口和執行任務的普通基礎繼承?

謝謝。

+6

你能提供一個鏈接或引用的建議嗎?我不能在沒有數據成員的情況下標記一個基類是非常重要的,因爲虛擬繼承最常見的目的是防止基類成員的重複。例如在標準庫中,ios_base有數據成員,並且是istream和ostream的虛擬基類(通過ios)。所以我幾乎(但不是完全相反)說 - 如果你要有一個虛擬基類,那麼它應該有數據成員,否則就是非虛擬地繼承。 – 2009-07-28 10:33:27

回答

11

作爲練習,你應該只使用虛擬繼承,因爲它們通常與多重繼承使用,以確保只有一個版本的類出現在派生類中定義的接口。而純粹的接口是多重繼承最安全的形式。當然,如果你知道你在做什麼,你可以使用多重繼承,但如果你不小心的話,會導致代碼變得脆弱。

虛擬繼承的最大缺點是它們的構造函數帶參數。如果必須將參數傳遞給虛擬基類的構造函數,則必須強制所有派生類顯式調用構造函數(它們不能依賴調用構造函數的基類)。

我明白您的明確建議的唯一原因是您的虛擬基類中的數據可能需要構造函數參數。

編輯 我在Martin的評論之後做了一些家庭作業,感謝Marin。第一行是不完全正確:

這樣的做法只應使用 虛擬繼承來定義 接口,因爲它們通常使用 多重繼承,以確保 該類只有一個版本是 存在在派生類中。

如果基類是一個純粹的接口(除了稍微不同的編譯器錯誤,在vc8中,如果沒有實現所有方法),虛擬繼承沒有區別。它不僅使一個真正的區別,如果基類中有數據,在這種情況下,你最終用金剛石,而不是U形

Non virtual virtual 
    A  A   A 
    |  |  / \ 
    B  C  B  C 
    \ /  \ /
    D    D 

在虛擬的情況下,B和C分享A.

的同一副本

但是我仍然同意其他所有關於純接口是多繼承最安全的形式,即使它們不需要虛繼承。而構造參數和虛擬繼承是一個痛苦的事實。

+0

+1這也總結了關於這個C++ FAQ精簡版http://www.new-brunswick.net/workshop/c++/faq/multiple-inheritance.html#faq-25.8 – 2009-07-28 12:20:23

+0

我不認爲任何的是真的。 – 2009-07-28 13:55:17

+0

@Martin:不幸的是,它是... – 2009-07-28 19:09:58

0

一)成員應該是私有的 - 所以你可以在派生類中使用它們(所以你必須添加getter和setter方法,它炸燬你的界面)

b得到的問題)不申報的東西你目前不使用 - 只在類聲明變量,接入/使用它們

C)虛基類應該只包含接口/虛擬方法,僅此而已

我希望幫助一點點,即使我的理由並不完美和完整:)

的Ciao, 克里斯

1

我從來沒有見過這樣的建議。

類是一組密切相關的功能和數據。基類的目的是擁有一組可供派生類重用的函數和數據。

我認爲限制自己沒有在基類的數據成員或非純虛函數會降低代碼重用,從而導致不可靠的代碼在長期運行的量。

2

的核心建議是在虛擬底座的默認構造函數。如果不這樣做,那麼每最派生類(即所有子類)必須顯式調用虛基類構造函數,並導致憤怒的同事敲你的辦公室的門...

class VirtualBase { 
public: 
    explicit VirtualBase(int i) : m_i(i) {} 
    virtual ~VirtualBase() {} 

private: 
    int m_i; 
}; 

class Derived : public virtual VirtualBase { 
public: 
    Derived() : VirtualBase(0) {} // ok, this is to be expected 
}; 

class DerivedDerived : public Derived { // no VirtualBase visible 
public: 
    DerivedDerived() : Derived() {} // ok? no: error: need to explicitly 
            // call VirtualBase::VirtualBase!! 
    DerivedDerived() : VirtualBase(0), Derived() {} // ok 
}; 
0

所有用於模擬C++中的接口的基礎類不應包含數據成員 - 因爲它們描述的是接口,而實例狀態是實現細節。

除此之外,包含虛擬函數的基類可能具有數據成員。但通常的規則適用於:

  • 使它們儘可能隱藏,如果需要保留類不變量,則使用getter和setter。 (這裏存在一個漏洞:如果沒有與成員關聯的不變量,即它可能在任何給定時間假定任何可能的值,那麼可以公開它,但是這否定了派生類添加不變量。繼承設計:你的班級的合同應該定義派生班級的職責和可能性,它需要/可以改寫什麼目的。