google c++編程規範筆記手記

1. 頭文件

  • 除了?單元測試?和只含有main函數的.cpp文件,其他.cpp都要包含.h文件
  • #ifndef …; #define H; #endif 目的是避免多重
    包含 [代碼組織]
  • include [可讀性, 代碼維護]
    #include <PATH_AFTER_SRC>/FILE.h
    順序:FILE.h, C系統文件, C++系統文件, 其他庫的.h文件, 本項目內.h文件
    目的:方便排查程序BUG
  • 直接包含原則,只考慮當前.cpp文件直接需要的.h文件 [代碼組織]
  • .cpp中函數和類的定義均使用.h,避免使用前置聲明.因爲前置聲明隱藏了依賴
    關係 [代碼組織]
  • self-contained [代碼組織]

2. 作用域和生命週期

2.1 命名空間

  • 匿名命名空間 namespace {} //namespace [解耦]
    • 作用域爲當前文件
    • 用於不同編譯單元的解耦
    • 推薦將只在當前編譯單元使用的全局變量和全局函數放在該空間內
    • 只用於.cpp文件
  • 具名命名空間 namespace NS { //不縮進} //namespace NS [代碼組織]
    • 避免名字衝突
    • 可用於.h文件或.cpp文件
    • 禁止使用using namespace FOO;污染命名空間
    • 在.cc文件,.h文件的函數,方法和類中,可使用using和別名namespace fb
      = ::foo::bar::baz; [可讀性]

2.2 全局/局部/靜態 函數/變量

  • 全局函數和靜態成員函數的作用域全局可見,若只在當前.cc文件使用,推薦使
     用匿名命名空間,避免在不同編譯單元引起不必要的耦合 [解耦]
  • 具有靜態生命週期的變量(全局變量,靜態全局/局部/成員變量)只能使用內建類
    型,如int,float,char或對應的數組,指針,不能使用類類型.原因有2個: 1)
    全局變量全局可見,不同編譯單元之間全局變量的初始化和析構順序是未定義的,
    違反變量使用前必須初始化的原則 2)靜態變量生命週期到程序結束,多線程場景
    不同線程之間同一靜態對象的構造和析構順序不確定 [潛在BUG]
  • 局部變量就近聲明並定義 [可讀性]

3. 類

3.1 構造函數

  • 不在構造函數中調用自身的虛函數 [潛在BUG]
    原因: 虛函數在運行時動態綁定,構造未完成時無法確定調用哪個對象(基類or子類)
  • 不在無法報出錯誤時進行可能失敗的初始化 [潛在BUG]
    原因: 不使用異常,無法在構造函數中報錯,可使用工廠函數

3.2 隱式類型轉換

  • 儘可能使用explicit關鍵字顯示錶明類型轉換,如類型轉換運算符and單參數構造
     函數 [可讀性]

3.3 可拷貝類型和可移動類型

  • 最低限度使用拷貝和移動,如非明顯需要,禁用拷貝和移動 [執行效率]
    原因: 拷貝操作(拷貝構造and拷貝賦值)常隱式調用,造成對象的不必要拷貝
  • 禁用基類的拷貝和移動,否則會導致子類對象的切割 [可讀性]
  • 需要拷貝時顯示定義拷貝構造/拷貝賦值成員函數,移動操作同理 [可讀性]

3.4 結構體and類

  • 只有數據成員時使用struct,否則使用class [可讀性]

3.5 繼承

  • 只有 “is-a” 關係使用繼承, “has-a” 關係使用組合 [設計邏輯]
  • 必須爲公有繼承,原因: 不改變繼承鏈的可見性 [設計邏輯]
  • 若類含有虛函數,則析構函數也要爲虛函數 [潛在BUG]
  • 不過度使用protected關鍵字 [設計邏輯]
  • 數據成員必須private [設計邏輯]
  • 極少情況使用多重繼承,若必須使用,則要求最多隻有一個基類是非抽象類,其
     他基類爲以Interface爲後綴的接口類 [設計邏輯]

3.6 接口 [設計邏輯]

  • 以 Interface爲後綴(不必須) [可讀性]
  • 只有純虛函數and靜態函數(析構函數除外)
  • 沒有非靜態數據成員
  • 無構造函數or構造函數無參數且必須爲protected
  • 若爲子類,其父類也只能是接口

3.7 運算符重載: 避免使用 [潛在BUG]

3.8 聲明順序 [可讀性]

  • public, protected, private
  • 類型(typedef, using, 嵌套的struct和class), 常量, 工廠函數, 構造函數,
    賦值運算符, 析構函數, 其他函數, 數據成員
  • 短小的函數才內聯定義 [程序性能]

