C/C++代碼規範

零、前言


 

  筆者最近在看開源代碼,看到代碼格式各自良莠不齊,感受像是各家各有所長。所以打算寫一篇關於C/C++代碼規範文章,請各位參考,並踐踏批評。java

1、文件排版


1. 包含頭文件

 • 先系統頭文件,後用戶頭文件。
 • 系統頭文件,穩定的目錄結構,應採用包含子路徑方式。
 • 自定義頭文件,不穩定目錄結構,應在dsp中指定包含路徑。
 • 系統頭文件應用:#include <xxx.h>
 • 自定義同文件應用:#include "xxx.h"
 • 只引用須要的頭文件。程序員

2. h和cpp文件

 • 頭文件命名爲.h,內聯文件命名爲.inl;C++文件命名爲*.cpp
 • 文件名用大小寫混合,或者小寫混合。例如DiyMainview.cppinfoview.cpp。不要用無心義的名稱:例如XImage.cppSView.cppxlog.cpp
 • 頭文件除了特殊狀況,應使用#ifdef控制塊。
 • 頭文件#endif應採用行尾註釋。
 • 頭文件,首先是包含代碼塊,其次是宏定義代碼塊,而後是全局變量,全局常量,類型定義,類定義,內聯部分。
 • CPP文件,包含指令,宏定義,全局變量,函數定義。數組

3. 文件結構

 • 文件應包含文件頭註釋和內容。
 • 函數體類體之間原則上用2個空行,特殊狀況下可用一個或者不須要空行。編輯器

4. 空行

 • 文件頭、控制塊,#include部分、宏定義部分、class部分、全局常量部分、全局變量部分、函數和函數之間,用兩個空行。函數

2、註釋


1. 文件頭註釋

 • 做者,文件名稱,文件說明,生成日期(可選)優化

2. 函數註釋

 • 關鍵函數必須寫上註釋,說明函數的用途。
 • 特別函數參數,須要說明參數的目的,由誰負責釋放等等。
 • 除了特別狀況,註釋寫在代碼以前,不要放到代碼行以後。
 • 對每一個#else#endif給出行末註釋。
 • 關鍵代碼註釋,包括但不限於:賦值,函數調用,表達式,分支等等。
 • 善未實現完整的代碼,或者須要進一步優化的代碼,應加上 // TODO …
 • 調試的代碼,加上註釋 // only for DEBUG
 • 須要引發關注的代碼,加上註釋 // NOTE …
 • 對於較大的代碼塊結尾,如for,while,do等,可加上 // end for|while|dospa

3、命名


1. 原則

 • 同一性:在編寫一個子模塊或派生類的時候,要遵循其基類或總體模塊的命名風格,保持命名風格在整個模塊中的同一性。
 • 標識符組成:標識符采用英文單詞或其組合,應當直觀且能夠拼讀,可望文知意,用詞應當準確,避免用拼音命名。
 • 最小化長度 && 最大化信息量原則:在保持一個標識符意思明確的同時,應當儘可能縮短其長度。
 • 避免過於類似:不要出現僅靠大小寫區分的類似的標識符,例如"i"與"I""function""Function"等等。
 • 避免在不一樣級別的做用域中重名:程序中不要出現名字徹底相同的局部變量和全局變量,儘管二者的做用域不一樣而不會發生語法錯誤,但容易令人誤解。
 • 正確命名具備互斥意義的標識符:用正確的反義詞組命名具備互斥意義的標識符,如:"nMinValue" 和"nMaxValue""GetName()" 和"SetName()" ….
 • 避免名字中出現數字編號:儘可能避免名字中出現數字編號,如Value1,Value2等,除非邏輯上的確須要編號。這是爲了防止程序員偷懶,不願爲命名動腦筋而致使產生無心義的名字(由於用數字編號最省事)。設計

2. T,C,M,R類

 • T類表示簡單數據類型,不對資源擁有控制權,在析構過程當中沒有釋放資源動做。
 • C表示從CBase繼承的類。該類不能從棧上定義變量,只能從堆上建立。
 • M表示接口類。
 • R是資源類,一般是系統固有類型。除了特殊狀況,不該在開發代碼中出現R類型。指針

