C++ 類型轉換
在C語言裏用到的類型轉換方式。一般都是用強制類型轉換,語法:(類型說明符)(表達式),比如: (float)a 把a轉換為實型,(int)(x+y) 把x+y的結果轉換為整型。
C語言這樣的賦值時的類型轉換形式可能會使人感到不精密和不嚴格,由於無論表達式的值如何,系統都自己主動將其轉為賦值運算符左部變量的類型。
C++類型轉換
const_cast,字面上理解就是去const屬性。
static_cast。命名上理解是靜態類型轉換。
如int轉換成char。
dynamic_cast,命名上理解是動態類型轉換。
如子類和父類之間的多態類型轉換。
reinterpret_cast,只又一次解釋類型,但沒有進行二進制的轉換。
const_cast:
該運算符用來改動類型的const或volatile屬性。
除了const 或volatile修飾之外。 type_id和expression的類型是一樣的。
#include <stdio.h> using namespace std; struct Data{ int value; }; int main(int argc, _TCHAR* argv[]) { const Data data1 = {10}; //data1.value = 15; //error C3892: “data1”: 不能給常量賦值 Data &data2 = const_cast<Data &>(data1); data2.value = 20; printf("data1.value = %d data2.value = %d\n",data1.value,data2.value); const int a = 10; int *b = const_cast<int*>(&a); *b = 20; printf("a = %d b == &a is %d\n",a,b == &a); system("pause"); return 0; }
dynamic_cast
有條件轉換,動態類型轉換,執行時類型安全檢查(轉換失敗返回NULL):
1. 安全的基類和子類之間轉換。
2. 必需要有虛函數。
3. 同樣基類不同子類之間的交叉轉換。但結果是NULL。
class BaseClass { public: int m_iNum; virtualvoid foo(){}; //基類必須有虛函數。保持多臺特性才幹使用dynamic_cast }; class DerivedClass: public BaseClass { public: char*m_szName[100]; void bar(){}; }; BaseClass* pb =new DerivedClass(); DerivedClass *pd1 = static_cast<DerivedClass *>(pb); //子類->父類。靜態類型轉換。正確但不推薦 DerivedClass *pd2 = dynamic_cast<DerivedClass *>(pb); //子類->父類,動態類型轉換,正確 BaseClass* pb2 =new BaseClass(); DerivedClass *pd21 = static_cast<DerivedClass *>(pb2); //父類->子類。靜態類型轉換,危急!訪問子類m_szName成員越界 DerivedClass *pd22 = dynamic_cast<DerivedClass *>(pb2); //父類->子類,動態類型轉換,安全的。結果是NULL
static_cast:
①用於類層次結構中基類(父類)和派生類(子類)之間指針或引用的轉換。
進行上行轉換(把派生類的指針或引用轉換成基類表示)是安全的。
進行下行轉換(把基類指針或引用轉換成派生類表示)時。因為沒有動態類型檢查,所以是不安全的。
②用於基本數據類型之間的轉換。如把int轉換成char,把int轉換成enum。
這樣的轉換的安全性也要開發者來保證。
③把空指針轉換成目標類型的空指針。(int* 轉 long* error C2440: “static_cast”: 無法從“int *”轉換為“long *”)
④把不論什麽類型的表達式轉換成void類型。
reinterpret_cast:
reinterpret_cast<type-id> (expression)
type-id 必須是一個指針、引用、算術類型、函數指針或者成員指針。它能夠把一個指針轉換成一個整數,也能夠把一個整數轉換成一個指針(先把一個指針轉換成一個整數。再把該整數轉換成原類型的指針。還能夠得到原先的指針值)
主要是將原地址的值又一次強制定義為新類型。
reinterpret_cast與static_cast的類型轉換比較:
#include <stdio.h> using namespace std; class A { public: int m_a; }; class B { public: int m_b; }; class C:public A,public B { }; int main(int argc, _TCHAR* argv[]) { C* c = new C(); c->m_a = 1; c->m_b = 2; A* a1 = static_cast<A*>(c); A* a2 = reinterpret_cast<A*>(c); B* b1 = static_cast<B*>(c); B* b2 = reinterpret_cast<B*>(c); printf("c = %p,a1 = %p,b1 = %p\n",c,a1,b1); printf("c = %p,a2 = %p,b2 = %p\n",c,a2,b2); printf("a1.m_a = %d, b1.m_b = %d\n",a1->m_a,b1->m_b); printf("a2.m_a = %d, b2.m_b = %d\n",a2->m_a,b2->m_b); system("pause"); return 0; }
執行結果:
c = 00392950,a1 = 00392950,b1 = 00392954 c = 00392950,a2 = 00392950,b2 = 00392950 a1.m_a = 1, b1.m_b = 2 a2.m_a = 1, b2.m_b = 1
由執行結果能夠非常明顯地看到,使用reinterpret_cast進行強制轉換的時候b2直接是從c的地址開始,將c轉換成了b2,而使用static_cast的時候。b1是進行了一個sizeof(A)的一個偏移量的位移,也就是說在多重繼承的情況下,將子類的對象指針轉換成基類對象指針,使用static_cast才是安全的。reinterpret_cast的目的是將類型重定義,除非你非常明白地希望指向的那一塊內存是希望定義到你要轉換的目標,否則慎用。
reinterpret_cast的一個使用樣例:
#include <stdio.h> using namespace std; typedef void(*FuncPtr)(); typedef int(*FuncPtr2)(); void fun1() { } int fun2() { return 10; } int main(int argc, _TCHAR* argv[]) { FuncPtr fun[10]; fun[0] = &fun1; //fun[1] = &fun2; //error C2440: “=”: 無法從“int (__cdecl *)(void)”轉換為“FuncPtr” //fun[1] = static_cast<FuncPtr>(&fun2); //error C2440: “static_cast”: 無法從“int (__cdecl *)(int)”轉換為“FuncPtr” fun[1] = reinterpret_cast<FuncPtr>(&fun2); //用強制轉換將int fun 轉為 void fun 保存起來 FuncPtr2* pfun = reinterpret_cast<FuncPtr2*>(&fun[1]); //在須要使用的時候,再轉換回來 printf("run pfun = %d\n",(*pfun)()); //打印得到 "run pfun =10" system("pause"); return 0; }
C++ 類型轉換