2012-09-12 124 views
0

我需要使用C++。 C + + 11會很有趣,但我更喜歡沒有。我有以下班級結構。繼承時避免覆蓋成員

class Wheel { /*...*/ }; 
class MtbWheel : public Wheel { /*...*/ }; 

class Bike { Wheel front_wheel; }; 
class Mountainbike : public Bike { MtbWheel front_wheel; }; 

現在,這完全有效:山地自行車覆蓋前輪,因此可以使用MtbWheel。但是,從技術上來說,我並不高興。

  • 我寧願被禁止覆蓋front_wheel,或者至少限制由做繼承類輪類覆蓋。
  • 而不是重寫front_wheel,我只想「添加」屬性。

編輯:有沒有虛擬功能的解決方案,但與模板呢?

+7

你不是在重寫''front_wheel'',你隱藏了''front_wheel''的名字。 – juanchopanza

+0

您不能覆蓋成員對象。 –

+3

「Softwaretechnically」必須是來自TeutoniC++的術語:-) –

回答

4

front_wheel成員Mountainbike類做覆蓋front_wheel,它隱藏它。 Mountainbike類實際上有兩個front_wheel成員,但Bike類中的成員隱藏在Mountainbike中聲明的成員中。這意味着如果Bike訪問front_wheel,它將訪問Wheel類型的對象,而當Mountainbike訪問front_wheel時,它將訪問MtbWheel類型的對象 - 但這兩個輪對象不知道彼此!

更好的OO設計將使front_wheel例如在Bike(或更好的智能指針)中的指針,並在Mountainbike的構造函數中對其進行初始化,以保存衍生自Wheel的類的對象,其最適合於Mountainbike。這樣,當訪問front_wheel時,當然會有一種方式或其他虛擬功能進入。

另一種解決方案,爲每史蒂夫·傑索普的建議在下面的意見,利用的模板,而不是使用多態,應該是:

class Wheel { /*...*/ }; 
class MtbWheel : public Wheel { /*...*/ }; 

template <typename WheelType> 
class Bike { WheelType front_wheel; }; 

class Mountainbike : public Bike<MtbWheel> { /* ... */ }; 

這樣一來,在front_wheel操作時沒有虛函數都參與其中。然而,有這樣一個解決方案需要考慮一些要點:

  • 對於使用自行車的每種不同WheelType,將創建單獨的代碼;如果你有許多不同的類型,這可能會導致代碼膨脹。
  • 如果您有不止一個來自Bike的類具有不同的WheelType參數,它們不會有具有相同的基類(請參閱Steve Jessop的評論和第1點),因此也無法以多態方式訪問。
  • 您無法強制將顯式接口作爲模板參數傳遞給Bike;只有隱式接口由Bike中使用的方法和成員定義。這應該沒問題,因爲編譯器仍然可以驗證該隱式接口。
+0

不錯,但有沒有使用模板而不是虛擬功能的方法? – Johannes

+1

@Johannes我不確定我是否明白你想要用模板做什麼?順便提一下,我提出的設計不使用任何虛擬方法。 – codeling

+1

@Johannes:有。這就是所謂的CRTP,它有點煩瑣,但那是你的搜索術語。您還可以使用不具備CRTP所有功能的模板做一個更簡單的解決方案,但通過將'WheelType'作爲'Bike'的模板參數來避免虛擬功能。然後'MountainBike'繼承自'自行車'。無論哪種方式,請注意''MountainBike'正在使用'PennyFarthing'將使用的一個* * *基類,它可能會或可能不適合您的設計。 –

1

你並沒有壓倒一切。派生類中仍有Wheel front_wheel;。你可以通過m.Bike::front_wheel或類似的東西訪問它。如果您希望派生類提供自己的front_wheel實例化,那麼您只需要在基類中提供一個virtual訪問器,並讓派生類自己創建它們。

1

變量front_wheel未被覆蓋 - 它只是隱藏。成員變量Wheel front_wheel仍處於Mountainbike類中。所以實際上在Mountainbike中有兩個變量front_wheel。但要訪問Mountainbike中的隱藏變量,您需要明確說明:Bike::front_wheel

做你想要什麼更好的辦法是不帶數據創建一個接口類:

class Bike { 
public: 
    virtual Wheel const &getFronWheel() const = 0; 
    virtual ~Bike() {} 
}; 

,然後得出與任何特定摩托:

class RegularBike: public Bike { 
public: 
    virtual Wheel const &getFronWheel() const { return wheel; } 
private: 
    Wheel wheel; 
} 

class MtbBike: public Bike { 
public: 
    virtual MtbWheel const &getFronWheel() const { return wheel; } 
private: 
    MtbWheel wheel; 
} 

編輯:沒有使用虛擬,但模板,而不是:

template<typename WheelType> 
class Bike { 
public: 
    /* Common methods for any bike...*/ 
protected: // or private 
    WheelType wheel; 
}; 

然後你願意,你可以延長自行車:

class RegularBike: public Bike<Wheel> { 
    /* Special methods for regular bike...*/ 
}; 

class MtbBike: public Bike<MtbWheel> { 
    /* Special methods for Mtb bike...*/ 
}; 
+0

非常感謝提供一個例子,但我正在尋找一個沒有任何虛函數的解決方案。我應該有說... :( – Johannes

+0

@Johannes:虛擬功能有什麼問題?無論如何,我已經添加了一個沒有多態的解決方案。 –

+0

謝謝,正是我一直在尋找的。我嘗試避免虛擬功能,因爲運行時間。我的應用程序需要有一個良好的運行時。 – Johannes

0

您可以創建IWheel代替類輪的接口。

在MtbWheel類中創建OverRide方法。因此,任何想要重寫此方法的人都可以重寫此方法,否則使用我們在MtbWheel類中實現的默認方法。通過使用這個,你可以添加它的屬性。

0

解決的辦法是不這樣做。寫一個派生類需要仔細研究基類,然後仔細設計。魔術餅乾不能代替知識和思想。而且,是的,我已經分享了這種錯誤,並且因爲粗心大意而自責。

+0

不是做什麼的?使用模板參數?你在想什麼錯誤? – Johannes

+0

@Johannes - 不小心將名字隱藏在基類中。 –