C++風(fēng)格的類型轉(zhuǎn)換的用法
這是More?Effecitve?C++里的第二條對類型轉(zhuǎn)換講的很好,也很基礎(chǔ)好懂。
Item?M2:盡量使用C++風(fēng)格的類型轉(zhuǎn)換
仔細(xì)想想地位卑賤的類型轉(zhuǎn)換功能(cast),其在程序設(shè)計中的地位就象goto語句一樣令人鄙視。但是它還不是無法令人忍受,因為當(dāng)在某些緊要的關(guān)頭,類型轉(zhuǎn)換還是必需的,這時它是一個必需品。
不過C風(fēng)格的類型轉(zhuǎn)換并不代表所有的類型轉(zhuǎn)換功能。
一
來它們過于粗魯,能允許你在任何類型之間進(jìn)行轉(zhuǎn)換。不過如果要進(jìn)行更精確的類型轉(zhuǎn)換,這會是一個優(yōu)點。在這些類型轉(zhuǎn)換中存在著巨大的不同,例如把一個指向
?const對象的指針(pointer-to-const-object)轉(zhuǎn)換成指向非const對象的指針(pointer-to-non
-const?-object)(即一個僅僅去除const的類型轉(zhuǎn)換),把一個指向基類的指針轉(zhuǎn)換成指向子類的指針(即完全改變對象類型)。
傳統(tǒng)的C風(fēng)格的類型轉(zhuǎn)換不對上述兩種轉(zhuǎn)換進(jìn)行區(qū)分。(這一點也不令人驚訝,因為C風(fēng)格的類型轉(zhuǎn)換是為C語言設(shè)計的,而不是為C++語言設(shè)計的)。
二
來C風(fēng)格的類型轉(zhuǎn)換在程序語句中難以識別。在語法上,類型轉(zhuǎn)換由圓括號和標(biāo)識符組成,而這些可以用在C++中的任何地方。這使得回答象這樣一個最基本的有
關(guān)類型轉(zhuǎn)換的問題變得很困難:“在這個程序中是否使用了類型轉(zhuǎn)換?”。這是因為人工閱讀很可能忽略了類型轉(zhuǎn)換的語句,而利用象grep的工具程序也不能從
語句構(gòu)成上區(qū)分出它們來。
C++通過引進(jìn)四個新的類型轉(zhuǎn)換操作符克服了C風(fēng)格類型轉(zhuǎn)換的缺點,這四個操作符是,?
static_cast,?const_cast,?dynamic_cast,?和reinterpret_cast。
在大多數(shù)情況下,對于這些操作符你只需要知道原來你習(xí)慣于這樣寫,
(type)?expression
而現(xiàn)在你總應(yīng)該這樣寫:
static_cast
<
type
>
(expression)
例如,假設(shè)你想把一個int轉(zhuǎn)換成double,以便讓包含int類型變量的表達(dá)式產(chǎn)生出浮點數(shù)值的結(jié)果。如果用C風(fēng)格的類型轉(zhuǎn)換,你能這樣寫:
int?firstNumber,?secondNumber;

