2009-02-24 95 views
8

我最近正在爲一個副項目(cpp-markdown library,好奇)編寫一段C++代碼,並遇到了一個編碼問題,我希望得到一些意見。避免dynamic_cast/RTTI

cpp-markdown有一個名爲Token的基類,它有許多子類。其中兩個主要子類是Container(其中包含其他Token的集合)和TextHolder(當然,用作Token的基類)。

大部分的處理都是通過虛擬函數來處理的,但是其中一些處理在單個函數中處理得更好。爲此,我最終使用dynamic_cast將指針從Token*下載到它的一個子類,所以我可以調用特定於子類及其子類的函數。鑄造失敗是不可能的,因爲代碼能夠通過虛函數(例如isUnmatchedOpenMarker)知道何時需要這樣的事情。

還有其他兩種方式我看得出來處理這個問題:

  1. 創建的,我想作爲Token虛函數調用函數的所有,只是讓他們有一個空的機構除了需要處理他們一個(或多個),或......每個子類中

  2. 創建Token一個虛函數,將在適當類型的指針回到this當它呼籲某些亞型,和一個空指針如果被調用在其他方面。基本上是我已經在那裏使用的虛擬功能系統的擴展。

第二種方法似乎比雙方現有的一個,第一個更好的,對我來說。但我想知道其他有經驗的C++開發人員對此的看法。或者我是否過於擔心瑣事。 :-)

回答

20

#1污染不需要它的對象的類名稱空間和vtable。好吧,當你有一些通常會被實現的方法時,但只有一個派生類需要時纔會變得很難看。

#2只是dynamic_cast<>在圓點連衣裙和口紅。不要讓客戶端代碼更簡單,並且糾纏整個層次結構,要求基類和每個派生類半認知派生類。請使用dynamic_cast<>。這就是它的目的。

4

如果你想變得聰明,你也可以建立一個double dispatch模式,這是visitor pattern的三分之二。

  • 創建一個基地TokenVisitor類包含空虛擬visit(SpecificToken*)方法。
  • 向Token添加一個虛擬的accept(TokenVisitor*)方法,該方法在傳遞的TokenVisitor上調用正確類型的方法。
  • 從TokenVisitor中派生出各種各樣的東西,您將需要以各種方式對所有令牌執行操作。

對於全訪問者模式,爲樹形結構是有用的,具有默認accept方法遍歷兒童呼籲各token->accept(this);

4

如果你知道轉換不能失效,那就使用static_cast。

+1

同意,如果您已經使用虛函數檢查類型,那麼dynamic_cast就沒用了。您的其他解決方案都只是白色洗滌,您的層次結構被打破。如果你有一個黑客,至少使用更快的黑客。 – BigSandwich 2009-02-24 03:56:28

+1

如果派生類使用虛擬繼承從Token繼承,則static_cast將不起作用。在這種情況下,dynamic_cast不會沒用。 – bk1e 2009-02-24 06:18:29

2

爲什麼你想避免使用dynamic_cast?它是否會在您的應用程序中造成不可接受的瓶頸?如果沒有,現在可能不值得對代碼做任何事情。

如果在特定情況下交易某種程度的安全性以確保速度,您應該沒問題static_cast;然而,這是固定你的假設,你知道對象的類型,並且沒有可能演員陣容不好。如果你的假設後來變得錯誤,你可能會在代碼中出現一些神祕的崩潰錯誤。回到我原來的問題,你真的確定這種交易在這裏值得嗎?

至於你列出的選項: 第一次聽起來不像解決方案,就像最後一分鐘的破解一樣,當人們在凌晨3點編寫代碼時,我期望看到它。功能性流向類層次結構的基礎是OOP新手最常見的反模式之一。不要這樣做。

對於第二種選擇,你列出的任何選項實際上只是重新實現dynamic_cast - 如果你在一個只有廢話編譯器可用的平臺上(我聽說過Gamecube的編譯器佔用了系統可用RAM的四分之一與RTTI信息)這可能是值得的,但更有可能你只是浪費你的時間。你真的確定這是值得關注自己的東西嗎?

1

防止dynamic_cast的真正乾淨的方法是在正確的位置使用正確類型的指針。抽象應該照顧其餘的。

恕我直言,dynamic_cast具有這種聲望的原因是因爲每次在類層次結構中添加另一個子類型時,它的性能會降低一點。如果您在層次結構中有4-5個班級,則無需擔心。

1

有趣的東西。標記器中的dynamic_cast意味着您希望實際上將Token分成根據文本流中的當前位置和Token中的邏輯創建令牌的東西。每個標記都可以是獨立的,也可以像上面那樣創建一個標記來正確處理文本。

通過這種方式,您可以取出通用的東西,但仍然可以根據令牌的層次結構進行解析。您甚至可以在不使用dynamic_cast的情況下製作整個解析器數據驅動。