1. 程式人生 > >C++的型別轉換(Type Casting)

C++的型別轉換(Type Casting)

C++的型別轉換包括了兩類,隱式轉換和顯式轉換。

隱式轉換常見於兩種情況,一種是基礎型別運算時,自動從低精度型別向高精度型別轉換,如char轉int、int轉double等。

int a = 100;
double b = a / 4.0;

另一種情況是物件之間賦值,隱式地呼叫了複製建構函式。
class A { };
class B { public: B(A a) { } };

A a;
B b = a;

顯式轉換,也可以叫做強制型別轉換。C++是強型別程式語言,每個變數的型別必需是明確的。一般的轉換如下:
double a = 10.0;
int b = (int) a; /* 精度損失 */

這種顯式轉換方式支援任意兩個型別之間轉換,但任意的轉換可能會導致Runtime Error。

class A { public: int num; };
classB {
    public: 
        void show() { cout << num << endl; }
        int num;
};

A a;
B *b = (B *) &a;
b->show(); /* Error */

為了解決這個問題,C++包含四種Casting操作符,對轉換的型別進行限制,基本語法如下:

dynamic_cast<new_type>(expression)
reinterpret_cast<new_type>(expression)
static_cast<new_type>(expression)
const_cast<new_type>(expression)

一般來說,對於一個好的設計,應該是儘量少用強制型別轉換,這裡從限制最嚴的Casting操作符開始說起。

dynamic_cast

該操作符允許型別在繼承鏈上向上或者向下轉換,子類指標向基類指標轉換是必定可以轉換成功的。事實上,這也是好的設計允許的,基類指標指向子類物件(多型條件之一)。

class Base { };
class Derived: public Base { };

Derived a;
Base *b = dynamic_cast<Base *>(&a);

而對於向下轉換,該操作符會在執行時檢測轉換是否合法。好的設計應儘量避免向下轉換。對於不合法的轉換,dynamic_cast返回NULL。
Base *a = new Derived();
Base *b = new Base();

Derived *pa = dynamic_cast<Derived *>(&a);
Derived *pb = dynamic_cast<Derived *>(&b); /* 返回NULL */

該操作符還支援NULL指標轉換為其他指標,以及其他指標轉換為void *。


static_cast

和dynamic_cast類似,static_cast也只能在繼承鏈上向上或向下轉換,但條件比前者更寬鬆。在向下轉換過程中,static_cast不會檢測轉換是否合法,即上例中dynamic_cast返回NULL,而static_cast則能成功轉換。

除了和dynamic_cast類似的功能外,static_cast還能對非指標型別進行轉換。該轉換的實質與隱式轉換類似,即基礎型別的轉換或者是實現了複製建構函式的物件間轉換。

double a = 1.25;
int b = static_cast<int>(a);

reinterpret_cast

該操作符最為寬鬆,允許任意指標的轉換,不要求物件之間是相關的。同時還支援從整型地址和指標之間的轉換。

class A { };
class B { };

A a;
B *b = reinterpret_cast<B *>(a);

const_cast

以上三個操作符都無法實現const和非const之間轉換,該操作符可以轉換物件的const和validate屬性(設定或取消)。

const int a = 100;
int *b = const_cast<int *>(&a);

typeid

該操作符可以獲取物件的型別,在標頭檔案typeinfo中定義。返回的結果是能夠最準確描述物件的型別,例如傳入基類指標(指向子類物件),返回的型別是子類。返回值可以直接用於比較,判斷兩個物件的型別是否相同。

#include <typeinfo>

Base *a = new Base();
Base *b = new Derived();

cout << tpyeinfo(a) == typeinfo(b) << endl; /* 不相等 */
cout << typeinfo(a).name << endl;
cout << typeinfo(b).name << endl;
/* name通常是一個可讀字串,與編譯器有關 */