將類型名作為強制類型轉換運算符的做法是C語言的老式做法碘箍,C++ 為保持兼容而予以保留。
C++ 引入了四種功能不同的強制類型轉換運算符以進行強制類型轉換:static_cast鲸郊、reinterpret_cast丰榴、const_cast 和 dynamic_cast。
強制類型轉換是有一定風險的秆撮,有的轉換并不一定安全四濒,如把整型數值轉換成指針,把基類指針轉換成派生類指針,把一種函數指針轉換成另一種函數指針盗蟆,把常量指針轉換成非常量指針等戈二。C++ 引入新的強制類型轉換機制,主要是為了克服C語言強制類型轉換的以下三個缺點喳资。
- 沒有從形式上體現轉換功能和風險的不同觉吭。
例如,將 int 強制轉換成 double 是沒有風險的仆邓,而將常量指針轉換成非常量指針鲜滩,將基類指針轉換成派生類指針都是高風險的,而且后兩者帶來的風險不同(即可能引發(fā)不同種類的錯誤)节值,C語言的強制類型轉換形式對這些不同并不加以區(qū)分徙硅。
將多態(tài)基類指針轉換成派生類指針時不檢查安全性,即無法判斷轉換后的指針是否確實指向一個派生類對象搞疗。
難以在程序中尋找到底什么地方進行了強制類型轉換嗓蘑。
強制類型轉換是引發(fā)程序運行時錯誤的一個原因,因此在程序出錯時贴汪,可能就會想到是不是有哪些強制類型轉換出了問題脐往。
如果采用C語言的老式做法,要在程序中找出所有進行了強制類型轉換的地方扳埂,顯然是很麻煩的业簿,因為這些轉換沒有統(tǒng)一的格式。
而用 C++ 的方式阳懂,則只需要查找_cast
字符串就可以了梅尤。甚至可以根據錯誤的類型,有針對性地專門查找某一種強制類型轉換岩调。例如巷燥,懷疑一個錯誤可能是由于使用了 reinterpret_cast 導致的,就可以只查找reinterpret_cast
字符串号枕。
C++ 強制類型轉換運算符的用法如下:
強制類型轉換運算符 <要轉換到的類型> (待轉換的表達式)
例如:
double d = static_cast <double> (35); //將 35 的值轉換成實數
下面分別介紹四種強制類型轉換運算符缰揪。
static_cast
static_cast 用于進行比較“自然”和低風險的轉換,如整型和浮點型葱淳、字符型之間的互相轉換钝腺。另外,如果對象所屬的類重載了強制類型轉換運算符 T(如 T 是 int赞厕、int* 或其他類型名)艳狐,則 static_cast 也能用來進行對象到 T 類型的轉換。
static_cast 不能用于在不同類型的指針之間互相轉換皿桑,也不能用于整型和指針之間的互相轉換毫目,當然也不能用于不同類型的引用之間的轉換蔬啡。因為這些屬于風險比較高的轉換。
static_cast 用法示例如下:
1. #include <iostream>
2. u[sin](http://c.biancheng.net/ref/sin.html)g namespace std;
3. class A
4. {
5. public:
6. operator int() { return 1; }
7. operator char*() { return NULL; }
8. };
9. int main()
10. {
11. A a;
12. int n;
13. char* p = "New Dragon Inn";
14. n = static_cast <int> (3.14); // n 的值變?yōu)?3
15. n = static_cast <int> (a); //調用 a.operator int镀虐,n 的值變?yōu)?1
16. p = static_cast <char*> (a); //調用 a.operator char*箱蟆,p 的值變?yōu)?NULL
17. n = static_cast <int> (p); //編譯錯誤,static_cast不能將指針轉換成整型
18. p = static_cast <char*> (n); //編譯錯誤粉私,static_cast 不能將整型轉換成指針
19. return 0;
20. }
reinterpret_cast
reinterpret_cast 用于進行各種不同類型的指針之間顽腾、不同類型的引用之間以及指針和能容納指針的整數類型之間的轉換。轉換時诺核,執(zhí)行的是逐個比特復制的操作抄肖。
這種轉換提供了很強的靈活性,但轉換的安全性只能由程序員的細心來保證了窖杀。例如漓摩,程序員執(zhí)意要把一個 int* 指針、函數指針或其他類型的指針轉換成 string* 類型的指針也是可以的入客,至于以后用轉換后的指針調用 string 類的成員函數引發(fā)錯誤管毙,程序員也只能自行承擔查找錯誤的煩瑣工作:(C++ 標準不允許將函數指針轉換成對象指針,但有些編譯器桌硫,如 Visual Studio 2010夭咬,則支持這種轉換)。
reinterpret_cast 用法示例如下:
1. #include <iostream>
2. using namespace std;
3. class A
4. {
5. public:
6. int i;
7. int j;
8. A(int n):i(n),j(n) { }
9. };
10. int main()
11. {
12. A a(100);
13. int &r = reinterpret_cast<int&>(a); //強行讓 r 引用 a
14. r = 200; //把 a.i 變成了 200
15. cout << a.i << "," << a.j << endl; // 輸出 200,100
16. int n = 300;
17. A *pa = reinterpret_cast<A*> ( & n); //強行讓 pa 指向 n
18. pa->i = 400; // n 變成 400
19. pa->j = 500; //此條語句不安全铆隘,很可能導致程序崩潰
20. cout << n << endl; // 輸出 400
21. long long la = 0x12345678abcdLL;
22. pa = reinterpret_cast<A*>(la); //la太長卓舵,只取低32位0x5678abcd拷貝給pa
23. unsigned int u = reinterpret_cast<unsigned int>(pa);//pa逐個比特拷貝到u
24. cout << hex << u << endl; //輸出 5678abcd
25. typedef void (* PF1) (int);
26. typedef int (* PF2) (int,char *);
27. PF1 pf1; PF2 pf2;
28. pf2 = reinterpret_cast<PF2>(pf1); //兩個不同類型的函數指針之間可以互相轉換
29. }
程序的輸出結果是:
200, 100
400
5678abed
第 19 行的代碼不安全,因為在編譯器看來膀钠,pa->j 的存放位置就是 n 后面的 4 個字節(jié)掏湾。 本條語句會向這 4 個字節(jié)中寫入 500。但這 4 個字節(jié)不知道是用來存放什么的肿嘲,貿然向其中寫入可能會導致程序錯誤甚至崩潰融击。
上面程序中的各種轉換都沒有實際意義,只是為了演示 reinteipret_cast 的用法而已雳窟。在編寫黑客程序尊浪、病毒或反病毒程序時,也許會用到這樣怪異的轉換封救。
reinterpret_cast體現了 C++ 語言的設計思想:用戶可以做任何操作拇涤,但要為自己的行為負責。
const_cast
const_cast 運算符僅用于進行去除 const 屬性的轉換兴泥,它也是四個強制類型轉換運算符中唯一能夠去除 const 屬性的運算符。
將 const 引用轉換為同類型的非 const 引用虾宇,將 const 指針轉換為同類型的非 const 指針時可以使用 const_cast 運算符搓彻。例如:
1. const string s = "Inception";
2. string& p = const_cast <string&> (s);
3. string* ps = const_cast <string*> (&s); // &s 的類型是 const string*
dynamic_cast
用 reinterpret_cast 可以將多態(tài)基類(包含虛函數的基類)的指針強制轉換為派生類的指針,但是這種轉換不檢查安全性,即不檢查轉換后的指針是否確實指向一個派生類對象旭贬。dynamic_cast專門用于將多態(tài)基類的指針或引用強制轉換為派生類的指針或引用怔接,而且能夠檢查轉換的安全性。對于不安全的指針轉換稀轨,轉換結果返回 NULL 指針扼脐。
dynamic_cast 是通過“運行時類型檢查”來保證安全性的。dynamic_cast 不能用于將非多態(tài)基類的指針或引用強制轉換為派生類的指針或引用——這種轉換沒法保證安全性奋刽,只好用 reinterpret_cast 來完成瓦侮。
dynamic_cast 示例程序如下:
1. #include <iostream>
2. #include <string>
3. using namespace std;
4. class Base
5. { //有虛函數,因此是多態(tài)基類
6. public:
7. virtual ~Base() {}
8. };
9. class Derived : public Base { };
10. int main()
11. {
12. Base b;
13. Derived d;
14. Derived* pd;
15. pd = reinterpret_cast <Derived*> (&b);
16. if (pd == NULL)
17. //此處pd不會為 NULL佣谐。reinterpret_cast不檢查安全性肚吏,總是進行轉換
18. cout << "unsafe reinterpret_cast" << endl; //不會執(zhí)行
19. pd = dynamic_cast <Derived*> (&b);
20. if (pd == NULL) //結果會是NULL,因為 &b 不指向派生類對象狭魂,此轉換不安全
21. cout << "unsafe dynamic_cast1" << endl; //會執(zhí)行
22. pd = dynamic_cast <Derived*> (&d); //安全的轉換
23. if (pd == NULL) //此處 pd 不會為 NULL
24. cout << "unsafe dynamic_cast2" << endl; //不會執(zhí)行
25. return 0;
26. }
程序的輸出結果是:
unsafe dynamic_cast1
第 20 行罚攀,通過判斷 pd 的值是否為 NULL,就能知道第 19 行進行的轉換是否是安全的雌澄。第 23 行同理斋泄。
如果上面的程序中出現了下面的語句:
Derived & r = dynamic_cast <Derived &> (b);
那該如何判斷該轉換是否安全呢?不存在空引用镐牺,因此不能通過返回值來判斷轉換是否安全炫掐。C++ 的解決辦法是:dynamic_cast 在進行引用的強制轉換時,如果發(fā)現轉換不安全任柜,就會拋出一個異常卒废,通過處理異常,就能發(fā)現不安全的轉換宙地。