《Effective C++ 改善程式與設計的55個具體做法》讀書筆記

来源:https://www.cnblogs.com/binarch/archive/2023/07/06/17533459.html
-Advertisement-
Play Games

### 1 .讓自己習慣C++ #### 條款01 視C++為一個語言聯邦 * `C` * `Object-Oriented C++` * `Template C++` * `STL` * `C++`高效編程守則視情況而變化,取決於你使用`C++`的哪一部分。 #### 條款02 儘量與const, ...


1 .讓自己習慣C++

條款01 視C++為一個語言聯邦

  • C
  • Object-Oriented C++
  • Template C++
  • STL
  • C++高效編程守則視情況而變化,取決於你使用C++的哪一部分。

條款02 儘量與const,enum,inline替換#define

  • 對於單純常量,最好以const對象或enums替換#defines
  • 對於形似函數的巨集(macros),最好改用inline函數替換#defines

條款03 儘可能使用const

  • 將某些東西聲明為const可以幫助編譯器偵測出錯誤用法。const可被施加於任何作用域內的對象、函數參數、函數返回類型、成員函數本體。
  • 編譯器強制實施bitwise constness,但你編寫程式時應該使用“概念上的常量性”(conceptual constness)。
  • constnon-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 constructormove 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_ptrauto_ptr。前者通常是較好的選擇,因為其copy行為比較直觀。若選擇auto_ptr,賦值動作會使它(被覆制物)指向null。(C++11中使用std::shared_ptrstd::unique_ptrstd::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

  • 絕不要返回pointerreference指向一個local stack對象,或返回reference指向一個heap-allocated對象,或返回pointerreference指向一個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 classesInterface 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外部函數,帶來的一個缺點時,非成員函數無法訪問classnon-public成員。
  • tr1::functionC++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 瞭解隱式介面和編譯器多態

  • classestemplates都支持介面(interface)接多態(polymorphism)。
  • classes而言介面時顯式的(explicit),以函數簽名為中心,多態則是通過virtual函數發生於運行期。
  • 對於templates參數而言,介面是隱式的(implicit),奠基於有效表達式。多態則時通過template具現化和函數重載 解析(function overloading resolution)發生於編譯期。

條款42 瞭解typename的雙重意義

  • 聲明template參數時,首碼關鍵字classtypename可互換。
  • 請使用關鍵字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 metaprogrammingTPM,模板元編程)可將工作由運行期移到編譯期,因而得以實現早期錯誤偵測和更高的執行效率。
  • TMP可被用來生成“基於政策選擇組合”(based on combinations of policy choices)的客戶定製代碼,也可用來避免生成對某些特殊類型並不適合的代碼。

8. 定製new和delete

條款49 瞭解new-handler的行為

  • set_new_handler允許客戶指定一個函數,在記憶體分配無法獲得滿足時被調用。
  • Nothrow new是一個頗為局限的工具,因為它只適用於記憶體分配,後繼的構造函數調用還是可能拋出異常。

條款50 瞭解new和delete的合理替換時機

  • 有許多理由需要寫個自定義的newdelete,包括改善效能、對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++標準庫的主要機能由STLiostreamslocales組成。並包含C99標準程式庫。
  • TR1添加了智能指針(例如tr1::shared_ptr)、一般化函數指針(tr1::function)、hash-based容器(unorderd_map,unordered_set)、正則表達式(regular expressions)以及另外10個組件的支持。
  • TR1自身只是一份規範。為了獲得TR1提供的好處,你需要一份實物。一個好的實物來源時Boost

條款55 讓自己熟悉Boost

  • Boost是一個社群,也是一個網站。致力於免費、源碼開放、同僚覆審的C++程式庫開發。BoostC++標準化過程中扮演深具影響力的角色。
  • Boost提供許多TR1組件的實現品,以及其他許多程式庫

關註我,帶你21天“精通”C++!(狗頭)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 轉載請註明出處(~ ̄▽ ̄)~嚴禁用於商業目的的轉載~ 導語:同學,你也不想你根本不懂ClickHouse,卻趕鴨子上架使用的事情被其他人知道吧? 寫在前面:本文旨在讓原先有一定SQL基礎的人快速簡單瞭解ClickHouse的(關鍵)概念/特性,側重於使用方面的介紹比較而非原理/實現挖掘。文章算是個人 ...
  • OTA(Over-The-Air,空中下載技術),是汽車使用的一種遠程無線升級技術。車主只要保持聯網的狀態,汽車就可以像手機一樣進行系統升級。OTA升級最大的好處是,不管對車輛進行常規的升級還是大面積出現軟體問題需要修複,都可以通過OTA來解決。 數據顯示,2022年中國乘用車OTA裝配量達到765 ...
  • 博客推行版本更新,成果積累制度,已經寫過的博客還會再次更新,不斷地琢磨,高質量高數量都是要追求的,工匠精神是學習必不可少的精神。因此,大家有何建議歡迎在評論區踴躍發言,你們的支持是我最大的動力,你們敢投,我就敢肝 ...
  • 大促備戰,最大的隱患項之一就是慢sql,帶來的破壞性最大,也是日常工作中經常帶來整個應用抖動的最大隱患,而且對sql好壞的評估有一定的技術要求,有一些缺乏經驗或者因為不夠仔細造成一個壞的sql成功走到了線上,等發現的時候要麼是造成了線上影響、報警、或者後置的慢sql採集發現,這時候一般無法快速止損,... ...
  • 摘要:2023可信資料庫發展大會上,華為雲資料庫服務產品部總經理蘇光牛圍繞華為雲GaussDB的產品能力和實踐進行了分享 本文分享自華為雲社區《華為雲GaussDB亮相2023可信資料庫發展大會,榮獲三項評測證書!》,作者: GaussDB 資料庫。 近日,由中國信息通信研究院、中國通信標準化協會指 ...
  • ![](https://img2023.cnblogs.com/blog/3076680/202307/3076680-20230704144741811-1382373830.png) # 1. 術語的定義 ## 1.1. 服務 ### 1.1.1. 指共同協作、以單元的形式對外提供功能的跨機器進 ...
  • 一般發生在程式開始部分: `from pymodbus.client.sync import ModbusSerialClient` `from pymodbus.payload import BinaryPayloadDecoder` `from pymodbus.constants import ...
  • # Java 構造器 # 1. 構造器 ## 構造器也叫構造方法,是用來完成對象的初始化。 ## 構造器的定義: > ## 構造器的定義:[訪問修飾符] 方法名(形參),構造器與方法不同,並沒有返回值,也不能寫void,訪問修飾符可以是不同的,方法名要與本類的類名相同 > > ## 構造器的調用是由 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...