double?result?=?((double)firstNumber)/secondNumber;
如果用上述新的類型轉(zhuǎn)換方法,你應(yīng)該這樣寫:
double?result?=?static_cast
<
double
>
(firstNumber)/secondNumber;
這樣的類型轉(zhuǎn)換不論是對人工還是對程序都很容易識別。
static_cast?
在功能上基本上與C風(fēng)格的類型轉(zhuǎn)換一樣強(qiáng)大,含義也一樣。它也有功能上限制。例如,你不能用static_cast象用C風(fēng)格的類型轉(zhuǎn)換一樣把
?struct轉(zhuǎn)換成int類型或者把double類型轉(zhuǎn)換成指針類型,另外,static_cast不能從表達(dá)式中去除const屬性,因為
另一個新的類型轉(zhuǎn)換操作符const_cast有這樣的功能。
其它新的C++類型轉(zhuǎn)換操作符被用在需要更多限制的地方。const_cast用于
類型轉(zhuǎn)換掉表達(dá)式的const或volatileness屬性。通過使用const_cast,你向人們和編譯器強(qiáng)調(diào)你通過類型轉(zhuǎn)換想做的只是改變一些東
西的?constness或者volatileness屬性。這個含義被編譯器所約束。如果你試圖使用const_cast來完成修改
constness?或者volatileness屬性之外的事情,你的類型轉(zhuǎn)換將被拒絕。下面是一些例子:
class?Widget?{?
?};
class?SpecialWidget:?public?Widget?{?
?};
void?update(SpecialWidget?*psw);
SpecialWidget?sw;?//?sw?是一個非const?對象。
const?SpecialWidget&?csw?=?sw;?//?csw?是sw的一個引用
//?它是一個const?對象
update(
&csw
);?//?錯誤!不能傳遞一個const?SpecialWidget*?變量
//?給一個處理SpecialWidget*類型變量的函數(shù)
update(const_cast
<
SpecialWidget
*
>
(
&csw
));
//?正確,csw的const被顯示地轉(zhuǎn)換掉(
//?csw和sw兩個變量值在update
//函數(shù)中能被更新)
update((SpecialWidget*)
&csw
);
//?同上,但用了一個更難識別
//的C風(fēng)格的類型轉(zhuǎn)換
Widget?*pw?=?new?SpecialWidget;
update(pw);?//?錯誤!pw的類型是Widget*,但是
//?update函數(shù)處理的是SpecialWidget*類型
update(const_cast
<
SpecialWidget
*
>
(pw));
//?錯誤!const_cast僅能被用在影響
//?constness?or?volatileness的地方上。,
//?不能用在向繼承子類進(jìn)行類型轉(zhuǎn)換。
到目前為止,const_cast最普通的用途就是轉(zhuǎn)換掉對象的const屬性。
第
二種特殊的類型轉(zhuǎn)換符是dynamic_cast,它被用于安全地沿著類的繼承關(guān)系向下進(jìn)行類型轉(zhuǎn)換。這就是說,你能用dynamic_cast把指向基
類的指針或引用轉(zhuǎn)換成指向其派生類或其兄弟類的指針或引用,而且你能知道轉(zhuǎn)換是否成功。失敗的轉(zhuǎn)換將返回空指針(當(dāng)對指針進(jìn)行類型轉(zhuǎn)換時)或者拋出異常
(當(dāng)對引用進(jìn)行類型轉(zhuǎn)換時):
Widget?*pw;

update(dynamic_cast
<
SpecialWidget
*
>
(pw));
//?正確,傳遞給update函數(shù)一個指針
//?是指向變量類型為SpecialWidget的pw的指針
//?如果pw確實指向一個對象,
//?否則傳遞過去的將使空指針。
void?updateViaRef(SpecialWidget&?rsw);
updateViaRef(dynamic_cast
<
SpecialWidget
&
>
(*pw));
//正確。傳遞給updateViaRef函數(shù)
//?SpecialWidget?pw?指針,如果pw
//?確實指向了某個對象
//?否則將拋出異常
dynamic_casts在幫助你瀏覽繼承層次上是有限制的。它不能被用于缺乏虛函數(shù)的類型上(參見條款M24),也不能用它來轉(zhuǎn)換掉constness:
int?firstNumber,?secondNumber;

double?result?=?dynamic_cast
<
double
>
(firstNumber)/secondNumber;
//?錯誤!沒有繼承關(guān)系
const?SpecialWidget?sw;

update(dynamic_cast
<
SpecialWidget
*
>
(
&sw
));
//?錯誤!?dynamic_cast不能轉(zhuǎn)換
//?掉const。
如你想在沒有繼承關(guān)系的類型中進(jìn)行轉(zhuǎn)換,你可能想到static_cast。如果是為了去除const,你總得用const_cast。
這四個類型轉(zhuǎn)換符中的最后一個是reinterpret_cast。使用這個操作符的類型轉(zhuǎn)換,其的轉(zhuǎn)換結(jié)果幾乎都是執(zhí)行期定義(implementation-defined)。因此,使用reinterpret_casts的代碼很難移植。
reinterpret_casts的最普通的用途就是在函數(shù)指針類型之間進(jìn)行轉(zhuǎn)換。例如,假設(shè)你有一個函數(shù)指針數(shù)組:
typedef?void?(*FuncPtr)();?//?FuncPtr?is?一個指向函數(shù)
//?的指針,該函數(shù)沒有參數(shù)
//?返回值類型為void
FuncPtr?funcPtrArray[10];?//?funcPtrArray?是一個能容納
//?10個FuncPtrs指針的數(shù)組
讓我們假設(shè)你希望(因為某些莫名其妙的原因)把一個指向下面函數(shù)的指針存入funcPtrArray數(shù)組:
int?doSomething();
你不能不經(jīng)過類型轉(zhuǎn)換而直接去做,因為doSomething函數(shù)對于funcPtrArray數(shù)組來說有一個錯誤的類型。在FuncPtrArray數(shù)組里的函數(shù)返回值是void類型,而doSomething函數(shù)返回值是int類型。
funcPtrArray[0]?=?
&doSomething;
?//?錯誤!類型不匹配
reinterpret_cast可以讓你迫使編譯?