首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Effective C++ 55 條款精華導讀:寫出高質量 C++ 代碼的核心準則

     編輯:關於C++

Scott Meyers 的《Effective C++》被公認為 C++ 程序員必讀經典。55 條款涵蓋資源管理、類設計、模板泛型、繼承多態、並發等核心領域。本文提煉各章精華,結合現代 C++ (C++11/14/17/20) 視角,助你快速建立專業級編碼直覺。

第 1 章:讓自己習慣 C++

  • 條款 1:視 C++ 為語言聯邦 —— C++ = C + OOP + Template + STL,不同子語言守則不同。
  • 條款 2:以 const, enum, inline 替換 #define —— 宏無作用域、不進符號表、難調試;常量用 constexpr (C++11),函數用 inline/constexpr。
  • 條款 3:盡可能使用 const —— 讓編譯器幫你找錯;const 成員函數承諾邏輯不變狀態;mutable 配合緩存/鎖。
  • 條款 4:確定對象使用前已初始化 —— 內置類型無自動初始化;構造函數用成員初始化列表(順序 = 聲明順序);跨編譯單元用 local static 替換 non-local static 解決初始化順序災難。

第 2 章:構造/析構/賦值運算

  • 條款 5:了解 C++ 默默編寫並調用哪些函數 —— 默認構造、拷貝構造、拷貝賦值、移動構造、移動賦值、析構(C++11 共 6 個)。
  • 條款 6:若不想用編譯器生成的函數,明確拒絕 —— C++11 用 = delete,比私有未定義更清晰。
  • 條款 7:為多態基類聲明 virtual 析構函數 —— 核心鐵律,見條款 5 實驗三。
  • 條款 8:別讓異常逃離析構函數 —— 析構中捕獲並吞噬/終止,否則雙重異常導致 std::terminate。
  • 條款 9:絕不在構造/析構中調用 virtual 函數 —— 此時虛表指針指向當前類,派生類覆寫不可見。
  • 條款 10:令 operator= 返回 *this 引用 —— 支持鏈式賦值 a = b = c。
  • 條款 11:在 operator= 中處理「自我賦值」 —— 比較地址、copy-and-swap 慣用語(強異常安全)。
  • 條款 12:復制對象時勿忘其每一個成分 —— 所有成員 + 所有基類部分;別互調拷貝函數,提取公共 init() / copyFrom()。

第 3 章:資源管理

  • 條款 13:以對象管理資源 (RAII) —— 構造獲取,析構釋放;std::unique_ptr/shared_ptr 替代 auto_ptr (已移除)。
  • 條款 14:在資源管理類中小心 copying 行為 —— 抑制拷貝 (=delete) 或引用計數 (shared_ptr) 或深拷貝,視語義而定。
  • 條款 15:在資源管理類中提供對原始資源的訪問 —— 顯式轉換 get() 更安全,隱式轉換更便利。
  • 條款 16:成對使用 new 和 delete 時采取相同形式 —— new[] 配 delete[],單對象配單 delete。
  • 條款 17:以獨立語句將 new 出的對象置入智能指針 —— 避免 func(shared_ptr(new T), g()) 中 g() 拋異常導致洩漏。

第 4 章:設計與聲明

  • 條款 18:讓介面容易正確使用,難以誤用 —— 類型封裝、delete 不想要的重載、RAII 替換裸資源。
  • 條款 19:設計 class 猶如設計 type —— 考慮創建/銷毀、初始化/賦值、合法值、繼承、轉換、泛化關係。
  • 條款 20:寧以 pass-by-reference-to-const 替換 pass-by-value —— 避免切片、省拷貝;內置類型/迭代器/函數對象除外。
  • 條款 21:必須返回對象時,別妄想返回其 reference —— 絕不返回局部對象/臨時對象/靜態對象引用(多線程不安全)。
  • 條款 22:將成員變量聲明為 private —— 封裝性、不變量、二進制兼容;protected 不比 public 更封裝。
  • 條款 23:寧以 non-member, non-friend 替換 member 函數 —— 增加封裝、擴展性(如 std::begin/end)。
  • 條款 24:若所有參數皆需類型轉換,請採用 non-member 函數 —— 隱式轉換對稱性(如 operator*)。
  • 條款 25:考慮寫出不拋異常的 swap —— pimpl、泛型代碼關鍵;提供 member swap + non-member swap + std::swap 特化。

