轉自:https://www.cnblogs.com/chenyangchun/p/6795923.html
1. c強制轉換與c++強制轉換
c語言強制類型轉換主要用于基礎的資料類型間的轉換,文法為:
(type-id)expression//轉換格式1
type-id(expression)//轉換格式2
c++除了能使用c語言的強制類型轉換外,還新增了四種強制類型轉換:static_cast、dynamic_cast、const_cast、reinterpret_cast,主要運用于繼承關系類間的強制轉化,文法為:
static_cast<new_type> (expression)
dynamic_cast<new_type> (expression)
const_cast<new_type> (expression)
reinterpret_cast<new_type> (expression)
備注:new_type為目标資料類型,expression為原始資料類型變量或者表達式。
《Effective C++》中将c語言強制類型轉換稱為舊式轉型,c++強制類型轉換稱為新式轉型。
2. static_cast、dynamic_cast、const_cast、reinterpret_cast
-
static_cast
static_cast相當于傳統的C語言裡的強制轉換,該運算符把expression轉換為new_type類型,用來強迫隐式轉換,例如non-const對象轉為const對象,編譯時檢查,用于非多态的轉換,可以轉換指針及其他,但沒有運作時類型檢查來保證轉換的安全性。它主要有如下幾種用法:
①用于類層次結構中基類(父類)和派生類(子類)之間指針或引用的轉換。
進行上行轉換(把派生類的指針或引用轉換成基類表示)是安全的;
進行下行轉換(把基類指針或引用轉換成派生類表示)時,由于沒有動态類型檢查,是以是不安全的。
②用于基本資料類型之間的轉換,如把int轉換成char,把int轉換成enum。這種轉換的安全性也要開發人員來保證。
③把空指針轉換成目标類型的空指針。
④把任何類型的表達式轉換成void類型。
注意:static_cast不能轉換掉expression的const、volatile、或者__unaligned屬性。
基本類型資料轉換舉例如下:
char a = 'a';
int b = static_cast<char>(a);//正确,将char型資料轉換成int型資料
double *c = new double;
void *d = static_cast<void*>(c);//正确,将double指針轉換成void指針
int e = 10;
const int f = static_cast<const int>(e);//正确,将int型資料轉換成const int型資料
const int g = 20;
int *h = static_cast<int*>(&g);//編譯錯誤,static_cast不能轉換掉g的const屬性
類上行和下行轉換:
if(Derived *dp = static_cast<Derived *>(bp)){//下行轉換是不安全的
//使用dp指向的Derived對象
}
else{
//使用bp指向的Base對象
}
if(Base*bp = static_cast<Derived *>(dp)){//上行轉換是安全的
//使用bp指向的Derived對象
}
else{
//使用dp指向的Base對象
}
dynamic_cast
dynamic_cast<type*>(e)
dynamic_cast<type&>(e)
dynamic_cast<type&&>(e)
type必須是一個類類型,在第一種形式中,type必須是一個有效的指針,在第二種形式中,type必須是一個左值,在第三種形式中,type必須是一個右值。在上面所有形式中,e的類型必須符合以下三個條件中的任何一個:e的類型是是目标類型type的公有派生類、e的類型是目标type的共有基類或者e的類型就是目标type的的類型。如果一條dynamic_cast語句的轉換目标是指針類型并且失敗了,則結果為0。如果轉換目标是引用類型并且失敗了,則dynamic_cast運算符将抛出一個std::bad_cast異常(該異常定義在typeinfo标準庫頭檔案中)。e也可以是一個空指針,結果是所需類型的空指針。
dynamic_cast主要用于類層次間的上行轉換和下行轉換,還可以用于類之間的交叉轉換(cross cast)。
在類層次間進行上行轉換時,dynamic_cast和static_cast的效果是一樣的;
在進行下行轉換時,dynamic_cast具有類型檢查的功能,比static_cast更安全。dynamic_cast是唯一無法由舊式文法執行的動作,也是唯一可能耗費重大運作成本的轉型動作。
(1)指針類型
舉例,Base為包含至少一個虛函數的基類,Derived是Base的共有派生類,如果有一個指向Base的指針bp,我們可以在運作時将它轉換成指向Derived的指針,代碼如下:
if(Derived *dp = dynamic_cast<Derived *>(bp)){
//使用dp指向的Derived對象
}
else{
//使用bp指向的Base對象
}
值得注意的是,在上述代碼中,if語句中定義了dp,這樣做的好處是可以在一個操作中同時完成類型轉換和條件檢查兩項任務。
(2)引用類型
因為不存在所謂空引用,是以引用類型的dynamic_cast轉換與指針類型不同,在引用轉換失敗時,會抛出std::bad_cast異常,該異常定義在頭檔案typeinfo中。
void f(const Base &b){
try{
const Derived &d = dynamic_cast<const Base &>(b);
//使用b引用的Derived對象
}
catch(std::bad_cast){
//處理類型轉換失敗的情況
}
}
-
const_cast
const_cast,用于修改類型的const或volatile屬性。
該運算符用來修改類型的const(唯一有此能力的C++-style轉型操作符)或volatile屬性。除了const 或volatile修飾之外, new_type和expression的類型是一樣的。
①常量指針被轉化成非常量的指針,并且仍然指向原來的對象;
②常量引用被轉換成非常量的引用,并且仍然指向原來的對象;
③const_cast一般用于修改底指針。如const char *p形式。
舉例轉換如下:
const int g = 20;
int *h = const_cast<int*>(&g);//去掉const常量const屬性
const int g = 20;
int &h = const_cast<int &>(g);//去掉const引用const屬性
const char *g = "hello";
char *h = const_cast<char *>(g);//去掉const指針const屬性
-
reinterpret_cast
new_type必須是一個指針、引用、算術類型、函數指針或者成員指針。它可以把一個指針轉換成一個整數,也可以把一個整數轉換成一個指針(先把一個指針轉換成一個整數,再把該整數轉換成原類型的指針,還可以得到原先的指針值)。
reinterpret_cast意圖執行低級轉型,實際動作(及結果)可能取決于編輯器,這也就表示它不可移植。
舉一個錯誤使用reintepret_cast例子,将整數類型轉換成函數指針後,vc++在執行過程中會報"...中的 0xxxxxxxxx 處有未經處理的異常: 0xC0000005: Access violation"錯誤:
#include <iostream>
using namespace std;
int output(int p){
cout << p <<endl;
return 0;
}
typedef int (*test_func)(int );//定義函數指針test_func
int main(){
int p = 10;
test_func fun1 = output;
fun1(p);//正确
test_func fun2 = reinterpret_cast<test_func>(&p);
fun2(p);//...處有未經處理的異常: 0xC0000005: Access violation
return 0;
}
IBM的C++指南、C++之父Bjarne Stroustrup的FAQ網頁和MSDN的Visual C++也都指出:錯誤的使用reinterpret_cast很容易導緻程式的不安全,隻有将轉換後的類型值轉換回到其原始類型,這樣才是正确使用reinterpret_cast方式。
MSDN中也提到了,實際中可将reinterpret_cast應用到哈希函數中,如下(64位系統中需将unsigned int修改為unsigned long):
// expre_reinterpret_cast_Operator.cpp
// compile with: /EHsc
#include <iostream>
// Returns a hash code based on an address
unsigned short Hash( void *p ) {
unsigned int val = reinterpret_cast<unsigned int>( p );
return ( unsigned short )( val ^ (val >> 16));
}
using namespace std;
int main() {
int a[20];
for ( int i = 0; i < 20; i++ )
cout << Hash( a + i ) << endl;
}
另外,static_cast和reinterpret_cast的差別主要在于多重繼承,比如
class A {
public:
int m_a;
};
class B {
public:
int m_b;
};
class C : public A, public B {};
那麼對于以下代碼:
C c;
printf("%p, %p, %p", &c, reinterpret_cast<B*>(&c), static_cast <B*>(&c));
前兩個的輸出值是相同的,最後一個則會在原基礎上偏移4個位元組,這是因為static_cast計算了父子類指針轉換的偏移量,并将之轉換到正确的位址(c裡面有m_a,m_b,轉換為B*指針後指到m_b處),而reinterpret_cast卻不會做這一層轉換。
是以, 你需要謹慎使用 reinterpret_cast。
3. c++強制轉換注意事項
- 新式轉換較舊式轉換更受歡迎。原因有二,一是新式轉型較易辨識,能簡化“找出類型系統在哪個地方被破壞”的過程;二是各轉型動作的目标愈窄化,編譯器愈能診斷出錯誤的運用。
- 盡量少使用轉型操作,尤其是dynamic_cast,耗時較高,會導緻性能的下降,盡量使用其他方法替代。