2011-05-19 88 views
19

我知道虛擬方法允許派生類覆蓋從基類繼承的方法。什麼時候使用虛擬方法適合/不適合?不知道一個班級是否會被分類。一切都應該虛擬,只是「以防萬一?」或者會造成重大的開銷?何時適合使用虛擬方法?

回答

6

當你設計一個類時,你應該對它是否代表一個接口有個很好的主意(在這種情況下,你需要標記適當的overrideable方法和析構函數虛擬),或者它的目的是按原樣使用,與其他物體組成。

換句話說,你的意圖應該是你的指導。讓所有的東西都變成虛擬的往往是矯枉過正,有時會誤導哪些方法是爲了支持運行時多態。

-1

看一看Design Patterns。如果你的代碼/設計是這些或類似的一個去使用虛擬功能。否則,請嘗試this

+2

-1:想要實現GoF設計模式和需要虛擬功能之間沒有因果關係。 – 2011-05-19 13:31:25

3

如果您的代碼遵循特定的設計模式,那麼您的選擇應該反映DP自己的原則。例如,如果您編碼爲Decorator pattern,則應該是虛擬的功能是屬於Component界面的功能。除非我看到一個層次結構試圖從你的代碼中出現,否則我想遵循一個演化的方法,即我沒有虛擬方法。

0

我的觀點是,如果你想使用父類指針指向子類實例並使用它們的方法,那麼你應該使用虛擬方法。

+0

雖然是真的,但我不認爲這真的回答了一個更大的問題:「我怎麼知道我需要多態?「 – 2011-05-19 13:32:28

2

我主要使用的理智測試是 - 如果我定義的類是將來派生出來的,那麼行爲(函數)會保持不變還是需要重新定義。如果是這樣的話,那麼這個函數就會成爲虛擬的有力競爭者,如果不是,那麼我不知道 - 我可能需要研究問題領域,以便更好地理解我計劃實施的行爲。大多數問題域給了我答案 - 在這種情況下,行爲不是,通常非關鍵。

6

首先稍微迂迴的評論 - 在C++標準中,我們稱它們爲成員函數,而不是方法,儘管這兩個術語是等價的。

我看到兩個理由不讓虛擬成員函數。

  • 「YAGNI」 - 「你不會需要它」。如果你不確定課程是從哪個派生出來的,那麼假設它不會,也不會使成員函數變成虛擬的。順便說一句,「沒有從我這裏得到」就像一個非虛擬的析構函數。這也是關於意圖。如果它不是多態地使用類的意圖,請不要虛擬任何東西。如果你任意使用虛擬成員,你正在邀請對Liskov Substitution Principle的濫用,這些類別的錯誤很難追查和解決。
  • 性能/內存佔用。沒有虛擬成員函數的類不需要VTable(虛擬表,用於通過基類指針重定向多態調用),因此(可能)在內存中佔用更少的空間。此外,直接成員函數調用(可能)比虛擬成員函數調用更快。 不要通過先發制人地虛擬成員函數來過早地讓你的班級變得悲觀。
3

例如Java中的成員函數是100%虛擬的。在C++中,它被視爲代碼大小/函數調用時間懲罰。另外一個非虛函數保證了函數實現總是相同的(使用基類對象/引用)。 「有效的C++」中的Scott Meyers更詳細地討論了它。

+1

非虛函數」保持不變「這一事實對於調用它的其他函數很重要,當涉及到虛函數時更難讓**調用**函數更正。 – 2011-05-19 14:11:20

0

虛擬方法是一種實現多態的方法。當你想要在更抽象的層次上定義某些動作時使用它們,這樣就不可能實際執行,因爲它太籠統了。只有在派生類中,您可以告訴如何執行該操作。但是隨着虛擬方法的定義,你創建了一個需求,這增加了類的層次結構的剛性。這是可取的或不可取的,這取決於你試圖獲得什麼,以及你自己的口味。

1

我想快速確定一種可能的方法是考慮是否要處理一堆您將用於執行相同任務的相似類,其中方式爲關於完成這些任務。

一個微不足道的例子就是計算各種幾何圖形的面積問題。你需要正方形,圓形,矩形,三角形等區域,這裏唯一改變的是用來計算面積的數學公式(方式)。因此,將這些形狀從一個公共基類繼承並在返回該區域的基類中添加一個虛擬方法是一個很好的決定(然後,您可以使用相應的數學公式在每個孩子中實現該方法) 。

使一切虛擬「以防萬一」將使你的對象佔用更多的內存。另外,調用虛函數時有一個小的(但非零)開銷。因此,恕我直言,當性能/內存約束很重要時(這基本上意味着在您編寫的每個真實世界的程序中),使一切變爲虛擬「以防萬一」將是一個壞主意。

但是,基於需求的清晰程度以及預期的代碼更改頻率,這再次存在爭議。例如,在一個快速而髒的工具或一個初始原型中,少量額外的內存字節和幾毫秒的損失時間並不意味着太多,爲了這個原因可以有一堆(不必要的)虛擬函數的靈活性。

4

這是一個棘手的問題。但是有一些指導方針/經驗法則可以遵循。

  1. 只要你不需要從類派生,那麼不寫任何virtual方法,一旦你需要派生,只會讓那些virtual你需要在子類中自定義方法。
  2. 如果一個類有一個virtual方法,那麼析構函數應該是virtual(討論結束)。
  3. 嘗試遵循NVI(非虛擬接口)成語,使virtual方法非公開,並提供公共包裝來負責評估前置和後置條件,以便派生類不會意外打斷它們。

我覺得那些很簡單。我絕對會讓ABI的反射部分消失,它只在交付DLL時纔有用。