1 .讓自己習慣C++
條款01 視C++為一個語言聯邦
CObject-Oriented C++Template C++STLC++高效編程守則視情況而變化,取決于你使用C++的哪一部分,
條款02 盡量與const,enum,inline替換#define
- 對于單純常量,最好以
const物件或enums替換#defines, - 對于形似函式的宏(
macros),最好改用inline函式替換#defines,
條款03 盡可能使用const
- 將某些東西宣告為
const可以幫助編譯器偵測出錯誤用法,const可被施加于任何作用域內的物件、函式引數、函式回傳型別、成員函式本體, - 編譯器強制實施
bitwise constness,但你撰寫程式時應該使用“概念上的常量性”(conceptual constness), - 當
const和non-const成員函式有著實質等價的實作時,令con-const版本呼叫const版本可避免代碼重復,
條款04 確定物件使用前已被初始化
- 為內置型別物件進行手工初始化,因為
C++不保證初始化他們, - 建構式最好使用成員初始值列(
member initialization list),而不要在建構式本體內使用賦值操作(assignment),初始值列列出的成員變數,其排列次序應該和它們在class中宣告的次序相同, - 為免除“跨編譯單元之初始化次序”問題,請以
local static物件替換non-local static物件,
2. 構造/析構/賦值運算
條款05 了解C++默認撰寫并呼叫哪些函式
- 編譯器可以暗自為
class創建default建構式、copy建構式和copy assignment運算子,以及解構式,(C++11開始還有move constructor和move assignment),
條款06 若不想使用編譯器自動生成的函式,就應該明確拒絕
- 為駁回編譯器自動(暗自)提供的機能,可將相應的成員函式宣告為
private并且不予實作,使用像uncopyable這樣的base calss也是一種做法,(C++11以后可以使用=delete告訴編譯器洗掉不需要的成員函式,)
條款07 為多型積累宣告virtual解構式
- 多型性質的
base calsses應該宣告一個virtual解構式,如果class帶有任何virtual函式,它就應該擁有一個virtual解構式, Classes的設計目的如果不是作為base classes使用,或不是為了具備多型(polymorphically),就不應該宣告virtual解構式,
條款08 別讓例外逃離解構式
- 解構式絕對不要吐出例外,如果一個被析構哈你數呼叫的函式可能拋出例外,解構式應該捕捉人分和例外,并吞下它們或結束程式,
- 如果客戶需要對某個操作函式運行期間拋出的例外做出反應,那么
class應該提供一個普通函式(而非解構式中)執行該操作,
條款09 絕不在構造和析構程序中呼叫virtual函式
- 在構造和析構期間不要呼叫
virtual函式,因為這類呼叫從不下降至derived class,
條款10 令operator=回傳一個reference to *this
- 為了實作“連鎖賦值”,應該令
operator=回傳一個reference to *this,
條款11 在operator=中處理“自我賦值”
- 確保當物件自我賦值時
operator=有良好的行為,其中技術包括比較“來源物件”和“目標物件”的地址、精心周到的陳述句順序、以及copy-and-swap, - 確定任何函式如果操作一個以上的物件,而其中多個物件時同一個物件時,其行為仍然正確,
條款12 復制物件時勿忘其每一個成分
Copying函式應該確保賦值“物件內的所有成員變數”及“所有base class”成分,- 不要嘗試以某個
copying函式實作另一個copying函式,應該將共同機能放在第三個函式中,并有兩個copying函式共同呼叫,
3. 資源管理
條款13 以物件管理資源
- 為了防止資源泄露,請使用
RAII物件,它們在建構式中獲得資源并在解構式中釋放資源, - 兩個常被使用的
RAII Class分別時tr1::shared_ptr和auto_ptr,前者通常是較好的選擇,因為其copy行為比較直觀,若選擇auto_ptr,賦值動作會使它(被復制物)指向null,(C++11中使用std::shared_ptr、std::unique_ptr和std::weak_ptr代替了兩者,)
條款14 在資源管理類中小心copying行為
- 復制
RAII物件必須一并復制它所管理的資源,所以資源的copying行為決定RAII物件的copying行為, - 普通常見的
RAII class copying行為是:抑制copying、實行參考計數法(reference counting),不過其行為也都可以被實作,
條款15 在資源管理類中提供對原始資源的訪問
APIs往往要求訪問原始資源(raw resources),所以每一個RAII Class應該提供一個“取得其所管理之資源”的辦法,- 對原始資源的訪問可能經由顯式轉換或隱式轉換,一般而言顯式轉換比較安全,隱式轉換對客戶比較方便,
條款16 成對使用new和delete時要采取相同形式
- 如果在
new運算式中使用[],必須在相應的delete運算式中也使用[],如如果在new運算式中不使用[],一定不要在相應的delete運算式中也使用[],
條款17 以獨立陳述句蔣newed物件置入智能指標
- 以獨立陳述句將
newed物件存盤于(置入)智能指標內,如果不這樣做,一旦例外被拋出,有可能導致難以察覺的資源泄露,
4. 設計與宣告
條款18 讓介面容易被正確使用,不易被吳用
- 好的介面很容易被正確使用,不容易被誤用,應該在所有的介面中努力達成這些性質,
- “促進正確使用”的辦法包括介面的一致性,以及與內置型別的行為兼容,
- “阻止誤用”的辦法包括建立新型別、限制型別上的操作,束縛物件值,以及消除客戶的資源管理責任,
條款19 設計class猶如設計type
Class的設計就是type的設計,應該帶著和“語言設計者當初設計語言內置型別”時一樣的謹慎來研討class的設計,
條款20 寧以pass-by-reference-to-const 替換 pass-by-value
- 盡量以
pass-by-reference-to-const替換pass-by-value,前者通常比較高效,并可避免切割問題,(slicing problem) - 以上規則并不適用于內置型別,以及
STL的迭代器和函式物件,對它們而言,pass-by-value往往比較適當,
條款21 必須回傳物件時,別妄想回傳其reference
- 絕不要回傳
pointer或reference指向一個local stack物件,或回傳reference指向一個heap-allocated物件,或回傳pointer或reference指向一個local static物件而有可能同事需要多個這樣的物件,
條款22 將成員變數宣告為private
- 切記將成員變數宣告為
private,這可賦予客戶訪問資料的一致性、可細微劃分訪問控制、允諾約束條件獲得保證,并提供class作者以充分的實作彈性, protected并不比public更具封裝性,
條款23 寧以non-member、non-friend替換number函式
- 寧可拿
non-member non-friend函式替換member函式,這樣做可以增加封裝性、包裹彈性(packing flexibility)和機能擴充性,
條款24 若所有引數皆需型別轉換,請為此采用non-number函式
- 如果需要為某個函式的所有引數(包括被
this指標所指向的那個隱喻引數)進行型別轉換,那么這個函式必須是個non-member,
條款25 考慮寫出一個不拋棄例外的swap函式
- 當
std::swap對你的型別效率不高時,提供一個swap成員函式,并確定這個函式不拋出例外, - 如果你提供一個
member swap,也該提供一個non-member swap用來呼叫前者,對于class(而非template),也請特化std;;swap, - 呼叫
swap時應針對std::swap使用using宣告式,然后呼叫swap并且不帶任何“命名空間資格修飾”, - 為“用戶定義型別”進行
std templates全特化時好的,但千萬不要嘗試在std內加入某些對std而言全新的東西,
5. 實作
條款26 盡可能延后變數定義式的出現時間
- 盡可能延后變數定義式的出現,這樣做可增加程式的清晰度并改善程式效率,
條款27 盡量少做轉型動作
- 如果可以,盡量避免轉型,特別時在注重效率的代碼中避免
dynamic_casts,如果有個設計需要轉型動作,試著發展無需轉型的替代設計, - 如果轉型時必須的,試著將它隱藏于某個函式背后,客戶隨后可以呼叫該函式,而不需將轉型放進自己的代碼內,
- 寧可使用
C++-style(新式)轉型,而不是用舊式轉型,前者很容易辨識出來,而且也比較有著分門別類的執掌,
條款28 避免回傳handles指向物件內部成員
- 避免回傳
handles(包括references、指標、迭代器)指向內部物件,遵守這個條款可增加封裝性,幫助const成員函式的行為像個const,并將發生dangling handlers的可能性降至最低,
條款29 為“例外安全”而努力是值得的
- 例外安全函式(
Exception-salf functions)即使發生例外也不會泄露資源或允許任何資料結構破壞,這樣的函式區分為三種可能的保證:基本型、強烈型、不拋例外型, - “強烈保證”往往能夠以
copy-and-swap實作出來,但“強烈保證”并非對所有函式都可以實作或具備實作意義, - 函式提供的“例外安全保證”通常最高只等于其所呼叫之各個函式的“例外安全保證”中的最弱者,
條款30 透徹了解inlining的里里外外
- 將大多數
inlining限制在小型、被頻繁呼叫的函式身上,這可使日后的除錯程序和二進制升級(binary upgradability)更容易,也可使潛在的代碼膨脹問題最小化,使程式的速度提升機會最大化, - 不要只因為
function templates出現在頭檔案,就將它們宣告為inline,
條款31 將檔案間的編譯依存關系降至最低
- 支持“編譯依賴最小化”的一般構想是:依賴于宣告式,不要依賴于定義式,基于此構想的兩個手段時
Handle classes和Interface classes, - 程式庫頭檔案應該以“完全且僅有宣告式”(
full and declaration-only forms)的形式存在,這種做法不論是否設計templates都適用,
6. 繼承與面向物件
條款32 確定你的public繼承塑模出Is-a關系
- “
public繼承”意味Is-a,適用于base class身上的每一件事情一定也適用于derived classes身上,因為每一個“derived class”物件也是一個base class物件,
條款33 避免遮掩繼承而來的名稱
derived class內的名稱會遮掩base class內的名稱,在public繼承下從來沒有人希望如此,- 為了讓被遮掩的名稱再見天日,可使用
using宣告式或轉交函式(forwarding functions),
條款34 區分介面繼承和實作繼承
- 介面繼承和實作繼承不同,在
public繼承之下,derived classes總是繼承base class的介面, - 純虛(
pure virtual)函式只具體指定介面繼承, - 非純虛(
impure virtual)函式具體指定介面繼承及預設實作繼承, non-virtual函式具體指定介面繼承預計強制性實作繼承,
條款35 考慮virtual函式以外的其他選擇
virtual函式的替代方案包括NVI手法及Strategy設計模式的多種形式,NVI手法自身時一個特殊形式的Template Method設計模式,- 將機能從成員函式移到
class外部函式,帶來的一個缺點時,非成員函式無法訪問class的non-public成員, tr1::function(C++11已經移到std::function)物件的行為就像一般函式指標,這樣的物件可接納“與給定之目標簽名式(target signature)兼容”的所有可呼叫物(callable entities),
條款36 絕不重新定義繼承而來的non-virtual函式
- 絕對不要重新定義繼承而來的
non-virtual函式,
條款37 絕不重新定義繼承而來的預設引數
- 絕對不要重新定義一個繼承而來的預設引數值,因為預設引數值是靜態系結,而
virtual函式——你唯一應該覆寫的定西——確是動態系結,
條款38 通過復合塑模出has-a或“根據某物實作出”
- 復合(
composition)的意義和public繼承完全不同, - 在應用域(
application domain),復合意味著has-a(有一個),在實作域(implementation domain),復合意味著is-implemented-in-terms-of(根據某物實作出),
條款39 明智而審慎的使用private繼承
Private繼承意味著is-implemented-in-terms-of(根據某物實作出),它通常比復合(composition)的級別低,但是當derived class需要訪問protected base class的成員,或需要重新定義繼承而來的virtual函式時,這么設計時合理的,- 和復合(
composition)不同,private繼承可以造成empty base最優化,這對致力于“物件尺寸最小化”的程式庫開發者而言,可能很重要,
條款40 明智而審慎的使用多重繼承
- 多重繼承比單一繼承復雜,它可能導致新的歧義性,以及對
virtual繼承的需要, virtual繼承會增加大小、速度、初始化(及賦值)復雜度等成本,如果virtual base classes不帶任何資料,將時最具有實用價值的情況,- 多重繼承的確有正當用途,其中一個情節涉及“
public繼承某個interface class”和“private繼承某個協助實作的class”的兩相組合,
7. 模板與泛型編程
條款41 了解隱式介面和編譯器多型
classes和templates都支持介面(interface)接多型(polymorphism),- 對
classes而言介面時顯式的(explicit),以函式簽名為中心,多型則是通過virtual函式發生于運行期, - 對于
templates引數而言,介面是隱式的(implicit),奠基于有效運算式,多型則時通過template具現化和函式多載 決議(function overloading resolution)發生于編譯期,
條款42 了解typename的雙重意義
- 宣告
template引數時,前綴關鍵字class和typename可互換, - 請使用關鍵字
typename標識嵌套從屬型別名稱;但不得在base class lists(基類列)或member initialization list(成員初始值列)內以它最為base class修飾符,
條款43 學習處理模板化基類內的名稱
- 可在
derived class templates內通過this->指涉base class templates內的成員名稱,或由一個明白寫出的“base class資格修飾符”完成,
條款44 將與引數無關的代碼抽離templates
Templates生成多個classes和多個函式,所有任何template代碼都不該與某個造成膨脹的template引數產生相依關系,- 因非型別模板引數(
non-type template parameters)而造成的代碼膨脹往往可以消除,做法是以函式引數或class成員變數替換template引數,
條款45 運用成員函式末班接受所有兼容型別
- 請使用
member functions templates(成員函式模板)生成“可接受所有兼容型別”的函式, - 如果你宣告
member templates用于“泛化copy構造”或“泛化assignment操作”,你還是需要宣告正常的copy建構式和copy assignment運算子,
條款46 需要型別轉換時請為模板定義非成員函式
- 當撰寫一個
class template,而它所提供的“與此template相關的”函式支持“所有引數之隱式型別轉換”時,請將那些函式定義為“class template內部的friend函式”,
條款47 請使用traits classes表現型別資訊
Traits classes使得“型別相關資訊”在編譯期可用,它們以template和"template特化"完成實作,- 整合多載技術(
overloading)后,traits calsses有可能在編譯器對型別執行if...else測驗,
條款48 認識template元編程
Template metaprogramming(TPM,模板元編程)可將作業由運行期移到編譯期,因而得以實作早期錯誤偵測和更高的執行效率,TMP可被用來生成“基于政策選擇組合”(based on combinations of policy choices)的客戶定制代碼,也可用來避免生成對某些特殊型別并不適合的代碼,
8. 定制new和delete
條款49 了解new-handler的行為
set_new_handler允許客戶指定一個函式,在記憶體分配無法獲得滿足時被呼叫,Nothrow new是一個頗為局限的工具,因為它只適用于記憶體分配,后繼的建構式呼叫還是可能拋出例外,
條款50 了解new和delete的合理替換時機
- 有許多理由需要寫個自定義的
new和delete,包括改善效能、對heap運用錯誤進行除錯、收集heap使用資訊,
條款51 撰寫new和delete時需固守常規
operator new應該內含一個無窮回圈,并在其中嘗試分配記憶體,如果它無法滿足記憶體需求,就該呼叫new-handler,它也應該有能力處理0 byte申請,class專屬版本則還應該處理“比正確大小更大的(錯誤)申請”,operator delete應該在收到null指標時不做任何事,class專屬版本則還應該處理“比正確大小更大的(錯誤)申請”,
條款52 寫了placement new也要寫placement delete
- 當你寫一個
placement operator new,請確定也寫出對應的placement operator delete,如果沒有這樣做,你的程式可能會發生隱微而時斷時續的記憶體泄露, - 當你宣告
placement new和placement delete時,確定不要無意識(非故意)的掩蓋了它們的正常版本,
9. 雜項討論
條款53 不要輕忽編譯器的警告
- 請嚴肅對待編譯器發出的警告資訊,努力在你的編譯器的最高(最嚴苛)警告級別下爭取“無任何警告”的榮譽,
- 不要過度依賴編譯器的報警能力,因為不同的編譯器對待事情的態度并不相同,一旦移植到另一個編譯器上,你原本依賴的警告資訊有可能消失,
條款54 讓自己熟悉包括TR1在內的標準程式庫
C++標準庫的主要機能由STL、iostreams、locales組成,并包含C99標準程式庫,TR1添加了智能指標(例如tr1::shared_ptr)、一般化函式指標(tr1::function)、hash-based容器(unorderd_map,unordered_set)、正則運算式(regular expressions)以及另外10個組件的支持,TR1自身只是一份規范,為了獲得TR1提供的好處,你需要一份實物,一個好的實物來源時Boost,
條款55 讓自己熟悉Boost
Boost是一個社群,也是一個網站,致力于免費、原始碼開放、同僚復審的C++程式庫開發,Boost在C++標準化程序中扮演深具影響力的角色,Boost提供許多TR1組件的實作品,以及其他許多程式庫
關注我,帶你21天“精通”C++!(狗頭)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/556691.html
標籤:其他
上一篇:Python中startswith()和endswith()方法
下一篇:返回列表
