最全面的C/C++編碼規(guī)范總結(jié)
對(duì)于不同的編程語(yǔ)言來(lái)說(shuō),具體的編碼規(guī)范可以有很大的不同,但是其宗旨都是一致的,就是保證代碼在高質(zhì)量完成需求的同時(shí)具備良好的可讀性、可維護(hù)性。例如我們可以規(guī)定某個(gè)項(xiàng)目的C語(yǔ)言程序要遵循這樣的規(guī)定:變量的命名,頭文件的書寫和#include
等等。
下面是一些廣為采用的編碼規(guī)范:
-
GNU Coding Standards
-
Guidelines for the Use of the C Language in Vehicle Based Software
-
C++ Coding Guidelines
-
SUN Code Conventions for Java
以下是一些介紹編碼、編碼規(guī)范的書籍:
-
C++編碼規(guī)范,陳世忠,人民郵電出版社,2002
-
高質(zhì)量程序設(shè)計(jì)指南:C++/C語(yǔ)言,林銳等,電子工業(yè)出版社,2003
注:以下只是根據(jù)課題組已有的經(jīng)驗(yàn)給出的總結(jié),并非對(duì)所有場(chǎng)景均適用。
對(duì)于高質(zhì)量的工程,一般會(huì)做到:
-
代碼簡(jiǎn)潔精煉,美觀,可讀性好,高效率,高復(fù)用,可移植性好,高內(nèi)聚,低耦合,沒有冗余,不符合這些原則,必須特別說(shuō)明。
-
規(guī)范性,代碼有規(guī)可循。特殊排版、特殊語(yǔ)法、特殊指令,必須特別說(shuō)明。
一、文件排版方面
1.包含頭文件
1.1 先系統(tǒng)頭文件,后用戶頭文件。
1.2 系統(tǒng)頭文件,穩(wěn)定的目錄結(jié)構(gòu),應(yīng)采用包含子路徑方式。
1.3 自定義頭文件,不穩(wěn)定目錄結(jié)構(gòu),應(yīng)在dsp中指定包含路徑。
1.4 系統(tǒng)頭文件應(yīng)用:#include <xxx.h>
1.5 自定義同文件應(yīng)用:#include "xxx.h"
1.6 只引用需要的頭文件。
2.h和cpp文件
2.1 頭文件命名為*.h
,內(nèi)聯(lián)文件命名為*.inl
;C++文件命名為*.cpp
2.2 文件名用大小寫混合,或者小寫混合。例如DiyMainview.cpp
,infoview.cpp
。不要用無(wú)意義的名稱:例如XImage.cpp
;SView.cpp
;xlog.cpp
;
2.3 頭文件除了特殊情況,應(yīng)使用#ifdef
控制塊。
2.4 頭文件#endif
應(yīng)采用行尾注釋。
2.5 頭文件,首先是包含代碼塊,其次是宏定義代碼塊,然后是全局變量,全局常量,類型定義,類定義,內(nèi)聯(lián)部分。
2.6 CPP文件,包含指令,宏定義,全局變量,函數(shù)定義。
3.文件結(jié)構(gòu)
3.1 文件應(yīng)包含文件頭注釋和內(nèi)容。
3.2 函數(shù)體類體之間原則上用2個(gè)空行,特殊情況下可用一個(gè)或者不需要空行。
4.空行
4.1 文件頭、控制塊,#include
部分、宏定義部分、class
部分、全局常量部分、全局變量部分、函數(shù)和函數(shù)之間,用兩個(gè)空行。
二、注釋方面
1.文件頭注釋
1.1 作者,文件名稱,文件說(shuō)明,生成日期(可選)
2.函數(shù)注釋
2.1 關(guān)鍵函數(shù)必須寫上注釋,說(shuō)明函數(shù)的用途。
2.2 特別函數(shù)參數(shù),需要說(shuō)明參數(shù)的目的,由誰(shuí)負(fù)責(zé)釋放等等。
2.3 除了特別情況,注釋寫在代碼之前,不要放到代碼行之后。
2.4 對(duì)每個(gè)#else
或#endif
給出行末注釋。
2.5 關(guān)鍵代碼注釋,包括但不限于:賦值,函數(shù)調(diào)用,表達(dá)式,分支等等。
2.6 善未實(shí)現(xiàn)完整的代碼,或者需要進(jìn)一步優(yōu)化的代碼,應(yīng)加上 // TODO ...
2.7 調(diào)試的代碼,加上注釋 // only for DEBUG
2.8 需要引起關(guān)注的代碼,加上注釋 // NOTE ...
2.9 對(duì)于較大的代碼塊結(jié)尾,如for,while,do
等,可加上 // end for|while|do
三、命名方面
1.原則
1.1 同一性:在編寫一個(gè)子模塊或派生類的時(shí)候,要遵循其基類或整體模塊的命名風(fēng)格,保持命名風(fēng)格在整個(gè)模塊中的同一性。
1.2 標(biāo)識(shí)符組成:標(biāo)識(shí)符采用英文單詞或其組合,應(yīng)當(dāng)直觀且可以拼讀,可望文知意,用詞應(yīng)當(dāng)準(zhǔn)確,避免用拼音命名。
1.3 最小化長(zhǎng)度 && ***化信息量原則:在保持一個(gè)標(biāo)識(shí)符意思明確的同時(shí),應(yīng)當(dāng)盡量縮短其長(zhǎng)度。
1.4 避免過(guò)于相似:不要出現(xiàn)僅靠大小寫區(qū)分的相似的標(biāo)識(shí)符,例如"i"
與"I"
,"function"
與"Function"
等等。
1.5 避免在不同級(jí)別的作用域中重名:程序中不要出現(xiàn)名字完全相同的局部變量和全局變量,盡管兩者的作用域不同而不會(huì)發(fā)生語(yǔ)法錯(cuò)誤,但容易使人誤解。
1.6 正確命名具有互斥意義的標(biāo)識(shí)符:用正確的反義詞組命名具有互斥意義的標(biāo)識(shí)符,如:"nMinValue"
和 "nMaxValue"
,"GetName()"
和"SetName()"
….
1.7 避免名字中出現(xiàn)數(shù)字編號(hào):盡量避免名字中出現(xiàn)數(shù)字編號(hào),如Value1,Value2
等,除非邏輯上的確需要編號(hào)。這是為了防止程序員偷懶,不肯為命名動(dòng)腦筋而導(dǎo)致產(chǎn)生無(wú)意義的名字(因?yàn)橛脭?shù)字編號(hào)最省事)。
2.T,C,M,R類
2.1 T類表示簡(jiǎn)單數(shù)據(jù)類型,不對(duì)資源擁有控制權(quán),在析構(gòu)過(guò)程中沒有釋放資源動(dòng)作。
2.2 C表示從CBase繼承的類。該類不能從棧上定義變量,只能從堆上創(chuàng)建。
2.3 M表示接口類。
2.4 R是資源類,通常是系統(tǒng)固有類型。除了特殊情況,不應(yīng)在開發(fā)代碼中出現(xiàn)R類型。
3.函數(shù)名
3.1 M類的函數(shù)名稱應(yīng)采用HandleXXX
命名,例如:HandleTimerEvent
;不推薦采用java風(fēng)格,例如 handleTimerEvent
;除了標(biāo)準(zhǔn)c風(fēng)格代碼,不推薦用下劃線,例如,handle_event
。
3.2 Leave函數(shù),用后綴L。
3.3 Leave函數(shù),且進(jìn)清除棧,用后綴LC。
3.4 Leave函數(shù),且刪除對(duì)象,用后綴LD。
4.函數(shù)參數(shù)
4.1 函數(shù)參數(shù)用a作為前綴。
4.2 避免出現(xiàn)和匈牙利混合的命名規(guī)則如apBuffer
名稱。用aBuffer
即可。
4.3 函數(shù)參數(shù)比較多時(shí),應(yīng)考慮用結(jié)構(gòu)代替。
4.4 如果不能避免函數(shù)參數(shù)比較多,應(yīng)在排版上可考慮每個(gè)參數(shù)占用一行,參數(shù)名豎向?qū)R。
5.成員變量
5.1 成員變量用m最為前綴。
5.2 避免出現(xiàn)和匈牙利混合的命名規(guī)則如mpBuffer
名稱。用mBuffer
即可。
6.局部變量
6.1 循環(huán)變量和簡(jiǎn)單變量采用簡(jiǎn)單小寫字符串即可。例如,int i
;
6.2 指針變量用p
打頭,例如void* pBuffer;
7.全局變量
7.1 全局變量用g_
最為前綴。
8.類名
8.1 類和對(duì)象名應(yīng)是名詞。
8.2 實(shí)現(xiàn)行為的類成員函數(shù)名應(yīng)是動(dòng)詞。
8.3 類的存取和查詢成員函數(shù)名應(yīng)是名詞或形容詞。
9.風(fēng)格兼容性
9.1 對(duì)于移植的或者開源的代碼,可以沿用原有風(fēng)格,不用C++的命名規(guī)范。
四、代碼風(fēng)格方面
1.Tab和空格
1.1 每一行開始處的縮進(jìn)只能用Tab,不能用空格,輸入內(nèi)容之后統(tǒng)一用空格。除了最開始的縮進(jìn)控制用Tab,其他部分為了對(duì)齊,需要使用空格進(jìn)行縮進(jìn)。這樣可以避免在不同的編輯器下顯示不對(duì)齊的情況。
1.2 在代碼行的結(jié)尾部分不能出現(xiàn)多余的空格。
1.3 不要在"::","->","."
前后加空格。
1.4 不要在",",";"
之前加空格。
2.類型定義和{
2.1 類,結(jié)構(gòu),枚舉,聯(lián)合:大括號(hào)另起一行
3.函數(shù)
3.1 函數(shù)體的{需要新起一行,在{之前不能有縮進(jìn)。
3.2 除了特別情況,函數(shù)體內(nèi)不能出現(xiàn)兩個(gè)空行。
3.3 除了特別情況,函數(shù)體內(nèi)不能宏定義指令。
3.4 在一個(gè)函數(shù)體內(nèi),邏揖上密切相關(guān)的語(yǔ)句之間不加空行,其它地方應(yīng)加空行分隔。
3.5 在頭文件定義的inline
函數(shù),函數(shù)之間可以不用空行,推薦用一個(gè)空行。
4.代碼塊
4.1 "if"、"for"、"while"、"do"、"try"、"catch"
等語(yǔ)句自占一行,執(zhí)行語(yǔ)句不得緊跟其后。不論執(zhí)行語(yǔ)句有多少都要加 "{ }"
。這樣可以防止書寫和修改代碼時(shí)出現(xiàn)失誤。
4.2 "if"、"for"、"while"、"do"、"try"、"catch"
的括號(hào)和表達(dá)式,括號(hào)可緊挨關(guān)鍵字,這樣強(qiáng)調(diào)的是表達(dá)式。
5.else
5.1 if語(yǔ)句如果有else語(yǔ)句,用 } else { 編寫為一行,不推薦用 3 行代碼的方式。
6.代碼行
6.1 一行代碼只做一件事情,如只定義一個(gè)變量,或只寫一條語(yǔ)句。這樣的代碼容易閱讀,并且方便于寫注釋。
6.2 多行變量定義,為了追求代碼排版美觀,可將變量豎向?qū)R。
6.3 代碼行***長(zhǎng)度宜控制在一定個(gè)字符以內(nèi),能在當(dāng)前屏幕內(nèi)全部可見為宜。
7.switch語(yǔ)句
7.1 case關(guān)鍵字應(yīng)和switch對(duì)齊。
7.2 case子語(yǔ)句如果有變量,應(yīng)用{}包含起來(lái)。
7.3 如果有并列的類似的簡(jiǎn)單case語(yǔ)句,可考慮將case代碼塊寫為一行代碼。
7.4 簡(jiǎn)單的case之間可不用空行,復(fù)雜的case之間應(yīng)考慮用空行分割開。
7.5 case字語(yǔ)句的大括號(hào)另起一行,不要和case寫到一行。
7.6 為所有switch語(yǔ)句提供default分支。
7.7 若某個(gè)case不需要break一定要加注釋聲明。
8.循環(huán)
8.1 空循環(huán)可用 for( ;; )
或者 while( 1 )
或者 while( true )
9.類
9.1 類繼承應(yīng)采用每個(gè)基類占據(jù)一行的方式。
9.2 單繼承可將基類放在類定義的同一行。如果用多行,則應(yīng)用Tab縮進(jìn)。
9.3 多繼承在基類比較多的情況下,應(yīng)將基類分行,并采用Tab縮進(jìn)對(duì)齊。
9.4 重載基類虛函數(shù),應(yīng)在該組虛函數(shù)前寫注釋 // implement XXX
9.5 友元聲明放到類的末尾。
10.宏
10.1 不要用分號(hào)結(jié)束宏定義。
10.2 函數(shù)宏的每個(gè)參數(shù)都要括起來(lái)。
10.3 不帶參數(shù)的宏函數(shù)也要定義成函數(shù)形式。
11.goto
11.1 盡量不要用goto。
五、類型
- 定義指針和引用時(shí)
*
和&
緊跟類型。 - 盡量避免使用浮點(diǎn)數(shù),除非必須。
- 用
typedef
簡(jiǎn)化程序中的復(fù)雜語(yǔ)法。 - 避免定義無(wú)名稱的類型。例如:
typedef enum { EIdle, EActive } TState;
- 少用
union
,如果一定要用,則采用簡(jiǎn)單數(shù)據(jù)類型成員。 - 用
enum
取代(一組相關(guān)的)常量。 - 不要使用魔鬼數(shù)字。
- 盡量用引用取代指針。
- 定義變量完成后立即初始化,勿等到使用時(shí)才進(jìn)行。
- 如果有更優(yōu)雅的解決方案,不要使用強(qiáng)制類型轉(zhuǎn)換。
六、表達(dá)式
- 避免在表達(dá)式中用賦值語(yǔ)句。
- 避免對(duì)浮點(diǎn)類型做等于或不等于判斷。
- 不能將枚舉類型進(jìn)行運(yùn)算后再賦給枚舉變量。
- 在循環(huán)過(guò)程中不要修改循環(huán)計(jì)數(shù)器。
- 檢測(cè)空指針,用
if( p )
- 檢測(cè)非空指針,用
if( ! p )
七、函數(shù)
1.引用
1.1 引用類型作為返回值:函數(shù)必須返回一個(gè)存在的對(duì)象。
1.2 引用類型作為參數(shù):調(diào)用者必須傳遞一個(gè)存在的對(duì)象。
2.常量成員函數(shù)
2.1 表示該函數(shù)只讀取對(duì)象的內(nèi)容,不會(huì)對(duì)對(duì)象進(jìn)行修改。
3.返回值
3.1 除開void
函數(shù),構(gòu)造函數(shù),析構(gòu)函數(shù),其它函數(shù)必須要有返回值。
3.2 當(dāng)函數(shù)返回引用或指針時(shí),用文字描述其有效期。
4.內(nèi)聯(lián)函數(shù)
4.1 內(nèi)聯(lián)函數(shù)應(yīng)將函數(shù)體放到類體外。
4.2 只有簡(jiǎn)單的函數(shù)才有必要設(shè)計(jì)為內(nèi)聯(lián)函數(shù),復(fù)雜業(yè)務(wù)邏輯的函數(shù)不要這么做。
4.3 虛函數(shù)不要設(shè)計(jì)為內(nèi)聯(lián)函數(shù)。
5.函數(shù)參數(shù)
5.1 只讀取該參數(shù)的內(nèi)容,不對(duì)其內(nèi)容做修改,用常量引用。
5.2 修改參數(shù)內(nèi)容,或需要通過(guò)參數(shù)返回,用非常量應(yīng)用。
5.3 簡(jiǎn)單數(shù)據(jù)類型用傳值方式。
5.4 復(fù)雜數(shù)據(jù)類型用引用或指針方式。
八、類
1.構(gòu)造函數(shù)
1.1 構(gòu)造函數(shù)的初始化列表,應(yīng)和類的順序一致。
1.2 初始化列表中的每個(gè)項(xiàng),應(yīng)獨(dú)占一行。
1.3 避免出現(xiàn)用一個(gè)成員初始化另一個(gè)成員。
1.4 構(gòu)造函數(shù)應(yīng)初始化所有成員,尤其是指針。
1.5 不要在構(gòu)造函數(shù)和析構(gòu)函數(shù)中拋出異常。
2.純虛函數(shù)
2.1 M類的虛函數(shù)應(yīng)設(shè)計(jì)為純虛函數(shù)。
3.構(gòu)造和析構(gòu)函數(shù)
3.1 如果類可以繼承,則應(yīng)將類析構(gòu)函數(shù)設(shè)計(jì)為虛函數(shù)。
3.2 如果類不允許繼承,則應(yīng)將類析構(gòu)函數(shù)設(shè)計(jì)為非虛函數(shù)。
3.3 如果類不能被復(fù)制,則應(yīng)將拷貝構(gòu)造函數(shù)和賦值運(yùn)算符設(shè)計(jì)為私有的。
3.4 如果為類設(shè)計(jì)了構(gòu)造函數(shù),則應(yīng)有析構(gòu)函數(shù)。
4.成員變量
4.1 盡量避免使用mutable
和Volatile
。
4.2 盡量避免使用公有成員變量。
5.成員函數(shù)
5.1 努力使類的接口少而完備。
5.2 盡量使用常成員函數(shù)代替非常成員函數(shù),const
函數(shù)
5.3 除非特別理由,絕不要重新定義(繼承來(lái)的)非虛函數(shù)。(這樣是覆蓋,基類的某些屬性無(wú)初始化)
6.繼承
6.1 繼承必須滿足IS-A的關(guān)系,HAS-A應(yīng)采用包含。
6.2 虛函數(shù)不要采用默認(rèn)參數(shù)。
6.3 除非特別需要,應(yīng)避免設(shè)計(jì)大而全的虛函數(shù),虛函數(shù)功能要單一。
6.4 除非特別需要,避免將基類強(qiáng)制轉(zhuǎn)換成派生類。
7.友元
7.1 盡量避免使用友元函數(shù)和友元類。
九、錯(cuò)誤處理
- 申請(qǐng)內(nèi)存用
new
操作符。 - 釋放內(nèi)存用
delete
操作符。 new
和delete
,new[]
和delete[]
成對(duì)使用。- 申請(qǐng)內(nèi)存完成之后,要檢測(cè)指針是否申請(qǐng)成功,處理申請(qǐng)失敗的情況。
- 誰(shuí)申請(qǐng)誰(shuí)釋放。優(yōu)先級(jí):函數(shù)層面,類層面,模塊層面。
- 釋放內(nèi)存完成后將指針賦空,避免出現(xiàn)野指針。
- 使用指針前進(jìn)行判斷合法性,應(yīng)考慮到為空的情況的處理。
- 使用數(shù)組時(shí),應(yīng)先判斷索引的有效性,處理無(wú)效的索引的情況。
- 代碼不能出現(xiàn)編譯警告。
- 使用錯(cuò)誤傳遞的錯(cuò)誤處理思想。
- 衛(wèi)句風(fēng)格:先處理所有可能發(fā)生錯(cuò)誤的情況,再處理正常情況。
- 嵌套
do-while(0)
宏:目的是將一組語(yǔ)句變成一個(gè)語(yǔ)句,避免被其他if等中斷。
十、性能
- 使用前向聲明代替
#include
指令。Class M;
- 盡量用
++i
代替i++
。即用前綴代替后綴運(yùn)算。 - 盡量在
for
循環(huán)之前,先寫計(jì)算估值表達(dá)式。 - 盡量避免在循環(huán)體內(nèi)部定義對(duì)象。
- 避免對(duì)象拷貝,尤其是代價(jià)很高的對(duì)象拷貝。
- 避免生成臨時(shí)對(duì)象,尤其是大的臨時(shí)對(duì)象。
- 注意大尺寸對(duì)象數(shù)組。
- 80-20原則。
十一、兼容性
- 遵守ANSI C和ISO C++國(guó)際標(biāo)準(zhǔn)。
- 確保類型轉(zhuǎn)換不會(huì)丟失信息。
- 注意雙字節(jié)字符的兼容性。
- 注意運(yùn)算溢出問(wèn)題。
- 不要假設(shè)類型的存儲(chǔ)尺寸。
- 不要假設(shè)表達(dá)式的運(yùn)算順序。
- 不要假設(shè)函數(shù)參數(shù)的計(jì)算順序。
- 不要假設(shè)不同源文件中靜態(tài)或全局變量的初始化順序。
- 不要依賴編譯器基于實(shí)現(xiàn)、未明確或未定義的功能。
- 將所有
#include
的文件名視為大小寫敏感。 - 避免使用全局變量、靜態(tài)變量、函數(shù)靜態(tài)變量、類靜態(tài)變量。在使用靜態(tài)庫(kù),動(dòng)態(tài)庫(kù),多線程環(huán)境時(shí),會(huì)導(dǎo)致兼容性問(wèn)題。
- 不要重新實(shí)現(xiàn)標(biāo)準(zhǔn)庫(kù)函數(shù),如STL已經(jīng)存在的。