目錄 1. 隱式類型轉換 2. 顯示類型轉換/強制類型轉換( static_cast、const_cast、reinterpret_cast、dynamic_cast) 3. 類型轉換函數、轉換構造函數 類型轉換可分為 隱式類型轉換(編譯器自動完成) 與 顯示類型轉換(強制類型轉換,需要自己操作)。 ...
目錄
2. 顯示類型轉換/強制類型轉換( static_cast、const_cast、reinterpret_cast、dynamic_cast)
類型轉換可分為 隱式類型轉換(編譯器自動完成) 與 顯示類型轉換(強制類型轉換,需要自己操作)。
隱式類型轉換
基本數據類型之間會進行隱式的類型安全轉換。其轉換規則如下:
我們用 1個案列來介紹這種隱式類型轉換規則,會有意想不到的結果發生....!!!
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 int main() 7 { 8 short s = 'a'; 9 unsigned int ui = 1000; 10 int i = -2000; 11 double d = i; 12 13 cout << "d = " << d << endl; 14 cout << "ui = " << ui << endl; 15 cout << "ui + i = " << ui + i << endl; 16 17 if( (ui + i) > 0 ) // 變數i會進行隱式類型轉換 -> unsigned int // ui + i > 0 18 { 19 cout << "Positive" << endl; 20 } 21 else 22 { 23 cout << "Negative" << endl; 24 } 25 26 cout << "sizeof(s + 'b') = " << sizeof(s + 'b') << endl; // 4 // s -> int 'b' -> int // sizeof(s + 'b') = sizeof(int) // 編譯器預設是int運算 27 28 return 0; 29 } 30 /* 31 d = -2000 32 ui = 1000 33 ui + i = 4294966296 34 Positive 35 sizeof(s + 'b') = 4 36 */基本類型的隱式類型轉換案列
在c++中,還有其它幾種隱式類型轉換(後續講解),那麼現在試想一下這幾個問題:
1. 基本類型 可以轉換為 類類型嗎?--- 可以,見轉換構造函數;
2. 類類型 可以轉換為 基本類型嗎?--- 可以,見類型轉換函數;
3. 類類型之間可以轉換嗎?--- 可以,見轉換構造函數 和 類型轉換函數;
註:這種隱式類型轉換不能夠抑制,並且也是bug的來源之一;
顯示類型轉換/強制類型轉
在介紹c++強制類型轉換前,我們可以回顧一下c語言中的強制類型轉換。c語言中的強制類型轉換十分簡單、粗暴,即 (Type)(Expression);或者 Type(Expression);但是,這種簡單的強制類型轉換引發了很多問題,可歸納為如下2點:
1. 任意類型之間都可以進行轉換,編譯器很難判斷其正確性(過於粗暴);
2. 在源碼中無法快速定位所有使用強制類型轉換的語句(很難定位);
所以,基於這2點考慮,在c++中引入了新式類型轉換( static_cast、const_cast、reinterpret_cast、dynamic_cast),其使用方法可歸納為 xxx_cast<Type>(Expression) 。
1、static_cast
1. 用於 基本類型間的轉換
2. 不能用於基本類型指針間的轉換
3. 用於有繼承關係類對象之間的轉換和類指針之間的轉換
4. 用於 其它類型(基本類型和類類型) 向 類類型之間的轉換;static_cast<類類型>(其它類型),見轉換構造函數。
2、 const_cast
1. 用於去除變數的只讀屬性
2. 強制轉換的目標類型必須是指針或引用
3、 reinterpret_cast
1. 用於指針類型間的強制轉換
2. 用於整數和指針類型間的強制轉換
4、 dynamic_cast
1. 用於有繼承關係的類指針(引用)間的轉換
2. 用於有交叉關係的類指針(引用)間的轉換
3. 相關類(基類)中必須有虛函數的支持
4. 具有類型檢查的功能,但類型轉換的結果只可能在運行階段得到;
指針轉換:
轉換成功:得到目標類型的指針;
轉換失敗:得到一個空指針;
引用轉換:
轉換成功:得到目標類型的引用;
轉換失敗:得到一個異常操作的信息;
dynamic_cast 轉換時錯誤提示:
1. 不能將父類指針 直接 轉換為 子類指針
2. 在 父類中沒有虛函數,不能發生多態 polymorphic
1 #include <stdio.h> 2 3 void static_cast_demo() 4 { 5 int i = 0x12345; 6 char c = 'c'; 7 int* pi = &i; 8 char* pc = &c; 9 10 c = static_cast<char>(i); 11 pc = static_cast<char*>(pi); // error static_cast 不能用於基本類型指針之間 的轉換 12 } 13 14 void const_cast_demo() 15 { 16 const int& j = 1; 17 int& k = const_cast<int&>(j); 18 19 const int x = 2; 20 int& y = const_cast<int&>(x); 21 22 int z = const_cast<int>(x); // error const_cast 強制轉換的目標類型必須是指針或引用類型 23 24 k = 5; 25 26 printf("k = %d\n", k); // 5 27 printf("j = %d\n", j); // 5 28 29 y = 8; 30 31 printf("x = %d\n", x); // 2 32 printf("y = %d\n", y); // 8 33 printf("&x = %p\n", &x);// 0x7fffd40b84e8 34 printf("&y = %p\n", &y);// 0x7fffd40b84e8 35 } 36 37 void reinterpret_cast_demo() 38 { 39 int i = 0; 40 char c = 'c'; 41 int* pi = &i; 42 char* pc = &c; 43 44 pc = reinterpret_cast<char*>(pi); 45 pi = reinterpret_cast<int*>(pc); 46 pi = reinterpret_cast<int*>(i); 47 c = reinterpret_cast<char>(i); // error reinterpret_cast 適用於指針類型之間的轉換 和 整型與指針類型之間的轉換 48 } 49 50 void dynamic_cast_demo() 51 { 52 int i = 0; 53 int* pi = &i; 54 char* pc = dynamic_cast<char*>(pi); // error 55 } 56 57 int main() 58 { 59 static_cast_demo(); 60 const_cast_demo(); 61 reinterpret_cast_demo(); 62 dynamic_cast_demo(); 63 64 return 0; 65 }static_cast、const_cast、reinterpret_cast、dynamic_cast 初體驗
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Base 7 { 8 public: 9 Base() 10 { 11 cout << "Base::Base()" << endl; 12 } 13 14 virtual ~Base() 15 { 16 cout << "Base::~Base()" << endl; 17 } 18 }; 19 20 class Derived : public Base 21 { 22 public: 23 Derived() 24 { 25 cout << "Derived::Derived()" << endl; 26 } 27 28 void func() 29 { 30 cout << "Derived::func()" << endl; 31 } 32 33 virtual ~Derived() 34 { 35 cout << "Derived::~Derived()" << endl; 36 } 37 }; 38 39 void test1() 40 { 41 Base* bp = new Derived(); 42 43 Derived *dp = dynamic_cast<Derived*>(bp); // 當父類指針指向的是子類對象,轉換成功 44 //Derived *dp = static_cast<Derived*>(bp); // 當父類指針指向的是子類對象,轉換成功 45 if( dp != NULL ) 46 { 47 cout << "dp = " << dp << endl; 48 dp->func(); 49 } 50 else 51 { 52 cout << "Cast error!" << endl; 53 } 54 55 delete bp; 56 } 57 58 void test2() 59 { 60 Base* bp = new Base(); 61 62 Derived *dp = dynamic_cast<Derived*>(bp); // 轉化失敗,不能將父類指針對象轉換為子類指針對象 63 //Derived *dp = static_cast<Derived*>(bp); // 轉換成功,可以將父類指針對象轉換為子類指針對象 64 if( dp != NULL ) 65 { 66 cout << "dp = " << dp << endl; 67 dp->func(); 68 } 69 else 70 { 71 cout << "Cast error!" << endl; 72 } 73 74 delete bp; 75 } 76 77 int main() 78 { 79 test1(); 80 81 cout << "-----------------------" << endl; 82 83 test2(); 84 85 return 0; 86 }static_cast 與 dynamic_cast 測試案列
通過 《 static_cast 與 dynamic_cast 測試案列 》可知,當使用 dynamic_cast 強制類型轉換時,即 Derived *dp = dynamic_cast<Derived*>(bp); 程式的運行結果為:
當使用 static_cast 強制類型轉換時,即 Derived *dp = static_cast<Derived*>(bp); 程式的運行結果為:
從運行結果可知, static_cast 與 dynamic_cast 在繼承中進行類指針轉換時是存在差異的;其中,
1. 相同點:當父類指針 指向 子類對象時,二者都可以將父類指針 成功轉換為 子類指針;
2. 不同點:當父類指針 指向 父類對象時,
1) static_cast 轉換:可以將父類指針 成功轉換為 子類指針;
2)dynamic_cast 轉換:父類指針 不能夠轉換為 子類指針;
類型轉換函數
1、轉換構造函數
當構造函數只有1個參數 且 參數的類型是基本類型 或者是 其它類型時,就是轉換構造函數。其作用是將其他類型 轉換為 類類型;
編譯器儘力嘗試的結果是隱式類型轉換,隱式類型轉換是工程中bug的重要來源;
工程中通過explicit關鍵字杜絕隱式轉換,轉換構造函數被explicit修飾時只能進行顯示轉換;
轉換方式
1. static_ cast<ClassName >(value);
2. ClassName(value);
3. (ClassName)value; //不推薦
在類型轉換時調用轉換構造函數。
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Test 7 { 8 int mValue; 9 public: 10 Test() 11 { 12 mValue = 0; 13 } 14 15 explicit Test(int i) 16 { 17 mValue = i; 18 } 19 20 Test operator + (const Test& p) 21 { 22 Test ret(mValue + p.mValue); 23 24 return ret; 25 } 26 27 int value() 28 { 29 return mValue; 30 } 31 }; 32 33 int main() 34 { 35 Test t; 36 Test r; 37 38 // 隱式類型轉換 不加explicit關鍵字 39 //t = 5; // t = Test(5); 40 //r = t + 10; // r = t + Test(10); 41 42 t = static_cast<Test>(5); // t = Test(5); 43 r = t + static_cast<Test>(10); // r = t + Test(10); 44 45 cout << r.value() << endl; // 15 46 47 return 0; 48 }轉換構造函數之 基本類型 -> 類類型
2、類型轉換函數
c++ 中可以定義類型轉換函數,其作用是將 類類型 轉換為 其它類型;其語法格式為:
1 // 類型轉換函數語法格式 2 operator Type() 3 { 4 Type ret; 5 6 // ... 7 8 retuan Type; 9 }
編譯器能夠隱式的使用類型轉換函數
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Test 7 { 8 int mValue; 9 public: 10 Test(int i = 0) 11 { 12 mValue = i; 13 } 14 int value() 15 { 16 return mValue; 17 } 18 operator int () 19 { 20 return mValue; 21 } 22 }; 23 24 int main() 25 { 26 Test t(100); 27 int i = t; // 隱式的使用類型轉換函數 operator int () 28 29 cout << "t.value() = " << t.value() << endl; // t.value() = 100 30 cout << "i = " << i << endl; // i = 100 31 32 return 0; 33 }類型轉換函數之 類類型 -> 基本類型
3、類型轉換函數 VS 轉換構造函數
結論:
1. 類型轉換函數 與 轉換構造函數 具有同等的地位;
2. 無法抑制隱式的類型轉換函數調用,此時 類型轉換函數可能與轉換構造函數衝突(類類型之間的轉換);
3. 在類型轉換時 調用類型轉換函數 、轉換構造函數。
4. 工程中以Type toType()的公有成員 代替 類型轉換函數;
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Test; 7 8 class Value 9 { 10 public: 11 Value() 12 { 13 } 14 explicit Value(Test& t) 15 { 16 cout << "explicit Value(Test& t)" << endl; 17 } 18 }; 19 20 class Test 21 { 22 int mValue; 23 public: 24 Test(int i = 0) 25 { 26 mValue = i; 27 } 28 int value() 29 { 30 return mValue; 31 } 32 operator Value() 33 { 34 Value ret; 35 cout << "operator Value()" << endl; 36 return ret; 37 } 38 }; 39 40 int main() 41 { 42 Test t(100); 43 Value v1 = t; // 隱式的調用類型轉換函數 operator Value() 44 Value v2 = static_cast<Value>(t); // 顯示的調用轉換構造函數 explicit Value(Test& t) 45 46 return 0; 47 } 48 /* 49 運行結果: 50 operator Value() 51 explicit Value(Test& t) 52 */類類型之間的相互轉換
// 類型轉換函數與轉換構造函數發生衝突 的示意圖