Guru of the week:#17 型別對映. (轉)

amyz發表於2007-10-02
Guru of the week:#17 型別對映. (轉)[@more@]

作者:Hub Sutter
譯者:黃森堂

/*此文是譯者出於自娛翻譯的GotW(Guru of the Week)系列文章的一篇,原文的版權是屬於Hub Sutter(著名的C++專家,《Exceptional C++》的作者)。此文的翻譯沒有徵得原作者的同意,只供學習討論。——譯者:黃森堂*/

#17 型別對映.

難度:6/10

你知道C++的型別對映嗎?,在你的程式碼中使用它們來提高可靠性。

問題:

比舊的C的型別對映,在標準C++中新的型別對映提供了更多的與存取能力,你知道它們嗎?,剩下的問題就是如何使用它們:

class A { /*...*/ }; class B : virtual A { /*...*/ }; struct C : A { /*...*/ }; struct D : B, C { /*...*/ }; A a1; B b1; C c1; D d1; const A a2; const A& ra1 = a1; const A& ra2 = a2; char c;


1.以下新的型別對映沒有同等的C的型別對映嗎?

const_cast

dynamic_cast

reinterpret_cast

static_cast

2.以下的每一個C的型別對映中,用同等的新的型別對映來寫,如果不用新的型別對映來寫,它們是錯的嗎?

void f() { A* pa; B* pb; C* pc; pa = (A*)&ra1; pa = (A*)&a2; pb = (B*)&c1; pc = (C*)&d1; }


3.評價以下C++型別對映後的型別與正確性。

void g() { unsigned char* puc = static_cast(&c); signed char* psc = static_cast(&c); void* pv = static_cast(&b1); B* pb1 = static_cast(pv); B* pb2 = static_cast(&b1); A* pa1 = const_cast(&ra1); A* pa2 = const_cast(&ra2); B* pb3 = dynamic_cast(&c1); A* pa3 = dynamic_cast(&b1); B* pb4 = static_cast(&d1); D* pd = static_cast(pb4); pa1 = dynamic_cast(pb2); pa1 = dynamic_cast(pb4); C* pc1 = dynamic_cast(pb4); C& rc1 = dynamic_cast(*pb2); }


解決方法:

比舊的C的型別對映,在標準C++中新的型別對映提供了更多的安全與存取能力,你知道它們嗎?,剩下的問題就是如何使用它們:

class A { /*...*/ }; class B : virtual A { /*...*/ }; struct C : A { /*...*/ }; struct D : B, C { /*...*/ }; A a1; B b1; C c1; D d1; const A a2; const A& ra1 = a1; const A& ra2 = a2; char c;


1.以下新的型別對映有同等的C的型別對映嗎?

只有dynamic_cast沒用同等的C的型別對映,所有其它的新的型別對映都有相應的舊的型別對映。

2.以下的每一個C的型別對映中,用同等的新的型別對映來寫,如果不用新的型別對映來寫,它們是錯的嗎?

void f() { A* pa; B* pb; C* pc; pa = (A*)&ra1;


使用 const_cast: pa = const_cast(&ra1);

pa = (A*)&a2;


這是錯誤的,沒有同等新的型別對映,const_cast是替代者,但因為a2是const,結果不明確。

pb = (B*)&c1;


使用 reinterpret_cast: pb = reinterpret_cast(&c1);

pc = (C*)&d1; }


上面的型別對映在C是錯誤的,在C++裡,沒有對映需要:pc = &d1;

3.評價以下C++型別對映後的型別與正確性。

開始,先宣告:我們不知道這些類任何一個是否有虛,如果這類不包含虛擬函式的話,以下所有dynamic_case全是錯的,剩下的討論部分,我們假定所有類都有虛擬函式,讓使用dynamic_cast是合法的,

