轉載自:http://www.weixueyuan.net/view/6329.htmlhtml
在C++語言中新增了四個關鍵字static_cast、const_cast、reinterpret_cast和dynamic_cast。這四個關鍵字都是用於強制類型轉換的。咱們逐一來介紹這四個關鍵字。ios
在C++語言中static_cast用於數據類型的強制轉換,強制將一種數據類型轉換爲另外一種數據類型。例如將整型數據轉換爲浮點型數據。
[例1]C語言所採用的類型轉換方式:數組
int a = 10; int b = 3; double result = (double)a / (double)b;
例1中將整型變量a和b轉換爲雙精度浮點型,而後相除。在C++語言中,咱們能夠採用static_cast關鍵字來進行強制類型轉換,以下所示。
[例2]static_cast關鍵字的使用:安全
int a = 10; int b = 3; double result = static_cast<double>(a) / static_cast<double>(b);
在本例中一樣是將整型變量a轉換爲雙精度浮點型。採用static_cast進行強制數據類型轉換時,將想要轉換成的數據類型放到尖括號中,將待轉換的變量或表達式放在元括號中,其格式能夠歸納爲以下形式:
static_cast <類型說明符> (變量或表達式)函數
在C語言中,const限定符一般被用來限定變量,用於表示該變量的值不能被修改。而const_cast則正是用於強制去掉這種不能被修改的常數特性,但須要特別注意的是const_cast不是用於去除變量的常量性,而是去除指向常數對象的指針或引用的常量性,其去除常量性的對象必須爲指針或引用。
[例3]一個錯誤的例子:測試
const int a = 10; const int * p = &a; *p = 20; //compile error int b = const_cast<int>(a); //compile error
在本例中出現了兩個編譯錯誤,第一個編譯錯誤是*p由於具備常量性,其值是不能被修改的;另外一處錯誤是const_cast強制轉換對象必須爲指針或引用,而例3中爲一個變量,這是不容許的!
[例4]const_cast關鍵字的使用spa
#include<iostream> using namespace std; int main() { const int a = 10; const int * p = &a; int *q; q = const_cast<int *>(p); *q = 20; //fine cout <<a<<" "<<*p<<" "<<*q<<endl; cout <<&a<<" "<<p<<" "<<q<<endl; return 0; }
在本例中,咱們將變量a聲明爲常量變量,同時聲明瞭一個const指針指向該變量(此時若是聲明一個普通指針指向該常量變量的話是不容許的,Visual Studio 2010編譯器會報錯),以後咱們定義了一個普通的指針*q。將p指針經過const_cast去掉其常量性,並賦給q指針。以後我再修改q指針所指地址的值時,這是不會有問題的。
最後將結果打印出來,運行結果以下:
10 20 20
002CFAF4 002CFAF4 002CFAF4
查看運行結果,問題來了,指針p和指針q都是指向a變量的,指向地址相同,並且通過調試發現002CFAF4地址內的值確實由10被修改爲了20,這是怎麼一回事呢?爲何a的值打印出來仍是10呢?
其實這是一件好事,咱們要慶幸a變量最終的值沒有變成20!變量a一開始就被聲明爲一個常量變量,無論後面的程序怎麼處理,它就是一個常量,就是不會變化的。試想一下若是這個變量a最終變成了20會有什麼後果呢?對於這些簡短的程序而言,若是最後a變成了20,咱們會一眼看出是q指針修改了,可是一旦一個項目工程很是龐大的時候,在程序某個地方出現了一個q這樣的指針,它能夠修改常量a,這是一件很可怕的事情的,能夠說是一個程序的漏洞,畢竟將變量a聲明爲常量就是不但願修改它,若是後面能修改,這就太恐怖了。
在例4中咱們稱「*q=20」語句爲未定義行爲語句,所謂的未定義行爲是指在標準的C++規範中並無明確規定這種語句的具體行爲,該語句的具體行爲由編譯器來自行決定如何處理。對於這種未定義行爲的語句咱們應該儘可能予以免!
從例4中咱們能夠看出咱們是不想修改變量a的值的,既然如此,定義一個const_cast關鍵字強制去掉指針的常量性到底有什麼用呢?咱們接着來看下面的例子。
例5:操作系統
#include<iostream> using namespace std; const int * Search(const int * a, int n, int val); int main() { int a[10] = {0,1,2,3,4,5,6,7,8,9}; int val = 5; int *p; p = const_cast<int *>(Search(a, 10, val)); if(p == NULL) cout<<"Not found the val in array a"<<endl; else cout<<"hvae found the val in array a and the val = "<<*p<<endl; return 0; } const int * Search(const int * a, int n, int val) { int i; for(i=0; i<n; i++) { if(a[i] == val) return &a[i]; } return NULL; }
在例5中咱們定義了一個函數,用於在a數組中尋找val值,若是找到了就返回該值的地址,若是沒有找到則返回NULL。函數Search返回值是const指針,當咱們在a數組中找到了val值的時候,咱們會返回val的地址,最關鍵的是a數組在main函數中並非const,所以即便咱們去掉返回值的常量性有可能會形成a數組被修改,可是這也依然是安全的。
對於引用,咱們一樣能使用const_cast來強制去掉常量性,如例6所示。
例6:.net
#include<iostream> using namespace std; const int & Search(const int * a, int n, int val); int main() { int a[10] = {0,1,2,3,4,5,6,7,8,9}; int val = 5; int &p = const_cast<int &>(Search(a, 10, val)); if(p == NULL) cout<<"Not found the val in array a"<<endl; else cout<<"hvae found the val in array a and the val = "<<p<<endl; return 0; } const int & Search(const int * a, int n, int val) { int i; for(i=0; i<n; i++) { if(a[i] == val) return a[i]; } return NULL; }
瞭解了const_cast的使用場景後,能夠知道使用const_cast一般是一種無奈之舉,同時也建議你們在從此的C++程序設計過程當中必定不要利用const_cast去掉指針或引用的常量性而且去修改原始變量的數值,這是一種很是很差的行爲。設計
在C++語言中,reinterpret_cast主要有三種強制轉換用途:改變指針或引用的類型、將指針或引用轉換爲一個足夠長度的整形、將整型轉換爲指針或引用類型。在使用reinterpret_cast強制轉換過程僅僅只是比特位的拷貝,所以在使用過程當中須要特別謹慎!
例7
int *a = new int; double *d = reinterpret_cast<double *>(a);
在C++中,編譯期的類型轉換有可能會在運行時出現錯誤,特別是涉及到類對象的指針或引用操做時,更容易產生錯誤。Dynamic_cast操做符則能夠在運行期對可能產生問題的類型轉換進行測試。
#include<iostream> using namespace std; class base { public : void m(){cout<<"m"<<endl;} }; class derived : public base { public: void f(){cout<<"f"<<endl;} }; int main() { derived * p; p = new base; p = static_cast<derived *>(new base); p->m(); p->f(); return 0; }
本例中定義了兩個類:base類和derived類,這兩個類構成繼承關係。在base類中定義了m函數,derived類中定義了f函數。在前面介紹多態時,咱們一直是用基類指針指向派生類或基類對象,而本例則不一樣了。本例主函數中定義的是一個派生類指針,當咱們將其指向一個基類對象時,這是錯誤的,會致使編譯錯誤。可是經過強制類型轉換咱們能夠將派生類指針指向一個基類對象,p = static_cast<derived *>(new base);語句實現的就是這樣一個功能,這樣的一種強制類型轉換時合乎C++語法規定的,可是是很是不明智的,它會帶來必定的危險。在程序中p是一個派生類對象,咱們將其強制指向一個基類對象,首先經過p指針調用m函數,由於基類中包含有m函數,這一句沒有問題,以後經過p指針調用f函數。通常來說,由於p指針是一個派生類類型的指針,而派生類中擁有f函數,所以p->f();這一語句不會有問題,可是本例中p指針指向的確實基類的對象,而基類中並無聲明f函數,雖然p->f();這一語句雖然仍沒有語法錯誤,可是它卻產生了一個運行時的錯誤。換言之,p指針是派生類指針,這代表程序設計人員能夠經過p指針調用派生類的成員函數f,可是在實際的程序設計過程當中卻誤將p指針指向了一個基類對象,這就致使了一個運行期錯誤。
產生這種運行期的錯誤緣由在於static_cast強制類型轉換時並不具備保證類型安全的功能,而C++提供的dynamic_cast卻能解決這一問題,dynamic_cast能夠在程序運行時檢測類型轉換是否類型安全。固然dynamic_cast使用起來也是有條件的,它要求所轉換的操做數必須包含多態類類型(即至少包含一個虛函數的類)。
例2:
#include<iostream> using namespace std; class base { public : void m(){cout<<"m"<<endl;} }; class derived : public base { public: void f(){cout<<"f"<<endl;} }; int main() { derived * p; p = new base; p = dynamic_cast<derived *>(new base); p->m(); p->f(); return 0; }
在本例中利用dynamic_cast進行強制類型轉換,可是由於base類中並不存在虛函數,所以p = dynamic_cast<derived *>(new base);這一句會編譯錯誤。dynamic_cast可否正確轉換與目標類型是否爲多態類類型無關,dynamic_cast要求被轉換的類型必須爲多態類類型。爲了解決本例中的語法錯誤,咱們能夠將base類中的函數m聲明爲虛函數,virtual void m(){cout<<"m"<<endl;}。
dynamic_cast還要求<>內部所描述的目標類型必須爲指針或引用。如例3所示,若是咱們將例2中的主函數換成例3的形式,這也是沒法經過編譯的。
例3:
int main() { base b; dynamic_cast<derived>(b); return 0; }
咱們來看一下正確使用dynamic_cast的代碼。
例4:
#include<iostream> using namespace std; class base { public : virtual void m(){cout<<"m"<<endl;} }; class derived : public base { public: void f(){cout<<"f"<<endl;} }; int main() { derived * p; p = dynamic_cast<derived *>(new base); if(p) { p->m(); p->f(); } else cout<<"Convert not safe!"<<endl; return 0; }
在本例中經過dynamic_cast來初始化指針p,在初始化過程當中dynamic_cast會檢測操做數new base轉換爲目標類型derived *是否能保證類型安全,若是類型安全則將new base結果賦給p指針,不然返回0,也即false。而本例中是要用基類對象地址去初始化派生類指針,這顯然是沒法保證類型安全的,所以p最後獲得的返回值是0。在主函數中通過判斷語句,最終程序輸出「Convert not safe!」。
Dynamic_cast轉換有本身的規則,下面將經過示例來介紹轉換規則。
例4:
#include<iostream> using namespace std; class base { public : virtual void m(){cout<<"m"<<endl;} }; class derived : public base { public: virtual void f(){cout<<"f"<<endl;} }; int main() { derived * d; d = dynamic_cast<derived *>(new base); if(d) { cout<<"Base to Derived is ok"<<endl; delete d; } else cout<<"Base to Derived is error"<<endl; base * b; b = dynamic_cast<base *>(new derived); if(b) { cout<<"Derived to Base is ok"<<endl; delete b; } else cout<<"Derived to Base is error"<<endl; return 0; }
本例分別定義了兩個類:base類和derived類,這兩個類構成繼承關係,爲了測試dynamic_cast轉換規則,咱們在類中各自定義了一個虛函數。在本例的主函數中咱們分別測試基類轉換爲派生類和派生類轉換爲基類時dynamic_cast轉換返回值。本例最終運行結果以下:
Base to Derived is error
Derived to Base is ok
從結果能夠看出從不能將指向基類對象的指針轉換爲指向派生類對象的指針,可是能夠將指向派生類對象的指針轉換爲指向基類對象的指針。
例5:
#include<iostream> using namespace std; class A { public : virtual void m(){cout<<"m"<<endl;} }; class B { public: virtual void f(){cout<<"f"<<endl;} }; int main() { A * a; a = dynamic_cast<A *>(new B); if(a) { cout<<"B to A is ok"<<endl; delete a; } else cout<<"B to A is error"<<endl; B * b; b = dynamic_cast<B *>(new A); if(b) { cout<<"A to B is ok"<<endl; delete b; } else cout<<"A to B is error"<<endl; return 0; }