版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、C+箴言:將強制轉(zhuǎn)型減到最少 C+ 的規(guī)則設(shè)計為保證不會發(fā)生類型錯誤。在理論上,如果你的程序想順利地通過編譯,你就不應(yīng)該試圖對任何對象做任何不安全的或無意義的操作。這是一個非常有價值的保證,你不應(yīng)該輕易地放棄它。 不幸的是,強制轉(zhuǎn)型破壞了類型系統(tǒng)。它會引起各種各樣的麻煩,其中一些容易被察覺,另一些則格外地微妙。如果你從 C,Java,或 C# 轉(zhuǎn)到 C+,請一定注意,因為強制轉(zhuǎn)型在那些語言中比在 C+ 中更有必要,危險也更少。但是 C+ 不是 C,也不是 Java,也不是 C#。在這一語言中,強制轉(zhuǎn)型是一個你必須全神貫注才可以靠近的特性。 我們就從回顧強制轉(zhuǎn)型的語法開始,因為對于同樣的強制轉(zhuǎn)型
2、通常有三種不同的寫法。C 風(fēng)格(C-style)強制轉(zhuǎn)型如下: (T expression / cast expression to be of type T 函數(shù)風(fēng)格(Function-style)強制轉(zhuǎn)型使用這樣的語法: T(expression / cast expression to be of type T 這兩種形式之間沒有本質(zhì)上的不同,它純粹就是一個把括號放在哪的問題。我把這兩種形式稱為舊風(fēng)格(old-style)的強制轉(zhuǎn)型。 C+ 同時提供了四種新的強制轉(zhuǎn)型形式(通常稱為新風(fēng)格的或 C+ 風(fēng)格的強制轉(zhuǎn)型): const_cast(expression dynamic_cast(
3、expression reinterpret_cast(expression static_cast(expression 每一種適用于特定的目的: const_cast 一般用于強制消除對象的常量性。它是唯一能做到這一點的 C+ 風(fēng)格的強制轉(zhuǎn)型。 dynamic_cast 主要用于執(zhí)行“安全的向下轉(zhuǎn)型(safe downcasting)”,也就是說,要確定一個對象是否是一個繼承體系中的一個特定類型。它是唯一不能用舊風(fēng)格語法執(zhí)行的強制轉(zhuǎn)型。也是唯一可能有重大運行時代價的強制轉(zhuǎn)型。(過一會兒我再提供細節(jié)。) reinterpret_cast 是特意用于底層的強制轉(zhuǎn)型,導(dǎo)致實現(xiàn)依賴(impleme
4、ntation-dependent)(就是說,不可移植)的結(jié)果,例如,將一個指針轉(zhuǎn)型為一個整數(shù)。這樣的強制轉(zhuǎn)型在底層代碼以外應(yīng)該極為罕見。在本書中我只用了一次,而且還僅僅是在討論你應(yīng)該如何為裸內(nèi)存(raw memory)寫一個調(diào)諧分配者(debugging allocator)的時候。 static_cast 可以被用于強制隱型轉(zhuǎn)換(例如,non-const 對象轉(zhuǎn)型為 const 對象(就像 Item 3 中的),int 轉(zhuǎn)型為 double,等等)。它還可以用于很多這樣的轉(zhuǎn)換的反向轉(zhuǎn)換(例如,void* 指針轉(zhuǎn)型為有類型指針,基類指針轉(zhuǎn)型為派生類指針),但是它不能將一個 const 對象轉(zhuǎn)
5、型為 non-const 對象。(只有 const_cast 能做到。) 舊風(fēng)格的強制轉(zhuǎn)型依然合法,但是新的形式更可取。首先,在代碼中它們更容易識別(無論是人還是像 grep 這樣的工具都是如此),這樣就簡化了在代碼中尋找類型系統(tǒng)被破壞的地方的過程。第二,更精確地指定每一個強制轉(zhuǎn)型的目的,使得編譯器診斷使用錯誤成為可能。例如,如果你試圖使用一個 const_cast 以外的新風(fēng)格強制轉(zhuǎn)型來消除常量性,你的代碼將無法編譯。 當我要調(diào)用一個 explicit 構(gòu)造函數(shù)用來傳遞一個對象給一個函數(shù)的時候,大概就是我僅有的使用舊風(fēng)格的強制轉(zhuǎn)換的時候。例如: class Widget public: ex
6、plicit 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 由于某種原因,有條不紊的對象創(chuàng)建感覺上不像一個強制轉(zhuǎn)型,所以在這個強制轉(zhuǎn)型中我多半會用函數(shù)風(fēng)格的強制轉(zhuǎn)型代替 static_cast。反過來說,在你寫出那些導(dǎo)致核心崩潰(core du
7、mp)的代碼時,你通常都感覺你有恰當?shù)脑颍阅阕詈煤雎阅愕母杏X并始終都使用新風(fēng)格的強制轉(zhuǎn)型。 很多程序員認為強制轉(zhuǎn)型除了告訴編譯器將一種類型看作另一種之外什么都沒做,但這是錯誤的。任何種類的類型轉(zhuǎn)換(無論是通過強制轉(zhuǎn)型的顯式的還是編譯器添加的隱式的)都會導(dǎo)致運行時的可執(zhí)行代碼。例如,在這個代碼片斷中, int x, y; . double d = static_cast(x/y; / divide x by y, but use / floating point division int x 到 double 的強制轉(zhuǎn)型理所當然要生成代碼,因為在大多數(shù)系統(tǒng)架構(gòu)中,一個 int 的底層表示與
8、 double 的不同。這可能還不怎么令人吃驚,但是下面這個例子可能會讓你稍微開一下眼: class Base . ; class Derived: public Base . ; Derived d; Base *pb = &d; / implicitly convert Derived* Base* 這里我們只是創(chuàng)建了一個指向派生類對象的基類指針,但是有時候,這兩個指針的值并不相同。在當前情況下,會在運行時在 Derived* 指針上應(yīng)用一個偏移量以得到正確的 Base* 指針值。 這后一個例子表明一個單一的對象(例如,一個類型為 Derived 的對象)可能會有不止一個地址(例如,它的被
9、一個 Base* 指針指向的地址和它的被一個 Derived* 指針指向的地址)。這在 C 中就不會發(fā)生,也不會在 Java 中發(fā)生,也不會在 C# 中發(fā)生,它僅在 C+ 中發(fā)生。實際上,如果使用了多繼承,則一定會發(fā)生,但是在單繼承下也會發(fā)生。與其它事情合在一起,就意味著你應(yīng)該總是避免對 C+ 如何擺放事物做出假設(shè),你當然也不應(yīng)該基于這樣的假設(shè)執(zhí)行強制轉(zhuǎn)型。例如,將一個對象的地址強制轉(zhuǎn)型為 char* 指針,然后對其使用指針運算,這幾乎總是會導(dǎo)致未定義行為。 但是請注意我說一個偏移量是“有時”被需要。對象擺放的方法和他們的地址的計算方法在不同的編譯器之間有所變化。這就意味著僅僅因為你的“我知道
10、事物是如何擺放的”而使得強制轉(zhuǎn)型能工作在一個平臺上,并不意味著它們也能在其它平臺工作。這個世界被通過痛苦的道路學(xué)得這條經(jīng)驗的可憐的程序員所充滿。 關(guān)于強制轉(zhuǎn)型的一件有趣的事是很容易寫出看起來對(在其它語言中也許是對的)實際上錯的東西。例如,許多應(yīng)用框架(application framework)要求在派生類中實現(xiàn)虛成員函數(shù)時要首先調(diào)用它們的基類對應(yīng)物。假設(shè)我們有一個 Window 基類和一個 SpecialWindow 派生類,它們都定義了虛函數(shù) onResize。進一步假設(shè) SpecialWindow 的 onResize 被期望首先調(diào)用 Window 的 onResize。這就是實現(xiàn)這個
11、的一種方法,它看起來正確實際并不正確: class Window / base class public: virtual void onResize( . / base onResize impl . ; class SpecialWindow: public Window / derived class public: virtual void onResize( / derived onResize impl; static_cast(*this.onResize(; / cast *this to Window, / then call its onResize; / this doe
12、snt work! . / do SpecialWindow- / specific stuff . ; 我突出了代碼中的強制轉(zhuǎn)型。(這是一個新風(fēng)格的強制轉(zhuǎn)型,但是使用舊風(fēng)格的強制轉(zhuǎn)型也于事無補。)正像你所期望的,代碼將 *this 強制轉(zhuǎn)型為一個 Window。因此調(diào)用 onResize 的結(jié)果就是調(diào)用 Window:onResize。你也許并不期待它沒有調(diào)用當前對象的那個函數(shù)!作為替代,強制轉(zhuǎn)型創(chuàng)建了一個 *this 的基類部分的新的,臨時的拷貝,然后調(diào)用這個拷貝的 onResize!上面的代碼沒有調(diào)用當前對象的 Window:onResize,然后再對這個對象執(zhí)行 SpecialWind
13、ow 特有的動作它在對當前對象執(zhí)行 SpecialWindow 特有的動作之前,調(diào)用了當前對象的基類部分的一份拷貝的 Window:onResize。如果 Window:onResize 改變了當前對象(可能性并不小,因為 onResize 是一個 non-const 成員函數(shù)),當前對象并不會改變。作為替代,那個對象的一份拷貝被改變。如果 SpecialWindow:onResize 改變了當前對象,無論如何,當前對象將被改變,導(dǎo)致的境況是那些代碼使當前對象進入一種病態(tài),沒有做基類的變更,卻做了派生類的變更。 解決方法就是消除強制轉(zhuǎn)型,用你真正想表達的來代替它。你不應(yīng)該哄騙編譯器將 *thi
14、s 當作一個基類對象來處理,你應(yīng)該調(diào)用當前對象的 onResize 的基類版本。就是這樣: class SpecialWindow: public Window public: virtual void onResize( Window:onResize(; / call Window:onResize . / on *this . ; 這個例子也表明如果你發(fā)現(xiàn)自己要做強制轉(zhuǎn)型,這就是你可能做錯了某事的一個信號。在你想用 dynamic_cast 時尤其如此。 在探究 dynamic_cast 的設(shè)計意圖之前,值得留意的是很多 dynamic_cast 的實現(xiàn)都相當慢。例如,至少有一種通用的實
15、現(xiàn)部分地基于對類名字進行字符串比較。如果你在一個位于四層深的單繼承體系中的對象上執(zhí)行 dynamic_cast,在這樣一個實現(xiàn)下的每一個 dynamic_cast 都要付出相當于四次調(diào)用 strcmp 來比較類名字的成本。對于一個更深的或使用了多繼承的繼承體系,付出的代價會更加昂貴。一些實現(xiàn)用這種方法工作是有原因的(它們不得不這樣做以支持動態(tài)鏈接)。盡管如此,除了在普遍意義上警惕強制轉(zhuǎn)型外,在性能敏感的代碼中,你應(yīng)該特別警惕 dynamic_casts。 對 dynamic_cast 的需要通常發(fā)生在這種情況下:你要在一個你確信為派生類的對象上執(zhí)行派生類的操作,但是你只能通過一個基類的指針或引
16、用來操控這個對象。有兩個一般的方法可以避免這個問題。 第一個,使用存儲著直接指向派生類對象的指針的容器,從而消除通過基類接口操控這個對象的需要。例如,如果在我們的 Window/SpecialWindow 繼承體系中,只有 SpecialWindows 支持 blinking,對于這樣的做法: class Window . ; class SpecialWindow: public Window public: void blink(; . ; typedef / see Item 13 for info std:vector VPW; / on tr1:shared_ptr VPW winP
17、trs; . for (VPW:iterator iter = winPtrs.begin(; / undesirable code: iter != winPtrs.end(; / uses dynamic_cast +iter if (SpecialWindow *psw = dynamic_cast(iter-get( psw-blink(; 設(shè)法用如下方法代替: typedef std:vector VPSW; VPSW winPtrs; . for (VPSW:iterator iter = winPtrs.begin(; / better code: uses iter != wi
18、nPtrs.end(; / no dynamic_cast +iter (*iter-blink(; 當然,這個方法不允許你在同一個容器中存儲所有可能的 Window 的派生類的指針。為了與不同的窗口類型一起工作,你可能需要多個類型安全(type-safe)的容器。 一個候選方法可以讓你通過一個基類的接口操控所有可能的 Window 派生類,就是在基類中提供一個讓你做你想做的事情的虛函數(shù)。例如,盡管只有 SpecialWindows 能 blink,在基類中聲明這個函數(shù),并提供一個什么都不做的缺省實現(xiàn)或許是有意義的: class Window public: virtual void blin
19、k( / default impl is no-op; . / see Item 34 for why ; / a default impl may be / a bad idea class SpecialWindow: public Window public: virtual void blink( . ; / in this class, blink . / does something ; typedef std:vector VPW; VPW winPtrs; / container holds / (ptrs to all possible . / Window types fo
20、r (VPW:iterator iter = winPtrs.begin(; iter != winPtrs.end(; +iter / note lack of (*iter-blink(; / dynamic_cast 無論哪種方法使用類型安全的容器或在繼承體系中上移虛函數(shù)都不是到處適用的,但在很多情況下,它們提供了 dynamic_casting 之外另一個可行的候選方法。當它們可用時,你應(yīng)該加以利用。你應(yīng)該絕對避免的一件東西就是包含了極聯(lián) dynamic_casts 的設(shè)計,也就是說,看起來類似這樣的任何東西:class Window . ; . / derived classes are defined here typedef std:vector VPW; VPW winPtrs; . for (VPW:iterator iter = winPtrs.begin(; iter != winPtrs.end(; +iter if (SpecialWin
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年人教新起點四年級語文上冊階段測試試卷含答案
- 2025年人教新起點必修1地理上冊月考試卷含答案
- 2025年冀少新版七年級數(shù)學(xué)上冊階段測試試卷含答案
- 幼兒園冬季滑雪安全教育
- 2025年上教版必修3物理上冊階段測試試卷
- 商務(wù)禮儀職業(yè)形象培訓(xùn)
- 企業(yè)員工職業(yè)生涯規(guī)劃模板
- 二手房交易特別約定合同版B版
- 2024版:鋼結(jié)構(gòu)建筑施工合同3篇
- 《外科護理》第二十章第四節(jié)化膿性骨髓炎病人的護理
- 2024氫氣長管拖車安全使用技術(shù)規(guī)范
- 部編版語文中考必背文言文7-9年級
- 《中外歷史綱要(上)》期末專題復(fù)習(xí)提綱
- TCALC 003-2023 手術(shù)室患者人文關(guān)懷管理規(guī)范
- 初中學(xué)生交通安全教育教案
- 國家職業(yè)技術(shù)技能標準 4-04-04-02 網(wǎng)絡(luò)與信息安全管理員(數(shù)據(jù)安全管理員)S 2024年版
- 腦卒中后吞咽障礙患者進食護理-2023中華護理學(xué)會團體標準
- 常見的口腔疾病護理
- 阿里巴巴集團swot分析通用課件
- 水電風(fēng)電項目審批核準流程課件
- 2024年國華人壽保險股份有限公司招聘筆試參考題庫含答案解析
評論
0/150
提交評論