天天看點

dynamic_cast介紹

首先說到c++常用的四中轉換類型,我們都很清楚,分别是下面四中

 

dynamic_cast介紹

1 const_cast 

const_cast<目标類型>(辨別符):目标類型隻能是指針或者引用

2 static_cast 

    類似C風格的強制轉換,進行無條件轉換,靜态類型轉換:

    1)基類和子類之間的轉換:其中子類指針轉換為父類指針是安全的,但父類指針轉換為子類指針是不安全的(基類和子類之間的動态類型轉換建議用dynamic_cast)。

    2)基本資料類型轉換,enum,struct,int,char,float等。static_cast不能進行無關類型(如非基類和子類)指針之間的轉換。

    3)把任何類型的表達式轉換成void類型。

    4)static_cast不能去掉類型的const、volatile屬性(用const_cast)。

3 reinterpret_cast

    僅重新解釋類型,但沒有進行二進制的轉換:

    1) 轉換的類型必須是一個指針,應用、算術類型、函數指針或者成員指針。

    2) 在比特級别上進行轉換,可以把一個指針轉換成一個整數,也可以把一個整數轉換成一個指針(先把一個指針轉換成一個整數,在把該整數轉換成原類型的指針,還可以得到原先的指針值)。但不能将非32bit的執行個體轉成指針。

    3) 最普通的用途就是在函數指針類型之間進行轉換。

    4) 很難保證移植性。

4dynamic_cast

    有條件轉換,動态類型轉換,運作時檢查類型安全(轉換失敗傳回NULL):

    1 将一個基類對象指針(或引用)cast到繼承類指針,dynamic_cast會根據基類指針是否真正指向繼承類指針來做相應處理, 即會作出一定的判斷。

    2 若對指針進行dynamic_cast,失敗傳回null,成功傳回正常cast後的對象指針;

    3 若對引用進行dynamic_cast,失敗抛出一個異常,成功傳回正常cast後的對象引用。

    4 dynamic_cast在将父類cast到子類時,父類必須要有虛函數,否則編譯器會報錯。

    5 dynamic_cast主要用于類層次間的上行轉換和下行轉換,還可以用于類之間的交叉轉換。

    6 在類層次間進行上行轉換時,dynamic_cast和static_cast的效果是一樣的;

    7 在進行下行轉換時,dynamic_cast具有類型檢查的功能,比static_cast更安全。

    因為這裡主要說dynamic_cast,是以把dynamic_cast放最後一個說,怎麼用我們都很清楚,但是有個關鍵點值得深究,為什麼dynamic_cast在将父類cast到子類時,父類必須要有虛函數,否則編譯器會報錯,而當子類轉換為父類的時候不需要呢? 

    要說明白這個問題得先說多态,多态分為兩種一種是編譯器的多态,還有一種是運作期的多态。然後說當子類轉換為父類的時候為什麼不需要,因為在類層次間進行上行轉換時,dynamic_cast和static_cast的效果是一樣的,是以這種轉換關系在編譯時期編譯器就明确的直到父子之間的繼承關系,這個工作編譯時期就已經完成了,是以不需要,可為什麼dynamic_cast在将父類cast到子類時,為什麼又需要了呢?這種情況正好對應的是運作期的多态,為什麼這種情況在編譯期搞不定了呢,很簡單你想想,一個父親可以又多個孩子,但是所有的孩子隻有一個父親(不接受杠精的反駁),當向上轉換的時候,因為所有的子類隻有一個父親編譯器很明确目标直接就搞定了,可是向下就不一樣了,那麼多孩子,如果有一天老大過來說我是老二,又過了一天又說我是老四(對應程式中錯誤的轉換行為),要怎麼辨識真僞呢,如果是錯的怎麼般,關鍵的地方在這如果錯了會發生什麼問題。非常危險,先說為什麼這種錯誤的轉換很危險,要明白類型轉換實際上是對記憶體的截取或者擴充,為什麼這樣說呢,假如類D1繼承了類B,B有一個變量int i,D有一個int y,那麼記憶體結構是這樣的

 

dynamic_cast介紹

  

    當我們進行類型轉換時,理論上發生了什麼呢,當我們把子類強轉換為父類時很簡單,把D1中末尾屬于自己的部分截掉,将父類強轉為子類時隻要把記憶體大小擴大子類大小位元組就可以了,看到差別沒有,當父類強轉為子類時實際上時需要擴大記憶體區間映射到相應的子類對象上,如果轉換的子類類型是錯誤的呢,如果兩個子類的大小不一樣,可能通路到非法記憶體,就算記憶體大小是一樣的,但是記憶體和對象結構的對應關系是錯誤的,如果有指針變量,對象的指針變量也會變成野指針,直接亂掉,通路起來會造成災難性的後果。

  那麼如何確定轉換的正确性呢,這也是dynamic_cast最重要的功能,這裡就要說到才c++的RTTI機制,這裡的RTTI指c++的運作時類型識别資訊,我們知道隻有帶有虛函數的類才會生成虛函數表,虛函數指針指向了虛函數表,實際上在這張表裡還有一個很重要的資訊就是指向該類的typeinfo的指針,裡面記錄了該類的類型資訊和繼承關系,在進行動态類型轉換時隻要取虛函數表中的第-1個元素得到type_info類對象判斷其真正的類型在進行操作(vptr實際上向後移動了一個位置,編譯器取虛函數的時候vptr[0]是第一個虛函數指針,vptr[-1]就能取到指向type_info的指針,一般我們不會主動去取type_info指針).是以很簡單運作時轉換的時候隻要取虛函數表中的typeinfo資訊判斷他的類型是否是你要轉換的類型以及在繼承關系的合法性就可以了,如果是非法的就轉換失敗傳回空指針。這樣就保證了轉換的安全性,當然也有他的弊端,就是相比靜态編譯器的轉換帶來了運作時的開銷,且內建關系越複雜開銷越大,不過我們一般能通過虛函數的多态特性解決(這種虛函數的多态性是不需要周遊typeinfo的虛函數表已經進行了相應的替換)不會進行這種強制類型的轉換,除非虛函數解決不了需要子類中特有的屬性後者方法的時候才會進行這種運作時期的轉換。

繼續閱讀