在日常的編碼生活中,程序員時刻與變量的申明,初始化,賦值,運算交互。數據類型有很多種,在不同的編程場景中用到不同的數據類型。在不同類型的數據的交互中,難免遇到類型轉換。本文通過C++的類型轉換來探討一下數據類型轉換的方式,以及在編碼中遇到的問題。 程序代碼中充斥最多的就是隱式轉換。隱式轉換在程序編譯時編譯器根據編譯規則匹配的轉換方式。常見隱式轉換分為兩種方式,一種是表達式類型的,另一種是函數式的。 [cpp] int a = 1; float b = a; char c = 'a'; b = c + a; class A {}; class B{public: B(A* a){}}; A *a = new A(); B *b = a; 在隱式轉換過程中通常遵循如下條列: 1、在表達式裡,如果有比int型小的類型,一般都轉換為更大的類型 2、在初始化表達式裡,都將右邊的值轉換為初始化數據類型。在賦值表達式裡一般將右邊的值轉換為左邊值的類型。 3、在bool 表達式裡,將值轉換為bool類型 4、在有操作符算數表達式裡,不同的數據類型將被轉換為同一類型。 在有操作符的表達式裡,問題最多的就是unsigned 和signed 類型之間的轉換,當unsigned類型長度大於或者等於signed類型的時候,signed類型轉換unsigned類型。當兩種類型長度一樣的時候,signed類型如果是負數的話,這時候對這兩個變量進行運算的時候,值是沒意義的。 顯示轉換分為兩類,一種是C風格式的,另一種是函數式的。 [cpp] int num = 2; float PI = 3.14; num = (int)PI; num = int(PI); [cpp] #include "stdio.h" #include "string.h" typedef struct { int age; int* pFood ; }Dog; typedef struct{ int age; char* pName; }Duck; int main() { Duck duck; duck.pName = "Duck"; duck.age = 10; Dog* dog = (Dog*)&duck; printf("The dog age is:%d\n", dog->age); printf("The dog food is:%s\n", dog->pFood); return 0; } 例一中將float類型顯示的轉換為int類型。例二中將一個結構體轉換為另一種結構體。因此在在打印中就出現了違背常識的狗的食物變為鴨子。 隱式轉換的坑防不勝防,在隱式轉換上少出錯的方式只能是深入理解程序語言自己的編譯規則。知道的背後的秘密也多,就能更好的運用。對於顯示轉換, 是通過自己手動的去轉換數據類型,是建立在我們知道這種轉換方式是我們自己主動去轉換的。對於結果的話是可預測的,對於看代碼的人也明白發生了什麼。C++為何提出自己的轉換方式呢?因為顯示轉換方式,編譯階段是發現錯誤的。所以問題就遺留到運行時發生崩潰。因此C++引入了自己的三種類型方式。 動態轉換方式是通過動態模板函數的方式轉換類型。動態轉換方式只能轉換從在繼承關系的對象指針,或者引用。如果是從子類轉向父類,轉換結果是安全的。如果是父類轉向子類,且父類沒有虛函數的情況下,編譯錯誤。如果有虛函數,轉換結果為0。 [cpp] #include <iostream> using namespace std; class Base { public: int x; }; class Derived : public Base { public: int y; int z; }; int main() { Base* b = new Base(); b->x = 10; Derived* d = new Derived(); d->x = 20; d->y = 30; d->z = 40; Base *b1 = dynamic_cast<Base*>(d); // Derived* d1 = dynamic_cast<Base*>(b); cout<<"b1->x is:"<<b1->x<<endl; cout<<"b1->x address is:"<<&b1->x<<endl; cout<<"d->x address is:"<<&d->x<<endl; cout<<"d->y address is:"<<&d->y<<endl; cout<<"d->z address is:"<<&d->z<<endl; } [cpp] #include <iostream> #include <exception> using namespace std; class Base{ public: virtual void Phony() { } }; class Derived : public Base { public: int a; }; int main() { Base *b = new Base(); Derived *d = new Derived(); Base b1; try{ d = dynamic_cast<Derived*>(b); if(d==NULL){ cout<<"Error"<<endl; } }catch(exception& e) { cout<<"exception is :"<<e.what()<<endl; } return 0; } static_cast這種轉換方式在安全轉換的時候沒有問題,在非安全的轉換方式下,也能轉換,不過轉換的時候如果數據段不能填充目標轉換類型的時候,將會為不足的數據段填充。 [cpp] #include <iostream> #include <exception> using namespace std; class Base { public: int x; }; class Derived : public Base{ public: int y; int z; }; int main() { Base* b = new Base(); b->x = 10; Derived *d = new Derived(); d->y = 20; d->z = 30; try{ d = static_cast<Derived*>(b); cout<<"the d->x is:"<<d->x<<endl; cout<<"the d->y is:"<<d->y<<endl; cout<<"the d->z is:"<<d->z<<endl; }catch(exception& e){ cout<<"the exception is:"<<e.what()<<endl; } } reinterpret_cast這種轉換類型可以將任何類型的數據轉換為其他類型的數據。不做檢測,只是按內存數據布局將數據從一個數據塊按字節拷貝到另一個數據塊塊。這種轉換是不安全的。類似於c語言裡的memcpy函數。這種轉換方式及其不安全,所以不推薦用。 const_cast 這種轉換可以將常量轉換為動態變量返回。 [cpp] #include <iostream> #include <string.h> #include <exception> using namespace std; int main() { const char* str = "hello,world"; try{ char* newStr = const_cast<char*>(str); cout<<newStr<<endl; for(int i =0; i <strlen(newStr) ; ++i) { newStr[i] = 'x'; } cout<<newStr<<endl; }catch(exception& e) { cout<<"the exception is:"<<e.what()<<endl; } }