文章目錄
- 1. 頭文件
- 2. 作用域和生命週期
- 3. 類
- 3.1 構造函數
- 3.2 隱式類型轉換
- 3.3 可拷貝類型和可移動類型
- 3.4 結構體and類
- 3.5 繼承
- 3.6 接口 [設計邏輯]
- 3.7 運算符重載: 避免使用 [潛在BUG]
- 3.8 聲明順序 [可讀性]
- 4. 函數
- 5. 智能指針 [潛在BUG]
- 6. 右值引用 [可讀性]
- 7. 友元:
- 8. 異常:
- 9. RTTI:
- 10. 類型轉換 [可讀性]
- 11. 流 [可讀性]
- 12. 前置自增和自減 [程序性能]
- 13. const與constexpr [可讀性]
- 14. 整型 [潛在BUG]
- 15. 64位下的可移植性 [潛在BUG]
- 16. 預處理宏 [潛在BUG]
- 17. 0字符 [可讀性]
- 18. sizeof:
- 19. auto:只用於局部變量 [可讀性]
- 20. 列表初始化:可用 [可讀性]
- 21: lambda表達式:可用,但要顯示捕獲,
- 22: 模板編程:慎用,難以DEBUG [潛在BUG]
- 23: Boost庫: 只使用被認可的庫,如 [程序設計]
- 24: 代碼書寫樣式
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格,每行一個成員,成員名對齊
- 命名空間: 不縮進
- 空行: 越少越好
- 一致性: 包括與項目已有風格的一致性