天天看點

子類和父類對象在進行類型轉換時_C++語言之強制類型轉換

子類和父類對象在進行類型轉換時_C++語言之強制類型轉換
一、C風格強制轉換

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_cast

const_cast通常被用來将對象的常量性轉除(cast away the constness),它也是唯一有此能力的C++-style轉型操作符。簡單來說就是去掉const屬性,比如:

const int i = 1;

int j = const_cast(i);

、static_cast

static_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_cast

dynamic_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_cast

reinterpret_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)如果轉型是必要的,試着将它隐藏于某個函數背後,調用者随後可以調用該函數,而不需将轉型放進它們自己的代碼内。