天天看點

static_cast AND dynamic_cast

類型轉換是一種機制,讓程式員能夠暫時或永久性改變編譯器對對象的解釋。注意,這并不意味着程式員改變了對象本身,而隻是改變了對對象的解釋。

在很多情況下,類型轉換是合理的需求,可解決重要的相容問題。是以,程式員經常需要讓編譯器按其所需的方式解釋資料,讓應用程式能夠成功編譯并執行。

C++編譯器仍需向後相容以確定遺留代碼能夠通過編譯,是以支援下面這樣的文法:int* pBuf = (int *)pString ;

C風格類型轉換實際上強迫編譯器根據程式員的選擇來解釋目标對象,強迫編譯器遵從自己的意願。然而,對不希望類型轉換破壞其倡導的類型安全的C++程式員來說,這是無法接受的。

C++提供了一種新的類型轉換運算符,專門用于繼承的情形,這種情形在C語言中并不存在。

4個C++類型運算符如下:

static_cast

dynamic_cast

reinterpret_cast

const_cast

1、static_cast

①用于相關類型的指針之間的轉換

這個相關類型指針是指:具有繼承關系的類的指針。

static_cast實作了基本的編譯階段檢查,確定指針被轉換為相關類型。使用static_cast可将指針向上轉換為基類類型,也可向下轉換為派生類類型。

這改進了C風格類型轉換,在C語言中可将指向一個對象的指針轉換為完全不相關的類型,而編譯器不會報錯。(C語言十分自由,而C++對于不同用途的類型轉換,會有一些不同的限制)

注意:static_cast隻驗證指針類型是否相關(是否有繼承關系類的指針),而不執行任何運作階段檢查。(故稱之為靜态static轉換)

例:

CBase*pBase = new CBase( ) ;

CDerived*pDerived = static_cast<CDerived*>(pBase) ;

由于static_cast隻在編譯階段檢查轉換類型是否相關,而不執行運作階段檢查,是以上面代碼能夠編譯通過,但在運作階段可能導緻意外結果。

static_cast 與C風格的類型轉換類似,隻是static_cast隻用于有繼承相關性的類的指針。它會對這個關系進行檢查。

故:

用static_cast把指向派生類的基類指針 轉換為派生類指針,底層的派生類對象不變,指針的值也不變,隻是改變了指針的管轄範圍。即:隻是改變了編譯器的解釋方式。(基類指針實際上指向的隻是派生類中的基類核心)

同理,用static_cast把指向派生類的派生類指針 轉換為基類指針,也隻是改變了編譯器對其的解釋方式。(static_cast對與多重繼承的指針,有特殊處理)

②用于内置資料類型的類型轉換

double dPi = 3.14;

int nNum =static_cast<int>(dPi) ;

使用static_cast可讓代碼閱讀者注意到這裡使用了類型轉換,并指出編譯器根據編譯階段可用資訊進行了必要的調整,以便執行所需的類型轉換。

2、dynamic_cast

dynamic_cast動态類型轉換在運作階段執行類型轉換。可檢查dynamic_cast操作的結果以判斷類型轉換是否成功。

dynamic_cast可以把基類指針轉換為派生類指針,也可以把派生類指針轉換為基類指針。如果轉換後可以安全使用,則轉換成功,否則其傳回NULL。(而static_cast的轉換,無法檢查轉換後的結果是否可以安全使用)

若基類指針指向的是基類對象,則把它dynamic_cast為派生類指針,則轉換會失敗。因為這是不安全的轉換。

若基類指針指向的是派生類對象,則把它dynamic_cast為派生類指針,則轉換會成功。這是安全的轉換。(dynamic_cast比static_cast檢查更加嚴格,但使用範圍更小)

【注意】dynamic_cast必須要在有虛函數的繼承裡進行。(static_cast則無此限制)

給定一個基類指針,程式員可能不确定,它目前指向哪種類型,這時可使用dynamic_cast在運作時判斷其類型,并在安全時使用轉換後的指針。

dynamic_cast這種在運作階段識别對象類型的機制成為 運作階段類型識别RTTI。

關于RTTI

RTTI是Runtime Type Information的縮寫,從字面上來了解就是執行期的類型資訊,其重要作用就是動态判别執行期的類型。

即:判斷基類指針或引用,目前所綁定的類型。

它有兩種方法來識别:

①用dynamic_cast類型轉換是否成功來識别類型。(dynamic_cast必須要在有虛函數的繼承裡進行)

void fun(CBase* pBase)  
{  
    CDerived* pDerived = dynamic_cast<CDerived*>(pBase) ;  
    if (pDerived != NULL)  
        pDerived->funDerived ;  
    else  
        pDerived->funBase ;  
}        

②用typeid判斷基類位址是否一緻來識别類型

void fun(CBase* pBase)  
{  
    CDerived* pDerived = NULL ;  
    if (typeid(*pBase) == typeid(CDerived))  
    {  
    pDerived = static_cast<CDerived*>(pBase) ;  
    pDerived->funDerived ;  
    }  
    else  
        pDerived->funBase ;  
}        

編譯器從類中虛指針指向的虛函數表的前一表項中提取typeid的值。(請看 C++對象模型簡介)

【一般地講,能用虛函數解決的問題就不要用“dynamic_cast”,能夠用“dynamic_cast”解決的問題就不要用“typeid”。】

RTTI破壞了面向對象的純潔性。

首先,它破壞了抽象。用RTTI檢測目前基類指針綁定的類型,這與虛函數提倡的隐藏細節,智能實作多态相違背。

繼續閱讀