C++風格規範(google風格整理)
一、頭文件
1.1 define保護
所有頭文件都應該使用 #define 來防止頭文件被多重包含, 命名格式當是:
<PROJECT>_<PATH>_<FILE>_H_
例子:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
…
#endif // FOO_BAR_BAZ_H_
1.2 前置聲明
儘可能地避免使用前置聲明。使用 #include 包含需要的頭文件即可。
1.3 內聯函數
只有當函數只有 10 行甚至更少時纔將其定義爲內聯函數.
1.4 #include 的路徑及順序
使用標準的頭文件包含順序可增強可讀性, 避免隱藏依賴。
順序:
- dir2/foo2.h (優先位置, 詳情如下)
- C 系統文件
- C++ 系統文件
- 其他庫的 .h 文件
- 本項目內 .h 文件
二、作用域
2.1 命名空間
鼓勵在 .cc 文件內使用匿名命名空間或 static 聲明. 使用具名的命名空間時, 其名稱可基於項目名或相對路徑. 禁止使用 using 指示(using-directive)。禁止使用內聯命名空間(inline namespace)。
命名規則:
- 遵守 命名空間命名 中的規則。
- 在命名空間的最後註釋出命名空間的名字。
- 用命名空間把文件包含,類的前置聲明以外的整個源文件封裝起來。
- 不要在命名空間 std 內聲明任何東西, 包括標準庫的類前置聲明. 在 std 命名空間聲明實體是未定義的行爲, 會導致如不可移植. 聲明標準庫下的實體, 需要包含對應的頭文件.
- 不應該使用 using 指示 引入整個命名空間的標識符號。
- 不要在頭文件中使用 命名空間別名 除非顯式標記內部命名空間使用。因爲任何在頭文件中引入的命名空間都會成爲公開API的一部分。
namespace baz = ::foo::bar::baz; - 禁止用內聯命名空間
三、類
3.1 構造函數的職責
不要在構造函數中調用虛函數, 也不要在無法報出錯誤時進行可能失敗的初始化.
3.2 隱式類型轉換
不要定義隱式類型轉換. 對於轉換運算符和單參數構造函數, 請使用 explicit 關鍵字.
定義:
隱式類型轉換允許一個某種類型 (稱作 源類型) 的對象被用於需要另一種類型 (稱作 目的類型) 的位置。例如, 將一個 int 類型的參數傳遞給需要 double 類型的函數.
3.3 可拷貝類型和可移動類型
如果你的類型需要, 就讓它們支持拷貝 / 移動. 否則, 就把隱式產生的拷貝和移動函數禁用.
可拷貝類型允許對象在初始化時得到來自相同類型的另一對象的值, 或在賦值時被賦予相同類型的另一對象的值, 同時不改變源對象的值. 對於用戶定義的類型, 拷貝操作一般通過拷貝構造函數與拷貝賦值操作符定義. string 類型就是一個可拷貝類型的例子.
3.4 結構體 VS. 類
僅當只有數據成員時使用 struct, 其它一概使用 class.
3.5 繼承
使用組合 (YuleFox 注: 這一點也是 GoF 在 <> 裏反覆強調的) 常常比使用繼承更合理. 如果使用繼承的話, 定義爲 public 繼承.
3.6 多重繼承
真正需要用到多重實現繼承的情況少之又少. 只在以下情況我們才允許多重繼承: 最多隻有一個基類是非抽象類; 其它基類都是以 Interface 爲後綴的 純接口類。
3.7 接口
接口是指滿足特定條件的類, 這些類以 Interface 爲後綴 (不強制).
3.8 運算符重載
除少數特定環境外, 不要重載運算符. 也不要創建用戶定義字面量.
3.9 存取控制
將 所有 數據成員聲明爲 private, 除非是 static const 類型成員 (遵循 常量命名規則). 處於技術上的原因, 在使用 Google Test 時我們允許測試固件類中的數據成員爲 protected.
3.10 聲明順序
將相似的聲明放在一起, 將 public 部分放在最前.
四、函數
4.1 參數順序
函數的參數順序爲: 輸入參數在先, 後跟輸出參數.
4.2 編寫簡短函數
我們傾向於編寫簡短, 凝練的函數.
4.3 引用參數
參數儘量使用引用傳遞,且所有按引用傳遞的參數必須加上 const,引用的參數不修改,要修改的參數用指針。
4.4 函數重載
若要使用函數重載, 則必須能讓讀者一看調用點就胸有成竹, 而不用花心思猜測調用的重載函數到底是哪一種. 這一規則也適用於構造函數.
4.5 缺省參數
有些函數一般情況下使用默認參數, 但有時需要又使用非默認的參數. 缺省參數爲這樣的情形提供了便利, 使程序員不需要爲了極少的例外情況編寫大量的函數. 和函數重載相比, 缺省參數的語法更簡潔明瞭, 減少了大量的樣板代碼, 也更好地區別了 “必要參數” 和 “可選參數”.
4.6 函數返回類型後置
只有在常規寫法 (返回類型前置) 不便於書寫或不便於閱讀時使用返回類型後置語法.
auto foo(int x) -> int;
五、來自Google的奇技
5.1 所有權與智能指針
動態分配出的對象最好有單一且固定的所有主, 並通過智能指針傳遞所有權.
5.2 Cpplint
使用 cpplint.py 檢查風格錯誤.
六、其他C++特性
6.1 引用參數
所有按引用傳遞的參數必須加上 const.
6.2 右值引用
只在定義移動構造函數與移動賦值操作時使用右值引用. 不要使用 std::forward.
6.3 函數重載
若要用好函數重載,最好能讓讀者一看調用點(call site)就胸有成竹,不用花心思猜測調用的重載函數到底是哪一種。該規則適用於構造函數。
6.4 缺省參數
我們不允許使用缺省函數參數,少數極端情況除外。儘可能改用函數重載。
6.5 變長數組和 alloca()
我們不允許使用變長數組和 alloca().
6.6 友元
我們允許合理的使用友元類及友元函數.
6.7 異常
我們不使用 C++ 異常.
6.8 運行時類型識別
我們禁止使用 RTTI.
6.9 類型轉換
使用 C++ 的類型轉換, 如 static_cast<>(). 不要使用 int y = (int)x 或 int y = int(x) 等轉換方式。
6.10 流
只在記錄日誌時使用流.
6.11 前置自增和自減
對於迭代器和其他模板對象使用前綴形式 (++i) 的自增, 自減運算符.
6.12 const 用法
我們強烈建議你在任何可能的情況下都要使用 const. 此外有時改用 C++11 推出的 constexpr 更好。
6.13 constexpr 用法
在 C++11 裏,用 constexpr 來定義真正的常量,或實現常量初始化。
6.14 整型
C++ 內建整型中, 僅使用 int. 如果程序中需要不同大小的變量, 可以使用 <stdint.h> 中長度精確的整型, 如 int16_t.如果您的變量可能不小於 2^31 (2GiB), 就用 64 位變量比如 int64_t. 此外要留意,哪怕您的值並不會超出 int 所能夠表示的範圍,在計算過程中也可能會溢出。所以拿不準時,乾脆用更大的類型。
6.15 64 位下的可移植性
代碼應該對 64 位和 32 位系統友好. 處理打印, 比較, 結構體對齊時應切記:
- 對於某些類型, printf() 的指示符在 32 位和 64 位系統上可移植性不是很好.
- 記住 sizeof(void *) != sizeof(int). 如果需要一個指針大小的整數要用 intptr_t.
- 你要非常小心的對待結構體對齊, 尤其是要持久化到磁盤上的結構體。
- 創建 64 位常量時使用 LL 或 ULL 作爲後綴。
- 如果你確實需要 32 位和 64 位系統具有不同代碼, 可以使用 #ifdef _LP64 指令來切分 32/64 位代碼. (儘量不要這麼做, 如果非用不可, 儘量使修改局部化)。
6.16 預處理宏
使用宏時要非常謹慎, 儘量以內聯函數, 枚舉和常量代替之.
6.17 0, nullptr 和 NULL
整數用 0, 實數用 0.0, 指針用 nullptr 或 NULL, 字符 (串) 用 ‘\0’.
整數用 0, 實數用 0.0, 這一點是毫無爭議的.
對於指針 (地址值), 到底是用 0, NULL 還是 nullptr. C++11 項目用 nullptr; C++03 項目則用 NULL, 畢竟它看起來像指針。實際上,一些 C++ 編譯器對 NULL 的定義比較特殊,可以輸出有用的警告,特別是 sizeof(NULL) 就和 sizeof(0) 不一樣。
字符 (串) 用 ‘\0’, 不僅類型正確而且可讀性好.
6.18 sizeof
儘可能用 sizeof(varname) 代替 sizeof(type).
使用 sizeof(varname) 是因爲當代碼中變量類型改變時會自動更新. 您或許會用 sizeof(type) 處理不涉及任何變量的代碼,比如處理來自外部或內部的數據格式,這時用變量就不合適了。
6.19 auto
用 auto 繞過煩瑣的類型名,只要可讀性好就繼續用,別用在局部變量之外的地方。
6.20 列表初始化
用列表初始化。
6.21 Lambda 表達式
適當使用 lambda 表達式。別用默認 lambda 捕獲,所有捕獲都要顯式寫出來。
6.22 模板編程
不要使用複雜的模板編程
6.23 Boost 庫
只使用 Boost 中被認可的庫.
6.24 C++11
適當用 C++11(前身是 C++0x)的庫和語言擴展,在貴項目用 C++11 特性前三思可移植性。
七 命名約定
7.1 通用命名規則
函數命名, 變量命名, 文件命名要有描述性; 少用縮寫.
7.2 文件命名
文件名要全部小寫, 可以包含下劃線 () 或連字符 (-), 依照項目的約定. 如果沒有約定, 那麼 “” 更好.
7.3 類型命名
類型名稱的每個單詞首字母均大寫, 不包含下劃線: MyExcitingClass, MyExcitingEnum.
7.4 變量命名
變量 (包括函數參數) 和數據成員名一律小寫, 單詞之間用下劃線連接. 類的成員變量以下劃線結尾, 但結構體的就不用, 如: a_local_variable, a_struct_data_member, a_class_data_member_.
7.5 常量命名
聲明爲 constexpr 或 const 的變量, 或在程序運行期間其值始終保持不變的, 命名時以 “k” 開頭, 大小寫混合.
7.6 函數命名
常規函數使用大小寫混合, 取值和設值函數則要求與變量名匹配: MyExcitingFunction(), MyExcitingMethod(), my_exciting_member_variable(), set_my_exciting_member_variable().
7.7 命名空間命名
命名空間以小寫字母命名. 最高級命名空間的名字取決於項目名稱. 要注意避免嵌套命名空間的名字之間和常見的頂級命名空間的名字之間發生衝突.
7.8 枚舉命名
枚舉的命名應當和 常量 或 宏 一致: kEnumName 或是 ENUM_NAME.
7.9 宏命名
你並不打算 使用宏, 對吧? 如果你一定要用, 像這樣命名: MY_MACRO_THAT_SCARES_SMALL_CHILDREN.
八、註釋
8.1 註釋風格
使用 // 或 /* */, 統一就好.
8.2 文件註釋
在每一個文件開頭加入版權公告.
文件註釋描述了該文件的內容. 如果一個文件只聲明, 或實現, 或測試了一個對象, 並且這個對象已經在它的聲明處進行了詳細的註釋, 那麼就沒必要再加上文件註釋. 除此之外的其他文件都需要文件註釋.
8.3 類註釋
每個類的定義都要附帶一份註釋, 描述類的功能和用法, 除非它的功能相當明顯.
8.4 函數註釋
函數聲明處的註釋描述函數功能; 定義處的註釋描述函數實現。
函數聲明處註釋的內容:
- 函數的輸入輸出.
- 對類成員函數而言: 函數調用期間對象是否需要保持引用參數, 是否會釋放這些參數.
- 函數是否分配了必須由調用者釋放的空間.
- 參數是否可以爲空指針.
- 是否存在函數使用上的性能隱患.
- 如果函數是可重入的, 其同步前提是什麼?
8.5 變量註釋
通常變量名本身足以很好說明變量用途. 某些情況下, 也需要額外的註釋說明
8.6 實現註釋
對於代碼中巧妙的, 晦澀的, 有趣的, 重要的地方加以註釋.
8.7 標點, 拼寫和語法
注意標點, 拼寫和語法; 寫的好的註釋比差的要易讀的多.
8.8 TODO 註釋
對那些臨時的, 短期的解決方案, 或已經夠好但仍不完美的代碼使用 TODO 註釋.
8.9 棄用註釋
通過棄用註釋(DEPRECATED comments)以標記某接口點已棄用.
九、格式
9.1 行長度
每一行代碼字符數不超過 80.
9.2 非 ASCII 字符
儘量不使用非 ASCII 字符, 使用時必須使用 UTF-8 編碼.
9.3 空格還是製表位
只使用空格, 每次縮進 2 個空格.
9.4 函數聲明與定義
返回類型和函數名在同一行, 參數也儘量放在同一行, 如果放不下就對形參分行, 分行方式與 函數調用 一致.
9.5 Lambda 表達式
Lambda 表達式對形參和函數體的格式化和其他函數一致; 捕獲列表同理, 表項用逗號隔開.
9.6 函數調用
要麼一行寫完函數調用, 要麼在圓括號裏對參數分行, 要麼參數另起一行且縮進四格. 如果沒有其它顧慮的話, 儘可能精簡行數, 比如把多個參數適當地放在同一行裏.
9.7 列表初始化格式
您平時怎麼格式化函數調用, 就怎麼格式化 列表初始化.
9.8 條件語句
傾向於不在圓括號內使用空格. 關鍵字 if 和 else 另起一行.
9.9 循環和開關選擇語句
switch 語句可以使用大括號分段, 以表明 cases 之間不是連在一起的. 在單語句循環裏, 括號可用可不用. 空循環體應使用 {} 或 continue.
9.10 指針和引用表達式
句點或箭頭前後不要有空格. 指針/地址操作符 (*, &) 之後不能有空格.
9.11 布爾表達式
如果一個布爾表達式超過 標準行寬, 斷行方式要統一一下.
9.12 函數返回值
不要在 return 表達式里加上非必須的圓括號.
9.13 變量及數組初始化
用 =, () 和 {} 均可.
9.14 預處理指令
預處理指令不要縮進, 從行首開始.
9.15 類格式
訪問控制塊的聲明依次序是 public:, protected:, private:, 每個都縮進 1 個空格.
9.16 構造函數初始值列表
構造函數初始化列表放在同一行或按四格縮進並排多行.
MyClass::MyClass(int var) : some_var_(var) {
DoSomething();
}
// 如果不能放在同一行,
// 必須置於冒號後, 並縮進 4 個空格
MyClass::MyClass(int var)
: some_var_(var), some_other_var_(var + 1) {
DoSomething();
}
9.17 命名空間格式化
命名空間內容不縮進.
9.18 水平留白
水平留白的使用根據在代碼中的位置決定. 永遠不要在行尾添加沒意義的留白.
9.19 垂直留白
垂直留白越少越好