C++類型轉換

C++類型轉換主要分爲兩種:隱式類型轉換、顯式類型轉換(強制類型轉換)。html

【1】隱式類型轉換ios

所謂隱式類型轉換,是指不須要用戶干預,編譯器默認進行的類型轉換行爲(不少時候用戶可能都不知道到底進行了哪些轉換)。express

隱式類型轉換通常分爲兩種:內置數據類型、自定義數據類型。安全

[1.1] 內置數據類型(基本數據類型)函數

例1:混合類型的算術運算表達式中測試

1     int nValue = 8;
2     double dValue = 10.7;
3     double dSum = nValue + dValue; // nValue會被自動轉換爲double類型,用轉換的結果再與dValue相加

 例2:不一樣類型的賦值操做時spa

1     int nValue = true; // bool類型被轉換爲int類型

 例3:函數參數傳值時指針

1     void func(double dArg); // 聲明函數
2     func(1); // 調用函數。整型數值1被轉換爲double類型數值1.0

 例4:函數返回值時code

1 double add(int na, int nb)
2 {
3     return (na + nb); // 運算結果會被隱式轉換爲double類型返回
4 }

以上各類狀況的隱式類型轉換,都知足了一個基本原則:由低精度向高精度的轉換。htm

若不知足該原則,編譯器會提示編譯警告。以下:

1     double dValue = 100.2;
2     int nValue = dValue; // : warning C4244: 「初始化」: 從「double」轉換到「int」,可能丟失數據

固然,這時咱們若不想看到警告,能夠選擇顯式類型轉換(又稱強制類型轉換)。以下:

1     double dValue = 100.2;
2     int nValue = (int)dValue;

[1.2] 自定義數據類型

隱式類型轉換的風險通常存在於自定義類型轉換間。尤爲須要注意自定義類的構造函數。例如:

 1 class MyString
 2 {
 3 public:
 4     MyString(int n) {}; // 本意:預先分配n個字節給字符串
 5     MyString(const char* p) {}; // 用C風格的字符串p做爲初始化值 
 6     // ......
 7 };
 8 
 9 void main()
10 {
11     MyString s1 = "China"; //OK 隱式轉換,等價於MyString s1 = MyString(」China」) 
12     MyString s2(10); // OK 分配10個字節的空字符串
13     MyString s3 = MyString(10); // OK 分配10個字節的空字符串
14 
15     MyString s4 = 10; // OK,編譯經過。也是分配10個字節的空字符串
16     MyString s5 = 'A'; // 編譯經過。分配int('A')個字節的空字符串
17     // s4 和s5 分別把一個int型和char型,隱式轉換成了分配若干字節的空字符串,容易使人誤解。
18 }

 如上例,要想禁止此種隱式類型轉換,可使用C++關鍵字explicit(詳細請參見隨筆《explicit關鍵字》)。

【2】顯式類型轉換(強制類型轉換)

四種強制類型轉換操做符:static_cast、const_cast、dynamic_cast、reinterpret_cast

[2.1] static_cast

(1)主要用於內置數據類型之間的相互轉換。

1     double dValue = 12.12;
2     float fValue = 3.14; // VS2013 warning C4305: 「初始化」: 從「double」到「float」截斷
3     int nDValue = static_cast<int>(dValue); // 12
4     int nFValue = static_cast<int>(fValue); // 3

 (2)也能夠轉換自定義類型。若是涉及到類,static_cast只能在有相互聯繫(繼承)的類型間進行轉換,且不必定包含虛函數

 1 class A
 2 {};
 3 
 4 class B : public A
 5 {};
 6 
 7 class C
 8 {};
 9 
10 void main()
11 {
12     A *pA = new A;
13     B *pB = static_cast<B*>(pA); // 編譯不會報錯, B類繼承於A類
14     pB = new B;
15     pA = static_cast<A*>(pB); // 編譯不會報錯, B類繼承於A類
16     C *pC = static_cast<C*>(pA); // 編譯報錯, C類與A類沒有任何關係。error C2440: 「static_cast」: 沒法從「A *」轉換爲「C *」
17 }

