公司C++規范學習
語法部分
- class和struct關鍵字的選擇:class表示被封裝的用戶自定義類型,不公開定義非靜態數據成員,struct表示數據的簡單集合,只定義用於初始化數據成員的方法。
- 必須使用構造函數初始化列表顯示初始化直接基類與所有基類類型數據成員。
- 沒有復制意義的類必須用DISALLOW_COPY_AND_ASSIGN宏禁止拷貝構造函數和賦值構造函數。
- DISALLOW_COPY_AND_ASSIGN 宏就是將復制拷貝函數和賦值操作符聲明為私有變量。
- 禁止在構造函數中進行可能出錯的復雜操作(比如申請資源),復雜操作用額外的init()函數實現。
- 顯式初始化能夠使代碼更清晰、不易錯誤調用,還能避免二次賦值造成的效率問題, 單參構造函數盡量加上explicit。
- 有默認值語義的類必須顯示定義默認構造函數。
- 沒有默認值語意的類,必須顯式定義其它構造函數或 private 聲明默認構造函數,並不給出實現。
- 如果類型是可拷貝,一定要同時定義拷貝構造函數和賦值構造函數,如果類型可移動,一定要同時定義移動構造函數和移動賦值函數。
- 如果使用默認的拷貝和移動操作,要使用=default定義。
- 如果類型不需要拷貝、移動操作,要使用=delete手段禁用。
- 向容器添加數據時,優先使用emplace開頭的接口函數。
- 在極少的情況下,當該類的隱式轉換很有意義時,可以不把單參數構造函數聲明為顯式構造函數,但必須十分小心,並使用文檔化注釋說明。
- 單參數構造函數如果不用explicit關鍵字修飾,則可能被編譯器用來做隱式類型轉換。
- 有復制意義的類必須顯式給出復制構造函數,並小心指定其行為(淺復制、深復制等)
- 托管了資源的類,往往是沒有復制意義的。此時應當防止用戶錯誤調用而導致資源泄漏、重復釋放的后果。
- 編譯器默認生成的復制構造函數,對指針數據成員使用淺復制策略,但這種策略常常不是程序員希望的。
- 析構函數:
- 若類定義了虛函數,必須定義虛析構函數。
- 若類設計為可被繼承的,應該定義公開的虛析構函數或保護的非虛析構函數。
- [RULE010] (不包括結構)含有指針成員,必須顯式給出析構函數,並小心指定其行為(是否銷毀指針,如何銷毀等).
- 絕不允許讓異常離開析構函數。
- 按照 public、protected、private 的順序聲明成員。按照類型、方法、數據成員的順序聲明成員, DISALLOW_COPY_AND_ASSIGN 放在 private 區段的末尾.
- 不是非常必要的話,避免使用友元: 友元破壞了類的封裝性,增加了類之間的耦合,適宜使用友元的如容器與它的迭代器,類與涉及該類的.
- 委派和繼承構造函數是由 C++11 引進為了減少構造函數重復代碼而開發的兩種不同的特性. 通過特殊的初始化列表語法, 委派構造函數允許類的一個構造函數調用其他的構造函數.
- C++11強類型枚舉
enum class Side
- 被重載的虛函數通過override顯示聲明.
- 禁止被重載的函數通過final顯示禁止.
- Lambda表達式:
- 不使用默認捕獲(包括&, =), 捕獲顯示寫出來.
- 只在兩種情況下使用右值引用, 一種是定義類型的移動操作函數時, 另一種是定義模板函數實現完美轉發的時候. 除此之外, 不要使用右值引用.
- 右值引用的語義比較復雜, 不恰當的使用會造成很難追查的bug.
- 建議 include 的路徑、文件名與命名空間保持一致
- 使用空格縮進,不使用制表符。 以 4 個空格為單位縮進。避免超過 5 重的縮進。
- 建議不要使用異常,除非已有項目/底層庫使用了異常, 這時候必須要catch所有異常。
- 所有代碼應該定義在namespace中, main函數除外。
- 禁止內聯(inline)虛函數,inline函數應該在10行以內。
- 函數靜止使用默認參數,模板可以使用默認參數。-- 默認參數是編譯期綁定的,不具有多態性。
- 使用 auto 自動推導類型, 只能定義局部變量(返回類型后置聲明除外).
- 禁止使用 auto_ptr. 如果需要傳遞對象, 使用std::unique_ptr明確所有權傳遞. 如果要共享所有權, 使用std::shared_ptr明確所有權共享.
- [RULE033] 避免對浮點數進行相等或不等比較。
- 避免使用非布爾型的變量或表達式作為分支語句條件。
- 作相等比較時,建議把常量或右值變量放在 == 運算符的右邊。
- 除非用於條件編譯、跟蹤調試,或者能夠顯著減少代碼量並確保可讀性的宏(如 DISALLOW_COPY_AND_ASSIGN, CFATAL_LOG etc.),否則不應使用。
- 鼓勵使用前向聲明,以減少文件之間的依賴關系。
- 盡可能避免使用全局變量,如有必要應使用 singleton 模式代替。
- 內部使用的全局函數/變量,必須聲明為靜態函數、變量,不能在目標文件中出現外部可見的符號。
- 不應在頭文件定義類/結構體類型的全局常量,以減少代碼膨脹。
- 可能用於跨模塊間通訊或者涉及存儲的枚舉值必須顯式指定值,避免版本不一致造成詭異的錯誤。
- const 關鍵字用在類型名前面而非后面。
- 不修改內部狀態的成員方法必須聲明為 const
- 返回不可修改的指針或引用必須聲明為 const
- 不被修改的引用/指針形參必須聲明為 const
- 除非保證狀態不會被修改,不應使用 const_cast 來去掉 const 屬性
- 避免定義超過 4 KB 的局部變量數組
風格/約定
- 必須按以下順序引用頭文件,並進行分節:.cpp 對應的頭文件(如果有, // 優先位置), C(標准)庫,C++(標准)庫,其它庫,自己項目的.h文件,每節內的include按照字母序。
- 分行與空格:
- 一條單獨的語句必須獨立成行。
- 避免連續的空行。
- 使用適當的空行來分組代碼的邏輯。
- 左大括號不獨立,右大括號獨立成行。並且左大括號所在行進行垂直對齊。
- 命名空間不縮進。
- 構造函數初始化列表放在同一行或按至少八格縮進並排幾行, 如果需要換行, 把: 放在第一行。
- 比較短的函數聲明,整個聲明占一行;過長的函數聲明,令每一參數占一行,並且垂直對齊, 換行后的參數至少保持 8 個空格縮進。
- 比較短的函數調用語句,整個語句占一行;過長的函數調用,控制折行確保每行不要超過 100 列,換行后至少額外縮進 8 個空格。
- 空格的使用:
- if/switch/while/for/catch 與后邊的圓括號之間加一個空格,圓括號內側與判斷表達式之間不加空格。
- for語句圓括號內分號前不加空格,分號后加一個空格。
- 函數調用中,函數名和圓括號之間不要加空格。
- 類繼承與構造函數的初始化列表的冒號前后加一個空格。
- 逗號表達式或參數列表中,逗號前不加空格,逗號后加一個空格。
- 一元運算符前后不加空格。二元、三元表達式前后各加一個空格。
- 成員訪問或作用域運算符前后不加空格。如:a.b, a->b, a.b, a->b, a::b。
- 圓括號和方括號運算符前后和內部都不加空格。
- .h 中不要定義復雜的函數, 復雜函數如果必須在頭文件中, 應該放在 .hpp 后綴的頭文件中。
- 沒有使用到的函數參數,把參數名注釋起來, 不允許未命名參數出現。
- :函數的參數順序,建議先安排輸入參數,再安排輸出參數。
// 輸入參數對象類型使用const引用 void foo(const std::string& input1, const MyClass& input2);
命名規范
-
全局可見的,卻又無法通過命名空間約束的標識符命名,必須以庫名作為前綴,以避免沖突。
-
命名應當盡可能有描述性,不使用非通用的縮寫(尤其是省一個字符的縮寫如creat,usr等),不使用有歧義的縮寫,不使用任意的無意義的字符.
-
標識符的作用域越大,命名就應該越清晰。
-
可能使用多個單位名稱的變量(如表示時間的變量),應以單位名稱縮寫為后綴。
-
文件名全部用小寫字母, 中間用_間隔; 比如: this_is_my_awesome_file.cpp.
-
單元測試文件名使用<被測文件名>_test.h(.cpp)命名.
-
如果是要發布供它人使用的 lib,推薦僅暴露一個以庫名同名,或者<庫名>_<功能集合>.h的 api 頭文件,然后將相關頭文件 include 到這個 api 頭文件中。如:mylib.h,mylib_utils.h,mylib_api.h。
-
使用下划線分隔的全小寫命名法命名命名空間。
-
命名空間名可以使用縮寫,同時應保證簡短、不易沖突但同時富有意義。
-
函數命名使用下划線分隔的全小寫命名法。
-
函數通常使用動詞短語命名。
-
自定義類類型使用首字母大寫的駝峰命名法命名,一般不使用前綴。
-
枚舉類型成員,使用全大寫蛇形命名法(即全部字母大寫,單詞間用下划線分隔)。
-
盡可能不使用全局變量,如果必須使用,必須以g為前綴,而且必須足夠長以避免名字沖突。
-
全局變量使用下划線分隔的全小寫命名法命名。
-
局部變量名使用下划線分隔的全小寫命名法命名。
- 假如局部變量作用域很小,可以適當使用縮寫。
-
全局靜態變量 s_ 作為前綴。類靜態成員使用 s 前綴的下划線分隔的全小寫命名法命名。
-
const 常量與枚舉常量都使用下划線分隔的全大寫命名法命名
-
確定需要不同大小的變量,推薦使用<stdint.h>中定義的長度明確的整形類型,例如 int64_t, int32_t 等。
-
多行注釋必須寫在被解釋內容的上方。單行注釋可以寫在被注釋語句的上文或右方。
-
一行內只應聲明一個變量。
-
局部變量應盡量延遲到第一次使用處聲明。
-
局部變量應在聲明處賦初值,指針類型局部變量必須在聲明處賦初值。
-
本規范不適用於引入的公司外第三方代碼, 修改時請遵守第三方代碼自身的編碼風格。
-
顯式或者通過宏等隱式的定義派生自第三方代碼中所申明的類,必須遵守對應第三方代碼自身的編碼風格。