前言
原文翻譯自http://www.cplusplus.com/doc/tutorial/typecasting/,覺得這篇文章講C++類型轉換簡單明了,所以特別翻譯了下。
在C++中,將一個已知的類型轉換為另一個類型,我們稱呼為類型轉換,本文會介紹C++的各種類型轉換。
隱式轉換
隱式轉換不需要任何操作符,它們會自動執行,當值被賦值到相容類型,就會執行,例如:
short a=2000;
int b;
b=a;
隱式轉換,也包括建構函式和運算子的轉換,例如:
class A {};
class B { public: B (A a) {} };
A a;
B b=a;
顯式轉換
C++是一個強型別的語言。許多轉換,需要顯式轉換,例如
short a=2000;
int b;
b = (int) a; // c-like cast notation
b = int (a); // functional notation
上述的類型轉換已經滿足了基本類型的轉換了,但是如果應用於類和指標中,代碼可以編譯,但是在運行過程中會出錯。例如
// class type-casting
#include <iostream>
using namespace std;
class CDummy {
float i,j;
};
class CAddition {
int x,y;
public:
CAddition (int a, int b) { x=a; y=b; }
int result() { return x+y;}
};
int main () {
CDummy d;
CAddition * padd;
padd = (CAddition*) &d;
cout << padd->result();
return 0;
}
這段代碼會在運行期出錯,在執行padd->result()時異常退出。
傳統明確的類型轉換,可以轉換成任何其他指標類型任何指標,它們指向的類型無關。在隨後的調用成員的結果,會產生一個執行階段錯誤或意外的結果。
C++標準轉換運算子
傳統的類和指標的類型轉換,十分不安全,可能會在運行時,由於類型不符異常退出,所以C++提供了四個標準轉換運算子:dynamic_cast, reinterpret_cast, static_cast, const_cast
dynamic_cast <new_type> (expression)
reinterpret_cast <new_type> (expression)
static_cast <new_type> (expression)
const_cast <new_type> (expression)
dynamic_cast
dynamic_cast只能用於指標和引用的對象。其目的是確保類型轉換的結果是一個有效完成所請求的類的對象,所以當我們從一個類轉換到這個類的基類,dynamic_cast總是可以成功
class CBase { };
class CDerived: public CBase { };
CBase b; CBase* pb;
CDerived d; CDerived* pd;
pb = dynamic_cast<CBase*>(&d); // ok: derived-to-base
pd = dynamic_cast<CDerived*>(&b); // wrong: base-to-derived
當新的類型不是被轉換的類型的基類,dynamic_cast無法完成指標的轉換,返回NULL,如果dynamic_cast是用來轉換為參考型別的轉換失敗,會拋出"Bad_cast exception"異常。
dynamic_cast 可以轉換NULL指標為不相關的類,也可以任何類型的指標為void指標。
dynamic_cast的需要的運行時類型資訊(RTTI),保持動態類型跟蹤。一些編譯器支援此功能預設情況下是禁用的選項。這必須啟用運行時類型檢查,使用dynamic_cast的正常工作。
static_cast
static_cast可以執行相關的類的指標之間的轉換,不僅從衍生類別到基類的轉換,也可以做到基類到衍生類別的轉換。static_cast沒有在運行時進行安全檢查,因此,它是程式員,以確保轉換是安全的,但是dynamic_cast的型別安全檢查的開銷,static_cast是可以避免的。
class CBase {};
class CDerived: public CBase {};
CBase * a = new CBase;
CDerived * b = static_cast<CDerived*>(a);
上述代碼是合法的,雖然b指向一個不完整的對象,並可能在運行期導致錯誤。
static_cast也可以用來執行任何其他非指標的轉換,例如像基本類型之間的標準轉換,也可以是隱式執行
double d=3.14159265;
int i = static_cast<int>(d);
reinterpret_cast
reinterpret_cast轉換成任何其他指標類型,甚至無關的類,任何指標類型。操作的結果是一個簡單的從一個指標到其他的值的二進位拷貝。所有的指標轉換是允許的:不管是指標指向的內容還是指標本身的類型。
同時,它也可以把指標轉換為整數,但是整數是平台相關的,必須保證整數足夠大到可以包含指標本身的內容,最後再轉換為一個合法的指標。
class A {};
class B {};
A * a = new A;
B * b = reinterpret_cast<B*>(a)
const_cast
const_cast用於操縱對象的常量性,既要設定或刪除。例如,一個函數要求一個非const參數,而程式需要傳遞一個const參數。
#include <iostream>
using namespace std;
void print (char * str)
{
cout << str << endl;
}
int main () {
const char * c = "sample text";
print ( const_cast<char *> (c) );
return 0;
}
typeid
typeid的允許檢查運算式的類型
typeid (expression)
這個操作符返回一個引用在標準標頭檔<typeinfo>中定義的常量對象,是一個類型的type_info。這個傳回值可以與另一個使用運算子==和!=進行比較兩個資料類型或類的名稱,或者也可以使用其name() 成員函數獲得類型名字(一個0結束的的字串)。
// typeid
#include <iostream>
#include <typeinfo>
using namespace std;
int main () {
int * a,b;
a=0; b=0;
if (typeid(a) != typeid(b))
{
cout << "a and b are of different types:\n";
cout << "a is: " << typeid(a).name() << '\n';
cout << "b is: " << typeid(b).name() << '\n';
}
return 0;
}
當typeid應用使用RTTI來跟蹤動態對象的類型,那麼當typeid的是應用於運算式,其類型是一個多態類,其結果是派生的最完整的對象的類型:
// typeid, polymorphic class
#include <iostream>
#include <typeinfo>
#include <exception>
using namespace std;
class CBase { virtual void f(){} };
class CDerived : public CBase {};
int main () {
try {
CBase* a = new CBase;
CBase* b = new CDerived;
cout << "a is: " << typeid(a).name() << '\n';
cout << "b is: " << typeid(b).name() << '\n';
cout << "*a is: " << typeid(*a).name() << '\n';
cout << "*b is: " << typeid(*b).name() << '\n';
} catch (exception& e) { cout << "Exception: " << e.what() << endl; }
return 0;
}
注意:返回的字串成員的type_info名稱取決於你的編譯器和庫的具體實現,其典型的類型名稱,它不一定是一個簡單的字串.
如果類型typeid的參數是引用操作符(*)開頭的指標,而且這個指標是NULL,typeid會拋出一個bad_typeid異常。