C++箴言:將強(qiáng)制轉(zhuǎn)型減到最少

字號(hào):

C++ 的規(guī)則設(shè)計(jì)為保證不會(huì)發(fā)生類型錯(cuò)誤。在理論上,如果你的程序想順利地通過編譯,你就不應(yīng)該試圖對任何對象做任何不安全的或無意義的操作。這是一個(gè)非常有價(jià)值的保證,你不應(yīng)該輕易地放棄它。
    不幸的是,強(qiáng)制轉(zhuǎn)型破壞了類型系統(tǒng)。它會(huì)引起各種各樣的麻煩,其中一些容易被察覺,另一些則格外地微妙。如果你從 C,Java,或 C# 轉(zhuǎn)到 C++,請一定注意,因?yàn)閺?qiáng)制轉(zhuǎn)型在那些語言中比在 C++ 中更有必要,危險(xiǎn)也更少。但是 C++ 不是 C,也不是 Java,也不是 C#。在這一語言中,強(qiáng)制轉(zhuǎn)型是一個(gè)你必須全神貫注才可以靠近的特性。
    我們就從回顧強(qiáng)制轉(zhuǎn)型的語法開始,因?yàn)閷τ谕瑯拥膹?qiáng)制轉(zhuǎn)型通常有三種不同的寫法。C 風(fēng)格(C-style)強(qiáng)制轉(zhuǎn)型如下:
    (T) expression // cast expression to be of type T
    函數(shù)風(fēng)格(Function-style)強(qiáng)制轉(zhuǎn)型使用這樣的語法:
    T(expression) // cast expression to be of type T
    這兩種形式之間沒有本質(zhì)上的不同,它純粹就是一個(gè)把括號(hào)放在哪的問題。我把這兩種形式稱為舊風(fēng)格(old-style)的強(qiáng)制轉(zhuǎn)型。
    C++ 同時(shí)提供了四種新的強(qiáng)制轉(zhuǎn)型形式(通常稱為新風(fēng)格的或 C++ 風(fēng)格的強(qiáng)制轉(zhuǎn)型):
    const_cast(expression)
    dynamic_cast(expression)
    reinterpret_cast(expression)
    static_cast(expression)
    每一種適用于特定的目的:
    ·const_cast 一般用于強(qiáng)制消除對象的常量性。它是能做到這一點(diǎn)的 C++ 風(fēng)格的強(qiáng)制轉(zhuǎn)型。
    ·dynamic_cast 主要用于執(zhí)行“安全的向下轉(zhuǎn)型(safe downcasting)”,也就是說,要確定一個(gè)對象是否是一個(gè)繼承體系中的一個(gè)特定類型。它是不能用舊風(fēng)格語法執(zhí)行的強(qiáng)制轉(zhuǎn)型。也是可能有重大運(yùn)行時(shí)代價(jià)的強(qiáng)制轉(zhuǎn)型。(過一會(huì)兒我再提供細(xì)節(jié)。)
    ·reinterpret_cast 是特意用于底層的強(qiáng)制轉(zhuǎn)型,導(dǎo)致實(shí)現(xiàn)依賴(implementation-dependent)(就是說,不可移植)的結(jié)果,例如,將一個(gè)指針轉(zhuǎn)型為一個(gè)整數(shù)。這樣的強(qiáng)制轉(zhuǎn)型在底層代碼以外應(yīng)該極為罕見。在本書中我只用了一次,而且還僅僅是在討論你應(yīng)該如何為*內(nèi)存(raw memory)寫一個(gè)調(diào)諧分配者(debugging allocator)的時(shí)候。
    ·static_cast 可以被用于強(qiáng)制隱型轉(zhuǎn)換(例如,non-const 對象轉(zhuǎn)型為 const 對象(就像 Item 3 中的),int 轉(zhuǎn)型為 double,等等)。它還可以用于很多這樣的轉(zhuǎn)換的反向轉(zhuǎn)換(例如,void* 指針轉(zhuǎn)型為有類型指針,基類指針轉(zhuǎn)型為派生類指針),但是它不能將一個(gè) const 對象轉(zhuǎn)型為 non-const 對象。(只有 const_cast 能做到。)
    舊風(fēng)格的強(qiáng)制轉(zhuǎn)型依然合法,但是新的形式更可取。首先,在代碼中它們更容易識(shí)別(無論是人還是像 grep 這樣的工具都是如此),這樣就簡化了在代碼中尋找類型系統(tǒng)被破壞的地方的過程。第二,更精確地指定每一個(gè)強(qiáng)制轉(zhuǎn)型的目的,使得編譯器診斷使用錯(cuò)誤成為可能。例如,如果你試圖使用一個(gè) const_cast 以外的新風(fēng)格強(qiáng)制轉(zhuǎn)型來消除常量性,你的代碼將無法編譯。
    當(dāng)我要調(diào)用一個(gè) explicit 構(gòu)造函數(shù)用來傳遞一個(gè)對象給一個(gè)函數(shù)的時(shí)候,大概就是我僅有的使用舊風(fēng)格的強(qiáng)制轉(zhuǎn)換的時(shí)候。例如:
    class Widget {
    public:
    explicit Widget(int size);
    ...
    };
    void doSomeWork(const Widget w);
    doSomeWork(Widget(15)); // create Widget from int
    // with function-style cast
    doSomeWork(static_cast(15)); // create Widget from int
    // with C++-style cast