C風格的轉型動作主要有如下兩種形式:
(T)expression //将expression轉型為T
T(expression) //将expression轉型為T(函數風格)
這兩種形式并無差别,隻是小括号擺放位置不同而已,這兩種形式為舊式轉型(old-style casts)。
C++還提供了四種新式轉型,常常被稱為new-style或C++-style casts:
const_cast(expression)
static_cast(expression)
dynamic_cast(expression)
reinterpret_cast(expression)
這四種方式各有不同目的,下面詳細解說。
二、const_castconst_cast通常被用來将對象的常量性轉除(cast away the constness),它也是唯一有此能力的C++-style轉型操作符。簡單來說就是去掉const屬性,比如:
const int i = 1;
int j = const_cast(i);
三
、static_caststatic_cast用來強迫隐式轉換(implicit conversions),簡單來說就是基本類型轉換,比如:
int i = 2;
double d = static_cast(i); //基本類型轉換
但是需要注意一下幾點:
(1)子類指針轉換為父類指針是安全的,但是父類指針轉換為子類指針時不安全的(建議用dynamic_cast),具體例子見後文。
(2)不能進行無關類型指針之間的轉換,即非父類和子類之間的轉換,但可以将任何類型的指針轉換成void類型指針,比如:
int i = 3;
int *pi = &i;
double *pd = static_cast(&i); //無關類型指針轉換,編譯錯誤
void *pv = static_cast(pi); //任意類型轉換成void類型
四、dynamic_castdynamic_cast主要用來執行“安全向下轉型”(safe downcasting),也就是用來決定某對象是否歸屬繼承體系中的某個類型,它是唯一無法由舊式文法執行的動作,也是唯一可能耗費重大運作成本的轉型動作。簡單來說就是多态類(父子類)之間的類型轉換,比如:
class Base {
public:
int m_iTest;
virtual void TestFunc() {}; //基類必須有虛函數,保持多态性才能使用dynamic_cast
};
class Derived : public Base {
public:
char *m_szTest[256];
void TestSon() {};
};
Base *pb1 = new Derived();
Derived *pd1 = static_cast(pb1); //子類轉換為父類,靜态類型轉換,正确但不推薦
Derived *pd2 = dynamic_cast(pb1); //子類轉換為父類,動态類型轉換,正确
Base *pb2 = new Base();
Derived *pd3 = static_cast(pb2); //父類轉換為子類,靜态類型轉換,不安全,由于切割導緻通路子類成員變量時越界
Derived *pd4 = dynamic_cast(pb2); //父類轉換為子類,動态類型轉換,安全,結果為NULL
需要注意的是:首先,要保證基類有虛函數即有多态性才能使用dynamic_cast;其次,要判斷轉換結果是否為NULL,否則後果自負。
五、reinterpret_castreinterpret_cast意圖執行低級轉型,實際動作及結果可能取決于編譯器,這也就表示其不可移植。簡單來說就是不同類型的指針類型轉換,具體如下:
(1)轉換的類型必須是一個指針、引用、函數指針或成員指針。
(2)在比特位級别進行轉換,即可以将一個指針轉換成一個整數,也可以把一個整數轉換成一個指針(即先把一個指針轉換成一個整數,再把該整數轉換成原類型的指針,還可以得到原先的指針值),但不能将非32 bit(具體位數跟平台有關,也可能是64 bit)的執行個體轉換成指針。
具體例子如下:
int doSomething() {return 0;}
typdef void (*FuncPtr)(); //函數指針類型
FuncPtr funcPtrArray[256]; //函數指針數組
funcPtrArray[0] = &doSomething; //編譯錯誤,類型不比對
funcPtrArray[0] = reinterpret_cast(&doSomething); //不同函數指針類型之間強制轉換
六、總結1、新舊轉型選擇
舊式轉型任然合法,但新式轉型較受歡迎,原因有二:
(1)它們很容易在代碼中被辨識出來(不論是人工辨識或使用工具如grep),因而得以簡化“找出類型系統在哪個地點被破壞”的過程。
(2)各轉型動作的目标愈窄化,編譯器愈可能診斷出錯誤的運用。
2、轉型副作用
無論哪種類型的轉型都會帶來性能的損失,是以:
(1)如果可以,盡量避免轉型,特别是在注重效率的代碼中避免dynamic_cast,如果有個設計需要轉型動作,試着發展無需轉型的替代設計。
(2)如果轉型是必要的,試着将它隐藏于某個函數背後,調用者随後可以調用該函數,而不需将轉型放進它們自己的代碼内。