第 5 章:實現

  • 條款 26:盡可能延後變量定義式 —— 減少構造/析構開銷,縮小作用域,提高清晰度。
  • 條款 27:盡量少做轉型 —— 設計消除轉型;必須轉型用 static_cast/dynamic_cast/reinterpret_cast/const_cast,隱藏在函數內。
  • 條款 28:避免返回 handles 指向對象內部 —— 破壞封裝、懸空指針、const 失效。
  • 條款 29:為「異常安全」而努力 —— 三級保證:基本型(不洩漏、不壞)、強烈型(回滾或成功)、不拋異常型;copy-and-swap 實現強保證。
  • 條款 30:透徹了解 inlining 裡裡外外 —— 小熱點函數內聯;頭文件模板非必須 inline;避免代碼膨脹、調試困難、二進制升級阻礙。
  • 條款 31:將文件間編譯依賴降至最低 —— 依賴聲明不依賴定義;Pimpl 慣用語、接口類、前置聲明、#include 最小化。

第 6 章:繼承與面向對象設計

  • 條款 32:確定 public 繼承塑模出 is-a 關係 —— 基類契約對派生類必須成立;違反即設計錯誤。
  • 條款 33:避免遮蔽繼承而來的名稱 —— 派生類同名函數隱藏基類所有同名重載;用 using Base::func 或轉發函數恢復。
  • 條款 34:區分接口繼承與實現繼承 —— pure virtual 只繼承接口;impure virtual 繼承接口+默認實現;non-virtual 繼承接口+強制實現。
  • 條款 35:考慮 virtual 函數以外的其他選擇 —— NVI (Template Method)、Strategy 模式 (std::function/lambda)、外部非成員函數。
  • 條款 36:絕不重定義繼承而來的 non-virtual 函數 —— 靜態綁定導致基類指針調用基類版本,語義混亂。
  • 條款 37:絕不重新定義繼承而來的緩省參數值 —— 緩省參數靜態綁定,虛函數動態綁定,混用極危險。
  • 條款 38:通過復合塑模出 has-a 或 is-implemented-in-terms-of —— 應用域 has-a,實現域 is-implemented-in-terms-of。
  • 條款 39:明智審慎地使用 private 繼承 —— 實現繼承(需訪問 protected/重寫 virtual);空基優化 (EBO) 省空間。
  • 條款 40:明智審慎地使用多重繼承 —— 鑽石繼承需 virtual 繼承;介面類 public + 實現類 private 是常見合法模式。

第 7 章:模板與泛型編程

  • 條款 41:了解隱式接口與編譯期多態 —— 類:顯式接口 + 運行期多態;模板:隱式接口 (有效表達式) + 編譯期多態 (實例化/重載解析)。
  • 條款 42:了解 typename 的雙重意義 —— 模板參數 class/typename 可換;嵌套從屬類型必須加 typename (基類列表/初始化列表除外)。
  • 條款 43:學習處理模板化基類內的名稱 —— this->member 或 Base::member 延遲查找。
  • 條款 44:將與參數無關的代碼抽離 —— 避免代碼膨脹;非類型參數改函數參數/成員;相同二進制佈局的具現類型共享實現 (如 void* + 模板包裝)。
  • 條款 45:運用成員函數模板接受所有兼容類型 —— 泛化拷貝構造/賦值;仍需聲明普通拷貝/賦值版本。
  • 條款 46:需要類型轉換時請為模板定義非成員函數 —— 友元函數模板支援所有參數隱式轉換。
  • 條款 47:請使用 traits classes 表現類型信息 —— 編譯期 if-else (SFINAE/if constexpr (C++17)) 基石。
  • 條款 48:認識 template 元編程 (TMP) —— 計算移編譯期,早期報錯、零運行期開銷、策略組合生成代碼。

第 8 章:定制 new 和 delete

  • 條款 49:了解 new-handler 行為 —— std::set_new_handler 設置分配失敗回調;nothrow new 僅覆蓋分配階段。
  • 條款 50:了解 new/delete 合理替換時機 —— 效能優化、堆診斷、內存池、對齊控制。
  • 條款 51:編寫 new/delete 時需固守常規 —— 無限循環嘗試分配、調用 new_handler、處理 0 字節、類專屬版本檢查大小。
  • 條款 52:寫了 placement new 也要寫 placement delete —— 構造拋異常時調用對應 delete 釋放內存,否則洩漏。

第 9 章:雜項

  • 條款 53:不要忽略編譯器警告 —— 最高警告級別追求 0 警告;跨編譯器不可依賴特定警告。
  • 條款 54:讓自己熟悉標準庫 (含 TR1/C++11) —— STL 容器/算法/迭代器、iostream/locale、shared_ptr/function/regex/哈希容器等。
  • 條款 55:讓自己熟悉 Boost —— 準標準庫孵化器、C++ 標準化風向標、海量高質量庫(Asio, Spirit, Graph, Hana...)。

55 條款非孤立技巧,而是構成一套連貫的 C++ 工程哲學:資源由對象管理、介面設計防誤用、泛型編程利用編譯期計算、繼承嚴守契約、異常安全分級保證。建議將其內化為代碼審查清單與重構指南,配合現代 C++ 標準庫特性(constexpr, span, concepts, coroutines...)持續演進。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved