C++中四種類型轉換以及const_cast是否能改變常量的問題
we have four specific casting operators:dynamic_cast, reinterpret_cast, static_cast and const_cast. Their format is to follow the new type enclosed between angle-brackets (<>) and immediately after, the expression to be converted between parentheses.
dynamic_cast <new_type> (expression)
reinterpret_cast <new_type> (expression)
static_cast <new_type> (expression)
const_cast <new_type> (expression)
一、對C++中四種類型轉換總結如下:
const_cast<T>(expr)
用來移除物件的常量性(cast away the constness)
const_cast一般用於指標或者引用
使用const_cast去除const限定的目的不是為了修改它的內容
使用const_cast去除const限定,通常是為了函式能夠接受這個實際引數
static_cast <T>(expr)
編譯器隱式執行的任何型別轉換都可以由static_cast完成
當一個較大的算術型別賦值給較小的型別時,可以用static_cast進行強制轉換。
可以將void*指標轉換為某一型別的指標
可以將基類指標強制轉換為派生類指標,但是不安全。
無法將const轉化為nonconst,這個只有const_cast才可以辦得到
reinterpret_cast<T>(expr)
“通常為運算元的位模式提供較低層的重新解釋”也就是說將資料以二進位制存在形式的重新解釋。
int i;
char *p = "This is a example." ;
i = reinterpret_cast<int>(p);
//此時結果,i與p的值是完全相同的。
int *ip
char *pc = reinterpret_cast<char*>(ip);
// 程式設計師需要記得pc所指向的真實物件是int型,並非字串。
// 如果將pc當作字元指標進行操作,可能會造成執行時錯誤
// 如int len = strlen(pc);
多重繼承時reinterpret_cast不安全。static_cast會根據父子類指標轉換的偏移量轉換到正確地址,而reinterpret_cast不會。
#include <cassert>
#include <iostream>
using namespace std;
class A
{
public:
int m_a;
};
class B
{
public:
int m_B;
};
class C: public A, public B
{
};
int main(void)
{
C c;
cout << (void *)&c << endl;
cout << (void *)reinterpret_cast<B *>(&c) << endl;
cout << (void *)static_cast<B *>(&c) << endl;
return 0;
}
dynamic_cast<T>(expr)
執行“安全向下”轉型操作,也就是說支援執行時識別指標或所指向的物件,這是唯一個無法用舊式語來進行的轉型操作。
dynamic_cast可謂是最嚴格的轉換,static_cast次之,而reinterpret_cast則是最寬鬆的。如果你遇到不能將整型轉變為函式指標的問題,你可以這樣解決:
reinterpret_cast<LPFUN&>(nAddress);
注意LPFUN這裡有個“&”符號,表示引用,C++的引用其實就是用指標實現的,而這些“轉換”其實都是指標的轉換,所以加上引用符號編譯才能通過。
二、也許大家都有過這樣的疑惑:const_cast可以去除一個常量的const屬性,去除const屬性後應該可以對“常量”進行修改,通過偵錯程式發現記憶體中的值是被改變的,可是再傳遞這個“常量”的時候,值卻一直保持原狀,實在古怪,在Windows下用VC、嘗試如此,在Linux下用g++嘗試也如此,我原先以為和編譯器的優化選項有關係,把所有優化選項關閉,照樣沒用,為什麼?
寫了個程式進行測試:
#include <iostream>
using namespace std;
void Fun(int &value)
{
cout << "Fun(val) = " << value << endl;
}
int main(void)
{
const int val = 100;
int *ptr = const_cast<int *>(&val);
*ptr = 200;
cout << &val << endl;
cout << ptr << endl;
cout << "val = " << val << endl;
cout << "*ptr = " << *ptr << endl;
int &ref = const_cast<int &>(val);
ref = 300;
cout << "val = " << val << endl;
cout << "ref = " << ref << endl;
Fun(const_cast<int &>(val));
return 0;
}
輸出為:
可以看出列印的地址是一樣的,而且奇怪的是val還是等於100,而通過*ptr打印出來的卻是更改後的200,再者Fun函式列印的是300,即被引用再次修改了一次,在列印語句附近設定斷點並除錯反彙編,擷取一段如下圖:
可以明顯地看出系統是對val這個const進行了預處理般的替換,將它替換成“64h”(十六進位制的64就是十進位制的100),即在編譯生成的指令中val就已經被替換成100了,其實加const只是告訴編譯器不能修改而不是真正地不可修改,如果程式設計師不注意而去修改了它會報錯,現在我們利用const_cast去除了常量性,然後通過指標和引用對其進行了修改,所以通過指標列印或者引用傳參的時候就能看出其記憶體確實變化了,但為了保護val這個變數本來的const特性,所以每次我們使用val時,系統都將其替換成初始值100,確保了val還是“不可變”的。記住,只有當const限定符加在已經初始化的全域性變數前面的時候,此時變數處在.rodata段(linux下),才是真正的不可修改,否則通過指標都是可以修改的,雖然編譯過程中會產生警告。
在linux下測試也是同樣的輸出結果:
附錄:
MSDN上關於四種cast的說明:
reinterpret_cast Operator
The reinterpret_cast operator allows any pointer to be converted into any other pointer type. It also allows any integral type to be converted into any pointer type and vice versa. Misuse of the reinterpret_cast operator can easily be unsafe. Unless the desired conversion is inherently low-level, you should use one of the other cast operators.
dynamic_cast Operator
The expression dynamic_cast( expression ) converts the operand expression to an object of type type-id. The type-id must be a pointer or a reference to a previously defined class type or a“pointer to void”. The type of expression must be a pointer if type-id is a pointer, or an l-value if type-id is a reference.
static_cast Operator
The expression static_cast < type-id > ( expression ) converts expression to the type of type-id based solely on the types present in the expression. No run-time type check is made to ensure the safety of the conversion.
const_cast operator
The const_cast operator can be used to remove the const, volatile, and __unaligned attribute(s) from a class.