1. 程式人生 > >C++中static_cast和dynamic_cast強制類型轉換

C++中static_cast和dynamic_cast強制類型轉換

tro 父類 虛函數表 找到 virt 內部 pub 判斷 ()

在C++標準中,提供了關於類型層次轉換中的兩個關鍵字static_cast和dynamic_cast。

一、static_cast關鍵字(編譯時類型檢查)

用法:static_cast < type-id > ( expression ),該運算符把expression轉換為type-id類型,但沒有運行時類型檢查來保證轉換的安全性,它主要有如下幾種用法:

(1)用於基本數據類型之間的轉換,如把int轉換為char,把int轉換成enum,但這種轉換的安全性需要開發者自己保證(這可以理解為保證數據的精度,即程序員能不能保證自己想要的程序安全),如在把int轉換為char時,如果char沒有足夠的比特位來存放int的值(int>127或int<-127時),那麽static_cast所做的只是簡單的截斷,及簡單地把int的低8位復制到char的8位中,並直接拋棄高位。

(2)把空指針轉換成目標類型的空指針

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

(4)用於類層次結構中父類和子類之間指針和引用的轉換。

對於以上第(4)點,存在兩種形式的轉換,即上行轉換(子類到父類)和下行轉換(父類到子類)。對於static_cast,上行轉換時安全的,而下行轉換時不安全的,為什麽呢?因為static_cast的轉換時粗暴的,它僅根據類型轉換語句中提供的信息(尖括號中的類型)來進行轉換,這種轉換方式對於上行轉換,由於子類總是包含父類的所有數據成員和函數成員,因此從子類轉換到父類的指針對象可以沒有任何顧慮的訪問其(指父類)的成員。而對於下行轉換為什麽不安全,是因為static_cast只是在編譯時進行類型堅持,沒有運行時的類型檢查,具體原理在dynamic_cast中說明。

二、dynamic_cast關鍵字(運行時類型檢查)

(作為四個內部類型轉換操作符之一的dynamic_cast和傳統的C風格的強制類型轉換有著巨大的差別。除了dynamic_cast以外的轉換,其行為的都是在編譯期就得以確定的,轉換是否成功,並不依賴被轉換的對象。而dynamic_cast則不然。在這裏,不再討論其他三種轉換和C風格的轉換。
首先,dynamic_cast依賴於RTTI信息,其次,在轉換時,dynamic_cast會檢查轉換的source對象是否真的可以轉換成target類型,這種檢查不是語法上的,而是真實情況的檢查。
先看RTTI相關部分,通常,許多編譯器都是通過vtable找到對象的RTTI信息的,這也就意味著,如果基類沒有虛方法,也就無法判斷一個基類指針變量所指對象的真實類型, 這時候,dynamic_cast只能用來做安全的轉換,例如從派生類指針轉換成基類指針.而這種轉換其實並不需要dynamic_cast參與.
也就是說,dynamic_cast是根據RTTI記載的信息來判斷類型轉換是否合法的.)

用法:同static_cast

dynamic_cast主要用於類層次結構中父類和子類之間指針和引用的轉換,由於具有運行時類型檢查,因此可以保證下行轉換的安全性,何為安全性?即轉換成功就返回轉換後的正確類型指針,如果轉換失敗,則返回NULL,之所以說static_cast在下行轉換時不安全,是因為即使轉換失敗,它也不返回NULL。

對於上行轉換,dynamic_cast和static_cast是一樣的。

對於下行轉換,說到下行轉換,有一點需要了解的是在C++中,一般是可以用父類指針指向一個子類對象,如parent* P1 = new Children(); 但這個指針只能訪問父類定義的數據成員和函數,這是C++中的靜態聯翩,但一般不定義指向父類對象的子類類型指針,如Children* P1 = new parent;這種定義方法不符合生活習慣,在程序設計上也很麻煩。這就解釋了也說明了,在上行轉換中,static_cast和dynamic_cast效果是一樣的,而且都比較安全,因為向上轉換的對象一般是指向子類對象的子類類型指針;而在下行轉換中,由於可以定義就不同了指向子類對象的父類類型指針,同時static_cast只在編譯時進行類型檢查,而dynamic_cast是運行時類型檢查,則需要視情況而定。下面通過代碼進行說明

class Base
{
    virtual void fun(){}
};

class Derived:public Base
{
};

由於需要進行向下轉換,因此需要定義一個父類類型的指針Base *P,但是由於子類繼承與父類,父類指針可以指向父類對象,也可以指向子類對象,這就是重點所在。如果 P指向的確實是子類對象,則dynamic_cast和static_cast都可以轉換成功,如下所示:

Base *P = new Derived();
Derived *pd1 = static_cast<Derived *>(P);
Derived *pd2 = dynamic_cast<Derived *>(P);

以上轉換都能成功。

但是,如果 P 指向的不是子類對象,而是父類對象,如下所示:

Base *P = new Base;
Derived *pd3 = static_cast<Derived *>(P);
Derived *pd4 = dynamic_cast<Derived *>(P);

在以上轉換中,static_cast轉換在編譯時不會報錯,也可以返回一個子類對象指針(假想),但是這樣是不安全的,在運行時可能會有問題,因為子類中包含父類中沒有的數據和函數成員,這裏需要理解轉換的字面意思,轉換是什麽?轉換就是把對象從一種類型轉換到另一種類型,如果這時用 pd3 去訪問子類中有但父類中沒有的成員,就會出現訪問越界的錯誤,導致程序崩潰。而dynamic_cast由於具有運行時類型檢查功能,它能檢查P的類型,由於上述轉換是不合理的,所以它返回NULL。

三、總結

C++中層次類型轉換中無非兩種:上行轉換和下行轉換

對於上行轉換,static_cast和dynamic_cast效果一樣,都安全;

對於下行轉換:你必須確定要轉換的數據確實是目標類型的數據,即需要註意要轉換的父類類型指針是否真的指向子類對象,如果是,static_cast和dynamic_cast都能成功;如果不是static_cast能返回,但是不安全,可能會出現訪問越界錯誤,而dynamic_cast在運行時類型檢查過程中,判定該過程不能轉換,返回NULL。

註:

虛函數對於dynamic_cast轉換的作用

  為何使用dynamic_cast轉換類指針時,需要虛函數呢。

Dynamic_cast轉換是在運行時進行轉換,運行時轉換就需要知道類對象的信息(繼承關系等)。

如何在運行時獲取到這個信息——虛函數表。

  C++對象模型中,對象實例最前面的就是虛函數表指針,

通過這個指針可以獲取到該類對象的所有虛函數,包括父類的。

因為派生類會繼承基類的虛函數表,所以通過這個虛函數表,我們就可以知道該類對象的父類,在轉換的時候就可以用來判斷對象有無繼承關系。

  所以虛函數對於正確的基類指針轉換為子類指針是非常重要的。

C++中static_cast和dynamic_cast強制類型轉換