dynamic_cast有什麼用?實際上,dynamic_cast是ANSI C++中僅有的兩個與RTTI (Run Time Type Identification) 有關的用法之一。C++的類繼承,使得有時很難弄清楚你正在使用的object屬於哪個class,特別是當繼承樹比較深並且比較複雜的時候,例如,當你在程式中取得一個CWnd*指標,你的意圖是,如果它實際上指向一個dialog對象那麼就調用它的DoModal方法,這個時候,你就需要dynamic_cast: CWnd* pWin = myGetWin();CDialog* pDlg = NULL: if( pDlg = dynamic_cast<CDialog*>(pWin) ) pDlg->DoModal(); 這裡用簡單的強制類型轉換或者static_cast不行嗎?確實是不行的。假如pWin實際指向的是一個View對象,你的程式就會對View對象調用DoModal(),在MFC裡你這樣做或許僅僅會得到一個Assert,有些場合會得到一個segment fault,而按照Effective C++裡面的說法,這樣undefined的用法,也許會導致這個程式向你熱戀中的女友發一封絕交信,呵呵。如果是多線程的程式,這樣的一個問題導致你花上一整天時間瘋狂地打log是很正常的事情。 而使用dynamic_cast,如果實際對象不是CDialog,一個NULL指標會被傳回來。就算你忘記了寫if then,你也可以很快利用調試器定位出錯誤在哪裡。NULL指標的bug大概是所有bug中最幸福的一種。 dynamic_cast做到這一點是利用了編譯器提供的RTTI機制,編譯器會把一個class的類型資訊放在C++ Runtime系統的某處,常常就是在vtbl的末端,這樣從class指標得到vptr,再從vptr得到vtbl,就能夠檢查類型資訊是否匹配。 dynamic_cast的用法之所以少見,是因為它實際上是一種“不好的”用法,某種程度上破壞了O-O的一些基本原則。既然定義了一個基類類型,就是想把衍生類別的差異性隱藏起來,提供一個統一的interface,那麼你又有什麼理由再從外面把這種差異性還原出來呢?不止一本C++經典論述中提到過,在一個設計良好的類繼承體系中,不該為dynamic_cast留下生存空間。 然而,聖經和實際生活總有距離,有時候架構不那麼完美,只是把一堆對象用一個基類指標的數組管理起來,你必須使用某個衍生類別的特定方法,而類庫又沒有為你提供這樣的路徑,你就只好自己動手,把它解構。像上面給出的例子,很難說在實踐中不會遇見這樣的狀況。 也許有人會說,這樣的問題啊,我會自己處理的,用不著什麼RTTI,我會為在基類中添加一個type資料成員,再做一個GetType()方法,每個衍生類別對象在構造的時候賦予不同的值,調用的時候判斷一下,這不就OK了嗎?這樣做當然也可以,但是首先,你的實現效率比編譯器的RTTI實現差遠了,你要為每個object增加一個字的空間開銷,而編譯器則是對一個class增加一點空間,因為編譯器可以利用現有的vptr和vtbl,你卻沒法控制C++ Runtime庫。其次,退一萬步講,為什麼要設計C++呢?完全可以用純正的C程式實作類別封裝、繼承、多態這些機制,無非就是用一堆函數指標嘛!早期的C++程式是怎麼編譯的?是用一個前置處理器先翻譯成C代碼,再用C編譯器去編譯。用了C++,不就是為了少寫一些代碼,程式結構更清楚嘛。因此只要編譯器提供這個功能,就不要再自己去找麻煩。 關於dynamic_cast最有趣的事情在於,主流的C++編譯器為了滿足一些吝嗇的C程式員的要求,一般都提供了把RTTI關掉的編譯選項,這樣確實可以減少一些空間開銷。而如果你使用了這樣的編譯選項,而你的程式中又使用了dynamic_cast,啊哈,美妙的segment fault立即就會向你襲來。 在不少mail-list中都可以看到有人在抱怨dynamic_cast引起的程式崩潰,其中一些使用g++的提問者還明白地知道自己加上了-fno-rtti的選項,回答問題的老大們就往往會用"foolish"來形容這樣的行徑。至於用Visual C++的人就更糟糕了,VC6中的cl編譯器預設設定是關掉RTTI 的,你必須自己在project settings頁面裡把它選上,或者手工添加"/GR"選項。在Windows下面試圖移植OpenH323類庫的同道們,估計肯定有人吃過這個苦頭。 最無辜的程式員是這樣的,他認為使用ANSI C++標準中定義的東西怎麼會有問題呢?但偏偏他遇到了一位熱心的Build Master,為了最佳化效能把RTTI選項給關掉了。我想這個問題,是衡量一個軟體開發隊伍組態管理水平的一個經典題目。
此日誌的引用 URL 是:http://skywalkerj.spaces.live.com/blog/cns!49c467f1b758d6fc!146.entry?sa=20850098