天天看點

條款27:盡量少做轉型動作

條款27:盡量少做轉型動作

C++的四種顯示類型轉換

①reinterpret_cast(在編譯期間實作轉換)

      将一個類型的指針轉換成另一個類型的指針。這種轉換不用于修改指針變量值資料存放的格式(不改變指針變量值),隻需在編譯期間重新解釋指針的類型就可以做到,可以将指針值轉換為一個整型數。但是不能用于非指針類型的轉換,否則将不會通過編譯。

     意圖執行低級轉型,結果取決于編譯器,故不可移植。

②const_cast(在編譯期間實作轉換)

       用于去除指針變量的常量屬性,将它轉換為一個對應指針類型的普通變量,反過來,也可以将一個非常量的指針變量轉換為常量指針變量

③stactic_cast(在編譯期間實作轉換)

      用來強迫隐式轉換,例如将int轉換為double,将void*轉換為typed指針,将non-const對象轉換為const對象。

④dynamic_cast(在運作期間實作轉換,并可以傳回轉換成功與否的标志)

       用于繼承體系下的"向下安全轉換",通常用于将基類對象指針轉換為其子類對象指針,它也是唯一一種無法用舊式轉換進行替換的轉型,也是唯一可能耗費重大運作成本的轉型動作.

       運算符dynamic_cast可以針對兩種資料類型做強制轉換:指針類型和引用類型

轉型在對象中運用可能會導緻錯誤的調用

調用

結果

Window size:2 GlassWindow size:3

Window size:2 GlassWindow size:5

        将*this轉型為window調用的并不是目前對象上的函數,而是稍早轉型動作所建立的一個“*this對象的base class成分”的暫時副本身上的onResize!并不是在目前對象身上調用Window::onResize之後又在該對象上執行SpecialWindow專屬行為。不,它是在“目前對象之base

calss成分”的副本上調用Window::onResize,然後在目前對象上執行SpecialWindow專屬動作。如果Window::onResize修改了對象内容,目前對象其實沒被改動,改動的是副本。然而SpecialWindow::onResize内如果也修改對象,目前對象真的會被改動。這使目前對象進入一種“傷殘”狀态:其base class成分的更改沒有落實,而derived

class成分的更改倒是落實了。

       解決之道是拿掉轉型動作,代之你真正想要說的話。是以,真正的解決方法是:

修改後

同樣調用結果

Window size:4 GlassWindow size:5

dynamic_cast

        dynamic_cast的許多實作版本執行速度相當慢。假如至少有一個很普通的實作版本基于“class名稱之字元串比較”,如果你在四層深的單繼承體系内的某個對象身上執行dynamic_cast,可能會耗用多達四次的strcmp調用,用以比較class名稱。深度繼承或多重繼承的成本更高!某些實作版本這樣做有其原因(它們必須支援動态連結)。在對注重效率的代碼中更應該對dynamic_cast保持機敏猜疑。

       之是以需要用dynamic_cast,通常是因為你想在一個你認定為derived class對象身上執行derived class操作函數,但你的手上隻有一個“指向base”的pointer或者reference,你隻能靠他們來處理對象。兩個一般性做法可以避免這個問題:

①使用容器,并在其中存儲直接指向derived class對象的指針(通常是智能指針),如此便消除了“通過base class接口處理對象”的需要。假設先前的Window/GlassWindow 繼承體系中有GlassWindow 才支援閃爍效果,試着不要這樣做:

在類GlassWindow 中添加閃爍效果函數

這樣調用

我們應該改成下面的調用方式

        這種做法無法在同一個容器記憶體儲指針“指向所有可能之各種Window派生類”。在Window上繼承,那麼就要定義多個類型的容器

 ②通過base class接口處理“所有可能之各種window派生類”,那就是在base class 裡提供virtual函數做你想對各個Window派生類做的事。

修改後代碼  

GlassWindows Link

WoodWindow Link

絕對必須拒絕的是所謂的“連串(cascading)dynamic_casts,這樣導緻代碼又大又慢

記住

1.如果可以,盡量避免轉型,特别是在注重效率的代碼中避免dynamic_cast。如果有個設計需要轉型動作,試着發展無需轉型的替代設計。

2.如果轉型是必要的,試着将它隐藏于某個函數背後。客戶随後可以調用該函數,而不需要将轉型放進他們自己的代碼内。

3.甯可使用C++ style(新式)轉型,不要使用舊式轉型。前者很容易辨認出來,而且也有着比較分門别類的職責。

繼續閱讀