void g() { unsigned char* puc = static_cast(&c); signed char* psc = static_cast(&c);


錯誤:對以上兩行我們必須使用reinterpret_cast,這個首先讓你感到驚奇的,但理由是char,singed char與unsigned char是三個不同的型別,在它們之中任何透過對它們進行明確的轉換都是沒有相聯的,所以指向無關的的物件,

void* pv = static_cast(&b1); B* pb1 = static_cast(pv);


兩者之間是有細微的地方,但前者不是必須的,因為它總是明確地從物件指標向void*指標進行轉換。

B* pb2 = static_cast(&b1);


這兒有細微的地方,但不必要的,因為引數已經是B*.

This is fine, but unnecessary since the argument is already a B*.

A* pa1 = const_cast(&ra1);


這是合法的,但對映成const是通常缺少型別才進行的,大部分的情況下,在哪兒移去指標的const或引用相關到型別成員與關鍵字的掩蓋是合法的,在有更多的關於const的正確用法

A* pa2 = const_cast(&ra2);


錯誤:如果指標在物件裡使用寫將產生不確定的行為,原因a2實際上上是const物件,為什麼呢?,思考是允許a2建立同樣的const物件與使用它的資訊在只讀中並作最佳化,

註釋:我沒有給出使用const_cast轉換成非const指向const指標的示例,理由是這是多餘的,它早已是合法的分配非const指標指向const指標,我們只需要const_cast 去做相反的事。

B* pb3 = dynamic_cast(&c1);


錯誤(如果你嘗試使用pb3):因為c1不是A B(原因:C不是起源於B,在實際上它也不完全起源於B),它將pb3設為NULL,唯一正確的型別對映是使用reinterpret_cast,而且使用它始終是不幸的。

A* pa3 = dynamic_cast(&b1);


錯誤:因為b1不是A A(原因:B不是起源於A,但它起源是虛基類A(也就是說從虛基類派生的都不能轉換成該基類的型別)),這是的。

B* pb4 = static_cast(&d1);


這兒有細微的地方,因為源於基類的指標轉換是不需要明確宣告所以不需要。

D* pd = static_cast(pb4);


這兒有細微的地方,如果你預期這兒需要dynamic_cast的話是你會吃驚的。理由是當目標是已知道的時候,向下對映是靜態的,但要小心:你是說編譯器在事實上知道你的,它為什麼開始指向實際上的它的型別,如果你錯了,那麼型別對映就不能通知你有問題(像dynamic_cast一樣,如果型別對映失敗它將返回空指標),且最多你能取得非真實的執行時錯誤與崩潰。

pa1 = dynamic_cast(pb2); pa1 = dynamic_cast(pb4);


這兒有兩個看起來很相似,它們都嘗試使用dynamic_cast把B*轉換成A*,然而,第一個是錯誤的而第二個是對的。

這兒有個理由:同上面的註釋,你不能使用dynamic_cast去對映指向實際上是B物件(pb2是指向b1物件)成A物件,因為B是從A進行私有繼承,非公有繼承,然而,第二個對映成功是因為pb4指向物件d1,且d像A一樣是間接派生於基類(透過C),與dynamic_cast是允許對映越過繼承體系,使用這種路徑B* -> D* -> C* -> A*.

C* pc1 = dynamic_cast(pb4);


這兒有兩個很細微,與前面的理由一樣:dynamic_cast透過繼承體系進行對映,所以這是合法且成功。

C& rc1 = dynamic_cast(*pb2); }


最後,這而也有細微的地方...,因為*pb2不是實際上上的a C,dynamic_cast將執出bad_cast異常失敗訊號。為什麼呢?如果對映失敗,dynamic_cast將返null,但這兒引用的物件是空的,如果引用失敗那它不能返回空的引用,這兒除了丟擲異常外沒有失敗的訊號傳送到客戶端,所以那兒為什麼只有bad_cast異常。

一些個人看法

1.to:%22cber@.com.cn%22">cber

:

dynamic_cast用於繼承體系結構的轉型(向下是自動的,向上需要驗證),較為安全(不過需要是polymorphic classes)static_cast有點類似於C中的強制轉型。具體的可以看看MEC Item2

2.:One for compile time, one for runtime. I think.


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-974409/,如需轉載,請註明出處,否則將追究法律責任。

相關文章