4. 函數

4.1 參數順序:

輸入參數(值參或const引用,特殊情況可爲const指針,如傳空指針or把
指針或對地址的引用賦值給輸入形參,但必須註明),輸出參數(非const指針)
[可讀性]

4.2 簡短函數:

不超過40行 [可讀性]

4.3 函數重載

  • 必須直觀,如在函數名中體現不同重載的差異,對同一類型參數不同數量的重載
     使用std::vector代替 [可讀性]

4.4 缺省參數 [可讀性]

  • 缺省參數與函數重載的思想一致,簡化了編程,推薦使用函數重載
  • 只允許非虛函數使用缺省參數,因爲子類的實現無法保證缺省類型完全一致
  • 必須保證缺省參數的值始終一致,實際上缺省參數在每個調用點都重新求值

4.5 函數返回類型後置語法 [可讀性]

  • 只有在顯式指定lambda表達式的返回值時使用,最好不使用

4.6 內聯函數: 放在頭文件中

4.7 函數的可重入

5. 智能指針 [潛在BUG]

  • 必須動態分配內存時,優先使用std::unique_ptr,將所有權保存在分配者手中,
    其他地方需要使用時,傳遞拷貝or常量指針or常量引用,如
    std::unique_ptr FooFactory();
    void FooConsumer(std::unique_ptr ptr);
  • 共享所有權的理由: 1)性能提升非常明顯 2)操作對象爲const

6. 右值引用 [可讀性]

  • 只在定義移動構造函數and移動賦值函數時使用
  • 不使用 std::forward, 可使用std::move,表示左值轉爲右值,若類Foo定義了
    移動構造,則Foo foo; Foo&& rfoo = std::move(foo); Foo foo_2(rfoo);
    完成移動,否則foo_2仍爲拷貝.std::Forward使得類Foo未定義移動構造函數時
    Foo foo_2(std::forward(rfoo))完成移動成爲可能

7. 友元:

可合理使用友元類or友元函數,如單元測試類聲明爲待測類的友元[代碼組織]

8. 異常:

不使用異常,使用日誌和錯誤代碼代替,錯誤代碼由返回值返回,其他返回值
  通過輸出指針參數獲取 [代碼組織]

9. RTTI:

禁止使用運行時類型識別 [潛在bug]

10. 類型轉換 [可讀性]

  • 不使用C風格的強制類型轉換,如int x = 3.5; int y = int(x)
  • 使用static_cast, const_cast

11. 流 [可讀性]

  • 除了記錄日誌時使用流,其他情況均使用printf + read/write代替
  • printf: 關聯到stdout, sprintf: 關聯到字符串緩衝區(snprintf指定size防
    溢出), fprintf: 關聯到文件

12. 前置自增和自減 [程序性能]

  • 迭代器和其他模板對象使用前置,簡單數值二者均可.原因:後置會包含一次拷貝

13. const與constexpr [可讀性]

  • const指的是運行時常量,是假常量,constexpr是編譯時常量,是真常量
  • const用於指針或引用只是表明只讀特性,但是其指向的對象仍然可變
  • 儘量多使用const函數,如引用型或指針型形參, 訪問函數, 不修改數據成員的
    函數, 未調用非const函數的函數, 不返回數據成員非const指針或引用的函數
  • 數據成員在對象構造後不發生變化,使用const
  • mutable可使用,但在多線程中不安全

14. 整型 [潛在BUG]

  • 使用 int 或 <stdint.h>中的int16_t, int32_t, int64_t
  • 不推薦使用無符號整型數表示輸入非負,應當使用斷言檢查
  • 注意整型提升可能導致的溢出

15. 64位下的可移植性 [潛在BUG]

  • 打印,比較,結構體對齊等,參考google c++ style的處理

16. 預處理宏 [潛在BUG]

  • 慎用,儘量用內聯函數,枚舉和常量代替
  • 不要在.h中定義
  • 馬上使用才#define,使用後立即#undef

17. 0字符 [可讀性]

  • 整數用0,實數用0.0,指針用nullptr或NULL,字符(串)用’\0’

18. sizeof:

儘可能用sizeof(varname)代替sizeof(type) [潛在BUG]

19. auto:只用於局部變量 [可讀性]

20. 列表初始化:可用 [可讀性]

21: lambda表達式:可用,但要顯示捕獲,

可與std::functions, std::bind搭配成通用
  回調機制 [可讀性]

