1. 讓自己習(xí)慣 C++
條款 01:視 C++ 為一個(gè)語(yǔ)言聯(lián)邦
- C++ 高效編程守則視狀況而變化,取決于你使用 C++ 的哪一部分。
條款 02:盡量以 const,enum,inline 替換 #define
- 對(duì)于單純常量,最好使用 const 對(duì)象或 enums 替換 #defines。
- 對(duì)于形似函數(shù)的宏(macros),最好改用 inline 函數(shù)替換 #defines。
條款 03:盡可能使用 const
- 將某些東西聲明為 const 可幫助編譯器偵測(cè)出錯(cuò)誤用法。const 可被施加于任何作用域內(nèi)的對(duì)象、函數(shù)參數(shù)、函數(shù)返回類型、成員函數(shù)本體。
- 編譯器強(qiáng)制實(shí)施“物理上的常量性”(bitwise constness),但你編寫程序時(shí)應(yīng)使用“概念上的常量性”(conceptual constness)。
- 當(dāng) const 和 non-const 成員函數(shù)有著實(shí)質(zhì)等價(jià)的實(shí)現(xiàn)時(shí),令 non-const 版本調(diào)用 const 版本可避免代碼重復(fù)。
條款 04:確定對(duì)象被使用前已先被初始化
- 為內(nèi)置型對(duì)象進(jìn)行手工初始化,因?yàn)?C++ 不保證初始化它們。
- 構(gòu)造函數(shù)最好使用成員初值列(member initialization list),而不要在構(gòu)造函數(shù)本體內(nèi)使用賦值操作(assignment)。初值列列出的成員函數(shù),其排列次序應(yīng)該和它們?cè)陬愔械穆暶鞔涡蛳嗤?/li>
- 為免除“跨編譯單元之初始化次序”問(wèn)題,請(qǐng)以 local static 對(duì)象替換 non-local static 對(duì)象。
2. 構(gòu)造/析構(gòu)/賦值運(yùn)算
條款 05:了解 C++ 默默編寫并調(diào)用哪些函數(shù)
- 編譯器可以暗自為類創(chuàng)建默認(rèn)構(gòu)造函數(shù)、復(fù)制構(gòu)造函數(shù)、復(fù)制賦值操作符,以及析構(gòu)函數(shù)。
條款 06:若不想使用編譯器自動(dòng)生成的函數(shù),就該明確拒絕
- 為駁回編譯器自動(dòng)(暗自)提供的機(jī)能,可將相應(yīng)的成員函數(shù)聲明為私有并且不予實(shí)現(xiàn)。使用像 Uncopyable 這樣的基類也是一種做法。
條款 07:為多態(tài)基類聲明虛析構(gòu)函數(shù)
- 帶多態(tài)性質(zhì)的(polymorphic)基類應(yīng)該聲明一個(gè)虛析構(gòu)函數(shù)。如果類帶有任何虛函數(shù),它就應(yīng)該擁有一個(gè)虛析構(gòu)函數(shù)。
- 一個(gè)類的設(shè)計(jì)目的如果不是作為基類使用,或不是為了具備多態(tài)性(polymorphically),就不該聲明虛析構(gòu)函數(shù)。
條款 08:別讓異常逃離析構(gòu)函數(shù)
- 析構(gòu)函數(shù)絕對(duì)不要吐出異常。如果一個(gè)被析構(gòu)函數(shù)調(diào)用的函數(shù)可能拋出異常,析構(gòu)函數(shù)應(yīng)該捕獲任何異常,然后吞下它們(不傳播)或結(jié)束程序。
- 如果客戶需要對(duì)某個(gè)操作函數(shù)運(yùn)行期間拋出的異常做出反應(yīng),那么類應(yīng)該提供一個(gè)普通函數(shù)(而非在析構(gòu)函數(shù)中)執(zhí)行該操作。
條款 09:絕不在構(gòu)造和析構(gòu)過(guò)程中調(diào)用虛函數(shù)
- 在構(gòu)造和析構(gòu)期間不要調(diào)用虛函數(shù),因?yàn)檫@類調(diào)用從不下降至派生類(比起當(dāng)前執(zhí)行構(gòu)造函數(shù)和析構(gòu)函數(shù)的那層)。
條款 10:令 operator= 返回一個(gè) *this 的引用
- 令賦值(assignment)操作符返回一個(gè) *this 的引用,以支持連鎖賦值。
條款 11:在 operator= 中處理“自我賦值”
- 確保當(dāng)對(duì)象自我賦值時(shí) operator= 有良好行為。其中技術(shù)包括比較“來(lái)源對(duì)象”和“目標(biāo)對(duì)象”的地址、精心周到的語(yǔ)句順序、以及復(fù)制并交換。
- 確定任何函數(shù)如果操作一個(gè)以上的對(duì)象,其中多個(gè)對(duì)象是同一個(gè)對(duì)象時(shí),其行為依然正確。
條款 12:復(fù)制對(duì)象時(shí)勿忘其每一個(gè)成分
- 復(fù)制函數(shù)應(yīng)該確保復(fù)制“對(duì)象內(nèi)的所有成員變量”以及“所有基類成分”。
- 不要嘗試以某個(gè)復(fù)制函數(shù)實(shí)現(xiàn)另一個(gè)復(fù)制函數(shù)。應(yīng)該將共同機(jī)能放進(jìn)第三個(gè)函數(shù)中,并由兩個(gè)復(fù)制函數(shù)共同調(diào)用。
3. 資源管理
條款 13:以對(duì)象管理資源
- 為防止資源泄露,請(qǐng)使用 RAII 對(duì)象,它們?cè)跇?gòu)造函數(shù)中獲得資源并在析構(gòu)函數(shù)中釋放資源。
- 兩個(gè)常被使用的 RAII 類分別是 tr1::shared_ptr 和 auto_ptr。前者常是較佳的選擇,因?yàn)槠鋸?fù)制行為比較直觀。若選擇 auto_ptr,復(fù)制動(dòng)作會(huì)使它(被復(fù)制物)指向 null。
條款 14:在資源管理類中小心拷貝行為
- 復(fù)制 RAII 對(duì)象必須一并復(fù)制它所管理的資源,所以資源的復(fù)制行為決定 RAII 對(duì)象的復(fù)制行為。
- 普遍而常見的 RAII 類的復(fù)制行為是:抑制復(fù)制、試行引用計(jì)數(shù)(reference counting)。不過(guò)其他行為也都可能被實(shí)現(xiàn)。
條款 15:在資源管理類中提供對(duì)原始資源的訪問(wèn)
- API 往往要求訪問(wèn)原始資源(raw resources),所以每一個(gè) RAII 類應(yīng)該提供一個(gè)“取得其所管理之資源”的辦法。
- 對(duì)原始資源的訪問(wèn)可能經(jīng)由顯示轉(zhuǎn)換或隱式轉(zhuǎn)換。一般而言顯示轉(zhuǎn)換比較安全,但隱式轉(zhuǎn)換對(duì)客戶比較方便。
條款 16:成對(duì)使用 new 和 delete 時(shí)要采取相同形式
- 如果你在 new 表達(dá)式中使用 [],必須在相應(yīng)的 delete 表達(dá)式中也使用 []。如果你在 new 表達(dá)式中不使用 [],一定不要在相應(yīng)的 delete 表達(dá)式中使用 []。
條款 17:以獨(dú)立語(yǔ)句將 newed 對(duì)象置入智能指針
- 以獨(dú)立語(yǔ)句將 new 生成的對(duì)象存儲(chǔ)于(置入)智能指針內(nèi)。如果不這樣做,一旦異常被拋出,有可能導(dǎo)致難以察覺(jué)的資源泄露。
4. 設(shè)計(jì)與聲明
條款 18:讓接口容易被正確使用,不易被誤用
- 好的接口很容易被使用,不容易被誤用。你應(yīng)該在你的所有接口中努力達(dá)成這些性質(zhì)。
- “促進(jìn)正確使用”的辦法包括接口的一致性,以及與內(nèi)置類型的行為兼容。
- “阻止誤用”的辦法包括建立新類型、限制類型上的操作,束縛對(duì)象值,以消除客戶的資源管理責(zé)任。
- tr1::shared_ptr 支持定制型刪除器(custom deleter)。這可防范 DLL 問(wèn)題,可被用來(lái)自動(dòng)解除互斥鎖等等。
條款 19:設(shè)計(jì)類猶如設(shè)計(jì)類型
- 類的設(shè)計(jì)就是類型的設(shè)計(jì)。在定義一個(gè)新類型之前,請(qǐng)確保你已經(jīng)考慮過(guò)本條款覆蓋的所有討論主題。
條款 20:寧以傳常量引用替換傳值
- 盡量以傳遞常量引用替換傳值。前者通常比較高效,并可避免對(duì)象切割問(wèn)題。
- 以上規(guī)則并不適用于內(nèi)置類型,以及 STL 的迭代器和函數(shù)對(duì)象。對(duì)它們而言,傳值往往比較適當(dāng)。
條款 21:必須返回對(duì)象時(shí),別妄想返回其引用
- 絕不要返回指針或引用指向一個(gè) local stack 對(duì)象,或返回引用指向一個(gè) heap-allocated 對(duì)象,或返回指針或引用指向一個(gè) local static 對(duì)象而言有可能同時(shí)需要多個(gè)這樣的對(duì)象。條款 4 已經(jīng)為“在單線程中合理返回一個(gè) local static 對(duì)象”提供一份設(shè)計(jì)實(shí)例。
條款 22:將成員變量聲明為私有
- 切記將成員變量聲明為私有。這可賦予客戶訪問(wèn)數(shù)據(jù)的一致性、可細(xì)微劃分訪問(wèn)控制、允許約束條件獲得保證,并提供類作者以充分的實(shí)現(xiàn)彈性。
- protected 并不比 public 更具封裝性。
條款 23:寧以非成員、非友元替換成員函數(shù)
如果你需要為某個(gè)函數(shù)的所有參數(shù)(包括 this 指針?biāo)傅哪莻€(gè)隱藏參數(shù))進(jìn)行類型轉(zhuǎn)換,那么這個(gè)函數(shù)必須是個(gè)非成員函數(shù)。
寧可拿非成員非友元函數(shù)替換成員函數(shù)。這樣做可以增加封裝性、包裹彈性和機(jī)能擴(kuò)充性。
條款 24:若所有參數(shù)皆需類型轉(zhuǎn)換,請(qǐng)為此采用非成員函數(shù)
- 如果你需要為某個(gè)函數(shù)的所有參數(shù)(包括被 this 指針?biāo)傅哪莻€(gè)隱藏參數(shù))進(jìn)行類型轉(zhuǎn)換,那么這個(gè)函數(shù)必須是個(gè)非成員函數(shù)。
條款 25:考慮寫出一個(gè)不拋異常的 swap 函數(shù)
- 當(dāng) std::swap 對(duì)你的類型效率不高時(shí),提供一個(gè) swap 成員函數(shù),并確定這個(gè)函數(shù)不拋出異常。
- 如果你提供一個(gè)成員函數(shù)版 swap,也該提供一個(gè)非成員版 swap 用來(lái)調(diào)用前者。對(duì)于類(而非模板),也請(qǐng)?zhí)鼗?std::swap。
- 調(diào)用 swap 時(shí)應(yīng)針對(duì) std::swap 使用 using 聲明式,然后調(diào)用 swap 并且不帶任何“命名空間資格修飾”。
- 為“用戶定義類型”進(jìn)行 std templates 全特化是好的,但千萬(wàn)不要嘗試在 std 內(nèi)加入某些對(duì) std 而言全新的東西。
5. 實(shí)現(xiàn)
條款 26:盡可能延后變量定義式的出現(xiàn)時(shí)間
- 盡可能延后變量定義式的出現(xiàn)。這樣做可增加程序的清晰度并改善程序效率。
條款 27:盡量少做轉(zhuǎn)型動(dòng)作
- 如果可以,盡量避免轉(zhuǎn)型,特別是在注重效率的代碼中避免 dynamic_cast。如果有個(gè)設(shè)計(jì)需要轉(zhuǎn)型動(dòng)作,試著發(fā)展無(wú)需轉(zhuǎn)型的替代設(shè)計(jì)。
- 如果轉(zhuǎn)型是必須的,試著將它隱藏于某個(gè)函數(shù)背后。客戶隨時(shí)可以調(diào)用該函數(shù),而不需要將轉(zhuǎn)型放進(jìn)他們自己的代碼內(nèi)。
- 寧可使用 C++ 風(fēng)格(新式)轉(zhuǎn)型,不要使用舊式轉(zhuǎn)型。前者很容易辨別出來(lái),而且也比較有著分門別類的職掌。
條款 28:避免返回指向?qū)ο髢?nèi)部成分的句柄
- 避免返回句柄(包括引用、指針、迭代器)指向?qū)ο髢?nèi)部。遵守這個(gè)條款可增加封裝性,幫助 const 成員函數(shù)的行為像個(gè) const,并將發(fā)生“虛吊號(hào)碼牌”(dangling handles)的可能性降至最低。
條款 29:為“異常安全”而努力是值得的
- 異常安全函數(shù)(Exception-safe functions)即使發(fā)生異常也不會(huì)泄露資源或允許任何數(shù)據(jù)結(jié)構(gòu)敗壞。這樣的函數(shù)區(qū)分為三種可能的保證:基本型、強(qiáng)烈型、不拋異常型。
- “強(qiáng)烈保證”往往能夠以復(fù)制并交換實(shí)現(xiàn)出來(lái),但“強(qiáng)烈保證”并非對(duì)所有函數(shù)都可實(shí)現(xiàn)或具備現(xiàn)實(shí)意義。
- 函數(shù)提供的“異常安全保證”通常最高只等于其所調(diào)用之各個(gè)函數(shù)的“異常安全保證”中的最弱者。
條款 30:透徹了解內(nèi)聯(lián)的里里外外
- 將大多數(shù)內(nèi)聯(lián)限制在小型、被頻繁調(diào)用的函數(shù)身上。這可使日后的調(diào)試過(guò)程和二進(jìn)制升級(jí)更容易,也可使?jié)撛诘拇a膨脹問(wèn)題最小化,使程序的速度提升機(jī)會(huì)最大化。
- 不要只因?yàn)楹瘮?shù)模板出現(xiàn)在頭文件,就將它們聲明為內(nèi)聯(lián)。
條款 31:將文件的編譯依存關(guān)系降至最低
- 支持“編譯依存性最小化”的一般構(gòu)想是:相依與聲明式,不要相依于定義式。基于此構(gòu)想的兩個(gè)手段是句柄類(Handle classes)和接口類(Interface classes)。
- 程序庫(kù)頭文件應(yīng)該以“完全僅有聲明式”的形式存在。這種做法不論是否涉及模板都適用。
6. 繼承與面向?qū)ο笤O(shè)計(jì)
條款 32:確定你的公有繼承塑模出“是一個(gè)”關(guān)系
- “公有繼承”意味著“是一個(gè)”。適用于基類身上的每一件事情一定也適用于派生類身上,因?yàn)槊恳粋€(gè)派生類對(duì)象也都是一個(gè)基類對(duì)象。
條款 33:避免遮掩繼承而來(lái)的名稱
- 派生類內(nèi)的名稱會(huì)遮掩基類內(nèi)的名稱。在共有繼承下從來(lái)沒(méi)有人希望如此。
- 為了讓被遮掩的名稱再見天日,可使用 using 聲明式或轉(zhuǎn)交函數(shù)(forwarding functions)。
條款 34:區(qū)分接口繼承和實(shí)現(xiàn)繼承
- 接口繼承和實(shí)現(xiàn)繼承不同。在共有繼承下,派生類總是繼承基類的接口。
- 純虛函數(shù)只具體指定接口繼承。
- 簡(jiǎn)樸的非純虛函數(shù)具體指定接口繼承及缺省實(shí)現(xiàn)繼承。
- 非虛函數(shù)具體指定接口繼承以及強(qiáng)制性實(shí)現(xiàn)繼承。
條款 35:考慮虛函數(shù)以外的其他選擇
- 虛函數(shù)的替代方案包括 NVI 手法以及策略設(shè)計(jì)模式的多種形式。NVI 手法自身是一個(gè)特殊形式的模板方法設(shè)計(jì)模式。
- 將機(jī)能從成員函數(shù)移到類外部函數(shù),帶來(lái)的一個(gè)缺點(diǎn)是,非成員函數(shù)無(wú)法訪問(wèn)類的非公有成員。
- tr1::function 對(duì)象的行為就像一般函數(shù)指針。這樣的對(duì)象可接納“與給定之目標(biāo)簽名式兼容”的所有可調(diào)用物。
條款 36:絕不重新定義繼承而來(lái)的非虛函數(shù)
- 絕對(duì)不要重新定義繼承而來(lái)的非虛函數(shù)。
條款 37:絕不重新定義繼承而來(lái)的缺省參數(shù)值
- 絕對(duì)不要重新定義一個(gè)繼承而來(lái)的缺省參數(shù)值,因?yàn)槿笔?shù)值都是靜態(tài)綁定的,而虛函數(shù) -- 你唯一應(yīng)該覆寫的東西 -- 卻是動(dòng)態(tài)綁定的。
條款 38:通過(guò)復(fù)合塑模出“有一個(gè)”或“根據(jù)某物實(shí)現(xiàn)出”
- 復(fù)合的意義與共有繼承完全不同。
- 在應(yīng)用域,復(fù)合意味著有一個(gè)。在實(shí)現(xiàn)域,復(fù)合意味著根據(jù)某物實(shí)現(xiàn)出。
條款 39:明智而審慎地使用私有繼承
- 私有繼承意味著根據(jù)某物實(shí)現(xiàn)出。它通常比復(fù)合的級(jí)別低。但是當(dāng)派生類需要訪問(wèn)基類的保護(hù)成員,或需要重新定義繼承而來(lái)的虛函數(shù)時(shí),這么設(shè)計(jì)是合理的。
- 和復(fù)合不同,私有繼承可以造成空基類優(yōu)化。這對(duì)致力于“對(duì)象尺寸最小化”的程序庫(kù)開發(fā)者而言,可能很重要。
條款 40:明智而審慎地使用多重繼承
- 多重繼承比單一繼承復(fù)雜。它可能導(dǎo)致新的歧義,以及對(duì)虛繼承的需要。
- 虛繼承會(huì)增加大小、速度、初始化(及賦值)復(fù)雜度等等成本。如果虛基類不帶任何數(shù)據(jù),將是最具實(shí)用價(jià)值的情況。
- 多重繼承的確有正當(dāng)用途。其中一個(gè)情節(jié)涉及“共有繼承某個(gè)接口類”和“私有繼承某個(gè)協(xié)助實(shí)現(xiàn)的類”的兩相組合。
7. 模板與泛型編程
條款 41:了解隱式接口和編譯期多態(tài)
條款 42:了解 typename 的雙重意義
條款 43:學(xué)習(xí)處理模板化基類內(nèi)的名稱
條款 44:將與參數(shù)無(wú)關(guān)的代碼抽離模板
條款 45:運(yùn)用成員函數(shù)模板接受所有兼容類型
條款 46:需要類型轉(zhuǎn)換時(shí)請(qǐng)為模板定義非成員函數(shù)
條款 47:請(qǐng)使用萃取類表現(xiàn)類型信息
條款 48:認(rèn)識(shí)模板元編程
8. 定制 new 和 delete
條款 49:了解 new-handler 的行為
條款 50:了解 new 和 delete 的合理替換時(shí)機(jī)
條款 51:編寫 new 和 delete 時(shí)需固守常規(guī)
條款 52:寫了原位 new 也要寫原位 delete
9. 雜項(xiàng)討論
條款 53:不要輕易忽視編譯器的警告
條款 54:讓自己熟悉包括 TR1 在內(nèi)的標(biāo)準(zhǔn)程序庫(kù)
條款 55:讓自己熟悉 Boost