3. 函數名

 • M類的函數名稱應採用HandleXXX命名,例如:HandleTimerEvent;不推薦採用java風格,例如handleTimerEvent;除了標準c風格代碼,不推薦用下劃線,例如,handle_event
 • Leave函數,用後綴L。
 • Leave函數,且進清除棧,用後綴LC。
 • Leave函數,且刪除對象,用後綴LD。調試

4. 函數參數

 • 函數參數用a做爲前綴。
 • 避免出現和匈牙利混合的命名規則如apBuffer名稱。用aBuffer便可。
 • 函數參數比較多時,應考慮用結構代替。
 • 若是不能避免函數參數比較多,應在排版上可考慮每一個參數佔用一行,參數名豎向對齊。

5. 成員變量

 • 成員變量用m最爲前綴。
 • 避免出現和匈牙利混合的命名規則如mpBuffer名稱。用mBuffer便可。

6. 局部變量

 • 循環變量和簡單變量採用簡單小寫字符串便可。例如,int i;
 • 指針變量用p打頭,例如void* pBuffer;

7. 全局變量

 • 全局變量用g_最爲前綴。

8. 類名

 • 類和對象名應是名詞。
 • 實現行爲的類成員函數名應是動詞。
 • 類的存取和查詢成員函數名應是名詞或形容詞。

9. 風格兼容性

 • 對於移植的或者開源的代碼,能夠沿用原有風格,不用C++的命名規範。

4、代碼風格

1. Tab和空格

 • 每一行開始處的縮進只能用Tab,建議tab用四個空格代替,感受tab8個空格略長不美觀。不能用空格,輸入內容以後統一用空格。除了最開始的縮進控制用Tab,其餘部分爲了對齊,須要使用空格進行縮進。這樣能夠避免在不一樣的編輯器下顯示不對齊的狀況。
 • 在代碼行的結尾部分不能出現多餘的空格。
 • 不要在"::","->","."先後加空格。
 • 不要在",",";"以前加空格。