22: 模板編程:慎用,難以DEBUG [潛在BUG]

23: Boost庫: 只使用被認可的庫,如 [程序設計]

  • boost/call_traits.hpp
  • boost/compressed_pair.hpp
  • boost/graph except for adj_list_serialize.hpp, parallel/, distributed/
  • boost/property_map.hpp
  • boost/iterator/iterator_adaptor.hpp, boost/iterator/iterator_dacade
    .hpp, boost/function_output_iterator.hpp
  • boost/polygon/voronoi_builder.hpp, voronoi_diagram.hpp, voronoi_geomet
    ry_type.hpp
  • boost/bitmap
  • boost/math/distributions
  • boost/multi_index
  • boost/heap
  • boost/container/flat_map, flat_set

24: 代碼書寫樣式

1) 命名

  • 完整描述性命名
  • 文件命名:
    • 全部小寫,下劃線_或連字符-鏈接
    • 源文件以.cc結尾
    • 定義類的文件與類名保持一致
  • 類命名
    • 駝峯式命名(縮寫單詞只有首字母大寫)
    • 包括類, 結構體, 類型定義(typedef), 枚舉, 類模板參數
      class UrlTable {};
      struct UrlTableProperties ();
      typedef hash_map<UrlTableProperties*, string> PropertiesMap;
      using PropertiesMap = hash_map<UrlTableProperties*, string>;
      enum UrlTableErrors {};
  • 函數命名
    • 駝峯式命名
    • 取值和設值函數使用小寫加下劃線,與對應的成員變量名匹配
  • 變量命名
    • 變量, 函數參數, 數據成員一律小寫,下劃線連接
    • 數據成員下劃線結束,結構體數據成員不用下劃線結尾
  • 常量命名
    • 以k開頭,駝峯式命名,包括const變量或constexpr變量
  • 命名空間命名
    • 小寫,無下劃線
  • 枚舉值命名
    • 常量形式(推薦) or 大寫加下劃線形式(宏風格)

2) 註釋

  • 風格: 推薦使用 //, 也可使用 /* */
  • 文件註釋:
    • 每個文件開頭加入版權公告,除非已經註釋過
    • 包括許可證引用, 作者姓名, 郵件, 文件內容的大致說明
  • 類註釋
    • 接口處描述類的功能,使用注意事項
  • 函數註釋
    • 函數接口的功能,輸入,輸出
    • 除非用到了特別高超的技巧,不對函數實現註釋,假定代碼閱讀者水平比自己高
  • TODO註釋
    • 若是臨時的解決方案,在隨後的圓括號中寫下名字,郵件地址,issue
    • 若是將來某一天做某事,寫上明確的時間

3) 格式

  • 每行代碼字符數不超過80,除了不得已的註釋行,帶有命令示例的行,含有url的行,
    包含長路徑的#include語句
  • 儘量只使用ASCII字符,必須使用UTF-8編碼
  • 使用空格不使用製表符縮進,每次縮進2個空格
  • 函數聲明與定義
    • 返回類型,函數名,形參儘量放在一行
    • 一行放不下所有參數,形參分行,與第一行的形參對齊
    • 一行一個參數也放不下,換行,4空格縮進,每行一個形參
    • { 在最後一個參數同行的末尾,不換行, } 獨立位於最後一行or與 { 同行
  • 函數調用
    • 儘量一行寫完
    • 在()內對參數分行,參數與函數名對齊即可
    • 參數另起一行,縮進4格,每行儘量多放參數
  • lambda表達式: 與函數的定義格式一致
  • 列表初始化: 與函數調用一致
  • if-else條件語句
    • 只有一行且沒有else子句可不寫{}
  • switch語句
    • case 2空格縮進
    • break 4空格縮進
  • 循環語句
    • for循環只有一條語句可不用{}
    • while 空循環體可使用 {}
  • 指針和引用
    • *或&可緊挨變量或緊挨類型,但風格必須保持一致
  • bool表達式
    • 使用符號&&, ||而非and,or
    • 斷行時每行以&&等符號結尾
  • 變量及數組初始化
    • =, (), {}均可
  • 預處理指令: 不可縮進
  • 類格式:
    • public,protected,private縮進1格
    • protected, private與上一行空一行
  • 構造函數初始值列表
    • 儘量放在一行
    • 一行放不下換行,置於:後,縮進4格放在一行
    • 換行後放不下,置於:後,縮進4格,每行一個成員,成員名對齊
  • 命名空間: 不縮進
  • 空行: 越少越好
  • 一致性: 包括與項目已有風格的一致性
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章