一、C 風格(C-style)類型強制轉換
方法比較簡單,如下所示:
TYPE1 A;
TYPE2 B;
A = (TYPE1)B; // 強制轉換。
二、C++風格(C++ style)類型強制轉換
C++標準定義了四個新的轉換符:'reinterpret_cast', 'static_cast', 'dynamic_cast' 和 'const_cast',目的在於控制類(class)之間的類型轉換。
代碼:
reinterpret_cast<new_type>(expression)
dynamic_cast<new_type>(expression)
static_cast<new_type>(expression)
const_cast<new_type>(expression)
1、const_cast
const_cast轉換符是用來移除變量的const或volatile限定符。
對於const變量,我們不能修改它的值,這是這個限定符最直接的表現。但是我們就是想違背它的限定希望修改其內容怎麼辦呢?
下邊的代碼顯然是達不到目的的:const
int constant = 10;
int modifier = constant;
因爲對modifier的修改並不會影響到constant,這暗示了一點:const_cast轉換符也不該用在對象數據上,因爲這樣的轉換得到的兩個變量/對象並沒有相關性。
只有用指針或者引用,讓變量指向同一個地址纔是解決方案,可惜下邊的代碼在C++中也是編譯不過的:const
int constant = 21;
int* modifier = &constant
// Error: invalid conversion from 'const int*' to 'int*'
(上邊的代碼在C中是可以編譯的,最多會得到一個warning,所在在C中上一步就可以開始對constant裏面的數據胡作非爲了)
把constant交給非const的引用也是不行的。const
int constant = 21;
int& modifier = constant;
// Error: invalid initialization of reference of type 'int&' from expression of type 'const int'
於是const_cast就出來消滅const,以求引起程序世界的混亂。
下邊的代碼就順利編譯通過了:const
int constant = 21;
const int* const_p = &constant;
int* modifier = const_cast<int*>(const_p);
*modifier = 7;
測試發現,指針modifier指向的內存的數據變爲7,常量constant仍然是21,而不是7,爲什麼呢?modifier、&constant指向同一內存,內容卻不一樣,這是因爲常量constant是“可摺疊常量”,在編譯階段,對常量的引用全部替換爲該常量的值(21),常量名constant並不會消失,編譯器會把他放入到符號表中,同時,會爲該變量分配空間。因此,常量constant不是靠內存中的內容來取數的,而是靠保存在符號表中的索引來取數的,因此常數constant無論什麼情況下使用常數21,同時它又有內存空間。
C++準轉換運算符是可以用傳統轉換方式實現的。const_cast實現原因就在於C++對於指針的轉換是任意的,它不會檢查類型,任何指針之間都可以進行互相轉換,因此const_cast就可以直接使用顯示轉換(int*)來代替:
const int constant
= 21;
int* modifier = (int*)(&constant);
2、static_cast
static_cast在面對const的時候無能爲力:不能去除const限定。
static_cast只能用在“具有一定關係”的指針類型(類指針)之間的轉換;此外,static_cast還可以用在基本類型和對象的強制類型轉換上。對於static_cast所需要的關係,"繼承"是其中之一,static_cast支持具有繼承關係的類指針之間的類型轉換。但要注意:這種轉換是無條件的,只負責類型轉換,不負責檢測轉換是否安全,即,轉換後得到的結果(類指針)始終不爲空,但是結果不一定正確。代碼:
class A
{
......
};
class B : public A
{
......
};
int main()
{
B *pb =new B();
A *pa =static_cast<A*>(pb);// 子類強制轉換爲父類,結果pa不爲空,結果正確。
A *pa2 =new A();
B *pb2 =static_cast<B*> (pa2);// 父類強制轉換爲子類,編譯不會報錯,但是不安全,子類的一些新的成員
// 無法調用。結果pb2不爲空,但是結果錯誤。
......
}
實際上static_cast真正用處並不在指針和引用上,而在基本類型和類對象的類型轉換上。而基本類型和對象的轉換都是其他三個轉換運算符所辦不到的。代碼:
float fV
= 10.5;
int iV = static_cast<int>(fV);
A a;
B b =static_cast<B>(a);
3、dynamic_cast
dynamic_cast在面對const的時候無能爲力:不能去除const限定。
dynamic_cast與static_cast一樣,只能用在“具有一定關係”的指針類型(類指針)之間的轉換,實際上只適用於具有繼承關係的2個類指針之間的類型強制轉換。dynamic_cast與static_cast的區別在於,它是有條件的轉換,dynamic_cast會進行判別,確定源指針所指的內容,是否真的合適被目標指針接受(子類轉換爲父類可接受;父類轉換爲子類、沒有關係的類之間的轉換可能均不可接受)。如果是否定的,那麼dynamic_cast則會返回NULL。這是通過檢查"運行期類型信息"(Runtime type information,RTTI)來判定的,它還受到編譯器的影響,有些編譯器需要設置開啓才能讓程序正確運行),因此dynamic_cast也就不能用傳統的轉換方式來實現了。
4、reinterpret_cast
reinterpret_cast在面對const的時候無能爲力:不能去除const限定。
該運算符用於不同類型的指針之間、指針與整數類型之間的類型強制轉換,它會將一個類型的數據拷貝給另一個類型,使2個類型具有相同的數據,來實現類型強制轉換。用於以下數據類型之間的強制轉換:
- 從指針類型到一個足夠大的整數類型;
- 從整數類型或者枚舉類型到指針類型;
- 從一個指向函數的指針到另一個不同類型的指向函數的指針;
- 從一個指向對象的指針到另一個不同類型的指向對象的指針;
- 從一個指向類函數成員的指針到另一個指向不同類型的函數成員的指針;
- 從一個指向類數據成員的指針到另一個指向不同類型的數據成員的指針;
事實上reinterpret_cast的使用並不侷限在上邊所說的幾項的,任何類型的指針之間都可以互相轉換,都不會得到編譯錯誤。總結來說:reinterpret_cast用在任意指針(或引用)類型之間的轉換;以及指針與足夠大的整數類型之間的轉換;從整數類型(包括枚舉類型)到指針類型。
但是,使用reinterpret_cast很容易導致程序的不安全,只有將轉換後的類型值轉換回到其原始類型,這樣纔是正確使用reinterpret_cast方式。這樣說起來,reinterpret_cast轉換成其它類型的目的只是臨時的隱藏自己的類型,要真想使用那個值,還是需要讓其露出真面目才行。這一點很重要,在一些特殊的場合中很有用。
總 結
去const屬性用const_cast。
基本類型轉換用static_cast。
多態類之間的類型轉換用daynamic_cast。
不同類型的指針類型轉換用reinterpreter_cast。