2. 類型定義和{

 • 類,結構,枚舉,聯合:大括號另起一行

3. 函數

 • 函數體的{須要新起一行,在{以前不能有縮進。
 • 除了特別狀況,函數體內不能出現兩個空行。
 • 除了特別狀況,函數體內不能宏定義指令。
 • 在一個函數體內,邏揖上密切相關的語句之間不加空行,其它地方應加空行分隔。
 • 在頭文件定義的inline函數,函數之間能夠不用空行,推薦用一個空行。

4. 代碼塊

 • "if"、"for"、"while"、"do"、"try"、"catch" 等語句自佔一行,執行語句不得緊跟其後。不論執行語句有多少都要加 「{ }」 。這樣能夠防止書寫和修改代碼時出現失誤。
 • "if"、"for"、"while"、"do"、"try"、"catch" 的括號和表達式,括號可緊挨關鍵字,這樣強調的是表達式。

5. else

• if語句若是有else語句,用 } else { 編寫爲一行,不推薦用 3 行代碼的方式。

6. 代碼行

 • 一行代碼只作一件事情,如只定義一個變量,或只寫一條語句。這樣的代碼容易閱讀,而且方便於寫註釋。
 • 多行變量定義,爲了追求代碼排版美觀,可將變量豎向對齊。
 • 代碼行最大長度宜控制在必定個字符之內,能在當前屏幕內所有可見爲宜。

7. switch語句

 • case關鍵字應和switch對齊。
 • case子語句若是有變量,應用{}包含起來。
 • 若是有並列的相似的簡單case語句,可考慮將case代碼塊寫爲一行代碼。
 • 簡單的case之間可不用空行,複雜的case之間應考慮用空行分割開。
 • case字語句的大括號另起一行,不要和case寫到一行。
 • 爲全部switch語句提供default分支。
 • 若某個case不須要break必定要加註釋聲明。

8. 循環

 • 空循環可用 for( ;; ) 或者 while( 1 ) 或者 while( true )

9. 類

 • 類繼承應採用每一個基類佔據一行的方式。
 • 單繼承可將基類放在類定義的同一行。若是用多行,則應用Tab縮進。
 • 多繼承在基類比較多的狀況下,應將基類分行,並採用Tab縮進對齊。
 • 重載基類虛函數,應在該組虛函數前寫註釋 // implement XXX
 • 友元聲明放到類的末尾。

10. 宏

 • 不要用分號結束宏定義。
 • 函數宏的每一個參數都要括起來。
 • 不帶參數的宏函數也要定義成函數形式。

11. goto

 • 儘可能不要用goto

5、類型


• 定義指針和引用時*和&緊跟類型。
• 儘可能避免使用浮點數,除非必須。
• 用typedef簡化程序中的複雜語法。
• 避免定義無名稱的類型。例如:typedef enum { EIdle, EActive } TState;
• 少用union,若是必定要用,則採用簡單數據類型成員。
• 用enum取代(一組相關的)常量。
• 不要使用魔鬼數字。
• 儘可能用引用取代指針。
• 定義變量完成後當即初始化,勿等到使用時才進行。
• 若是有更優雅的解決方案,不要使用強制類型轉換。

6、表達式


• 避免在表達式中用賦值語句。
• 避免對浮點類型作等於或不等於判斷。
• 不能將枚舉類型進行運算後再賦給枚舉變量。
• 在循環過程當中不要修改循環計數器。

7、函數


1. 引用

 • 引用類型做爲返回值:函數必須返回一個存在的對象。
 • 引用類型做爲參數:調用者必須傳遞一個存在的對象。

2. 常量成員函數

 • 表示該函數只讀取對象的內容,不會對對象進行修改。

3. 返回值

 • 除開void函數,構造函數,析構函數,其它函數必需要有返回值。
 • 當函數返回引用或指針時,用文字描述其有效期。

4. 內聯函數

 • 內聯函數應將函數體放到類體外。
 • 只有簡單的函數纔有必要設計爲內聯函數,複雜業務邏輯的函數不要這麼作。
 • 虛函數不要設計爲內聯函數。

5. 函數參數

 • 只讀取該參數的內容,不對其內容作修改,用常量引用。
 • 修改參數內容,或須要經過參數返回,用很是量應用。
 • 簡單數據類型用傳值方式。
 • 複雜數據類型用引用或指針方式。

8、類


1. 構造函數

 • 構造函數的初始化列表,應和類的順序一致。
 • 初始化列表中的每一個項,應獨佔一行。
 • 避免出現用一個成員初始化另外一個成員。
 • 構造函數應初始化全部成員,尤爲是指針。
 • 不要在構造函數和析構函數中拋出異常。

2. 純虛函數

 • M類的虛函數應設計爲純虛函數。

3. 構造和析構函數

 • 若是類能夠繼承,則應將類析構函數設計爲虛函數。
 • 若是類不容許繼承,則應將類析構函數設計爲非虛函數。
 • 若是類不能被複制,則應將拷貝構造函數和賦值運算符設計爲私有的。
 • 若是爲類設計了構造函數,則應有析構函數。

4. 成員變量

 • 儘可能避免使用mutableVolatile
 • 儘可能避免使用公有成員變量。

5. 成員函數

 • 努力使類的接口少而完備。
 • 儘可能使用常成員函數代替很是成員函數,const函數
 • 除非特別理由,毫不要從新定義(繼承來的)非虛函數。(這樣是覆蓋,基類的某些屬性無初始化)

6. 繼承

 • 繼承必須知足IS-A的關係,HAS-A應採用包含。
 • 虛函數不要採用默認參數。
 • 除非特別須要,應避免設計大而全的虛函數,虛函數功能要單一。
 • 除非特別須要,避免將基類強制轉換成派生類。

7. 友元

 • 儘可能避免使用友元函數和友元類。

9、錯誤處理


• 申請內存用new操做符。
• 釋放內存用delete操做符。
• newdeletenew[]delete[]成對使用。
• 申請內存完成以後,要檢測指針是否申請成功,處理申請失敗的狀況。
• 誰申請誰釋放。優先級:函數層面,類層面,模塊層面。
• 釋放內存完成後將指針賦空,避免出現野指針。
• 使用指針前進行判斷合法性,應考慮到爲空的狀況的處理。
• 使用數組時,應先判斷索引的有效性,處理無效的索引的狀況。
• 代碼不能出現編譯警告。
• 使用錯誤傳遞的錯誤處理思想。
• 衛句風格:先處理全部可能發生錯誤的狀況,再處理正常狀況。
• 嵌套do-while(0)宏:目的是將一組語句變成一個語句,避免被其餘if等中斷。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息