(3)把void類型指針轉換成目標類型的指針(不安全

[2.2] const_cast

關於操做符const_cast。詳細請參見隨筆《強制類型轉換const_cast

[2.3] dynamic_cast

(1)其餘三種都是編譯時完成的。dynamic_cast是運行時處理的,運行時要進行類型檢查。

(2)不能用於內置基本數據類型間的強制轉換。例如:

1     double dValue = 12.12;
2     int nDValue = dynamic_cast<int>(dValue); // error C2680 : 「int」 : dynamic_cast 的目標類型無效。目標類型必須是指向已定義類的指針或引用

(3)使用dynamic_cast進行轉換時,基類中必定要有虛函數,不然編譯不經過。

須要有虛函數的緣由:類中存在虛函數,就說明它有想要讓基類指針或引用指向派生類對象的必要,此時轉換纔有意義。

因爲運行時類型檢查須要運行時類型信息,而這個信息存儲在類的虛函數表中,只有定義了虛函數的類纔有虛函數表。

 1 class A
 2 {};
 3 
 4 class B : public A
 5 {};
 6 
 7 class C
 8 {};
 9 
10 void main()
11 {
12     A *pA = new A;
13     B *pB = dynamic_cast<B*>(pA); // 編譯錯誤error C2683: 「dynamic_cast」:「A」不是多態類型
14 }

(4)dynamic_cast轉換若成功,返回的是指向類的指針或引用;若失敗則會返回NULL。

(5)在類的轉換時,在類層次間進行上行轉換時,dynamic_cast和static_cast的效果是同樣的。

在進行下行轉換時,dynamic_cast具備類型檢查的功能,比static_cast更安全。

向上轉換即爲指向子類對象的向上轉換,即將子類指針轉化父類指針。

向下轉換的成敗取決於將要轉換的類型,即要強制轉換的指針所指向的對象實際類型與將要轉換後的類型必定要相同,不然轉換失敗。

關於(4)、(5)兩條的代碼示例以下:

  1 #include <iostream> 
  2 #include <cstring>
  3 using namespace std;
  4 
  5 class A
  6 {
  7 public:
  8     virtual void f()
  9     {
 10         cout << "A::f()" << endl;
 11     }
 12 };
 13 
 14 class B : public A
 15 {
 16 public:
 17     void f()
 18     {
 19         cout << "B::f()" << endl;
 20     }
 21 
 22     void bf()
 23     {
 24         cout << "B::bf()" << endl;
 25     }
 26 };
 27 
 28 class C
 29 {
 30     void pp()
 31     {
 32         return;
 33     }
 34 };
 35 
 36 int fun()
 37 {
 38     return 1;
 39 }
 40 
 41 void main()
 42 {
 43     A* pAB = new B; // pAB是A類型的指針指向一個B類型的對象
 44     A* pAA = new A; // pAA是A類型的指針指向一個A類型的對象
 45     B* pB = nullptr;
 46     C* pC = nullptr;
 47     pB = dynamic_cast<B*>(pAB); // 結果爲not nullptr,向下轉換成功,pAB指向的就是B類型的對象,因此能夠轉換成B類型的指針。 
 48     if (nullptr == pB)
 49     {
 50         cout << "dynamic_cast :: nullptr" << endl;
 51     }
 52     else
 53     {
 54         cout << "dynamic_cast :: not nullptr" << endl;
 55     }
 56     // 等價於static_cast
 57     pB = static_cast<B*>(pAB); // 結果爲not nullptr,向下轉換成功,pAB指向的就是B類型的對象,因此能夠轉換成B類型的指針。 
 58     if (nullptr == pB)
 59     {
 60         cout << "static_cast :: nullptr" << endl;
 61     }
 62     else
 63     {
 64         cout << "static_cast :: not nullptr" << endl;
 65     }
 66 
 67     pB = dynamic_cast<B*>(pAA); // 結果爲nullptr,向下轉換失敗。pAA指向的是A類型的對象,因此沒法轉換爲B類型的指針。
 68     if (nullptr == pB)
 69     {
 70         cout << "dynamic_cast :: nullptr" << endl;
 71     }
 72     else
 73     {
 74         cout << "dynamic_cast :: not nullptr" << endl;
 75     }
 76 
 77     // static_cast的不安全性測試
 78     pB = static_cast<B*>(pAA); // 結果爲not nullptr,向下轉換成功。pAA指向的是A類型的對象,居然轉換爲B類型的指針!
 79     if (nullptr == pB)
 80     {
 81         cout << "static_cast :: nullptr" << endl;
 82     }
 83     else
 84     {
 85         cout << "static_cast :: not nullptr" << endl; // 不安全性
 86         pB->f();  // A::f()
 87         pB->bf();  // B::bf()
 88     }
 89 
 90     pC = dynamic_cast<C*>(pAB); // 結果爲nullptr,向下轉換失敗。pAB指向的是B類型的對象,因此沒法轉換爲C類型的指針。
 91     if (nullptr == pC)
 92     {
 93         cout << "dynamic_cast :: nullptr" << endl;
 94     }
 95     else
 96     {
 97         cout << "dynamic_cast :: not nullptr" << endl;
 98     }
 99 
100 //  pC = static_cast<C*>(pAB); 
101 //  error C2440: 「static_cast」: 沒法從「A *」轉換爲「C *」 與指向的類型無關;轉換要求 reinterpret_cast、C 樣式轉換或函數樣式轉換
102 
103     delete pAB;
104     delete pAA;
105     
106     system("pause");
107 }
108 // run out:
109 /*
110 dynamic_cast :: not nullptr
111 static_cast :: not nullptr
112 dynamic_cast :: nullptr
113 static_cast :: not nullptr
114 A::f()
115 B::bf()
116 dynamic_cast :: nullptr
117 */

由程序運行結果分析:static_cast的不安全性顯而易見。

1 pB = static_cast<B*>(pAA); 

向下轉換結果爲not nullptr。pAA指向的是A類型的對象,居然能夠轉換爲B類型的指針!至關危險!

(6)使用dynamic_cast的類型轉換,其轉換結果幾乎都是執行期定義(implementation-defined)。所以,使用reinterpret_casts的代碼很難移植。 

[2.4] reinterpret_cast

有着與C風格的強制轉換一樣的能力。

它能夠轉化任何內置的數據類型爲其餘任何的數據類型,也能夠轉化任何指針類型爲其餘的類型。

它甚至能夠轉化內置的數據類型爲指針,無須考慮類型安全或者常量的情形。不到萬不得已絕對不用。

【3】總結

(1)C風格是(type)expression 

(2)C++風格是static_cast<type>(expression)

 

Good Good Study, Day Day Up.

順序 選擇 循環 總結

相關文章
相關標籤/搜索