




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
章節內容_______________________________________________________________
0前言目的重點關注約定例外
1原則好代碼的原則類和函數設計指導原則保證靜態類型安全遵循C++ISO標
準優先編譯時檢查錯誤使用命名空間來限定作用域優先使用C++特性而
不是C特性
2命名通用命名文件命名函數命名類型命名變量命名宏、常量、枚舉命名
3格式行寬縮進大括號函數聲明和定義函數調用if語句循環語句switch語句
表達式變量賦值初始化指針和引用編譯預處理空格和空行類
4注釋注釋風格文件頭注釋函數頭注釋代碼注釋
5頭文件頭文件職責頭文件依賴
6作用域命名空間全局函數和靜態成員函數全局變量全局常量和靜態成員常量
7類構造、拷貝構造、賦值和析構函數繼承多重繼承重載
8函數函數設計內聯函數函數參數
9C++其常量與初始化表達式類型轉換資源分配和釋放標準庫const的用法異常
他特性模板宏
10現代代碼簡潔性和安全性提升智能指針Lambda接口
C++特性
0前言
目的
規則并不是完美的,通過禁止在特定情況下有用的特性,可能會對代碼實現造成影
響。但是我們制定規則的目的」為了大多數程序員可以得到更多的好處如果
在團隊運作中認為某個規則無法遵循,希望可以共同改進該規則。
參考該規范之前,希望您具有相應的C++基礎能力,而不是通過該文檔來學習C++。
1.了解C++的ISO標準;2.熟知C++的基本語言特性,包括C++03/11/14/17相
關特性;3.了解C++的標準庫;
重點關注
1約定C++的編程風格,比如命名,排版等。
2C++的模塊化設計,如何設計頭文件,類,接口和函數。
3C++相關特性的優秀實踐,比如常量,類型轉換,資源管理,模板等。
4現代C++的優秀實踐,包括C++11/14/17中可以提高代碼可維護性,提高代
碼可靠性的相關約定。
約定
規則:編程時必須遵守的約定(must)
建議:編程時應該遵守的約定(should)
本規范適用通用C++標準,如果沒有特定的標準版本,適用所有的版本
(C++03/ll/14/17)o
例外
無論是‘規則'還是'建議',都必須理解該條目這么規定的原因,并努力遵守。但是,
有些規則和建議可能會有例外。
在不違背總體原則,經過充分考慮,有充足的理由的前提下,可以適當違背規范中
約定。例外破壞了代碼的一致性,請盡量避免?!巹t’的例外應該是極少的。
下列情況,應風格一致性原則優先:
修改外部開源代碼、第三方代碼時,應該遵守開源代碼、第三方代碼已有規范,
保持風格統一。
某些特定領域,優先參考其行業規范。
1原則
好代碼的原則
我們參考KentBeck的簡單設計四原則來指導我們的如何寫出優秀的代碼,如何有
效地判斷我們的代碼是優秀的。1.通過所有測試(Passesitstests)2.盡可能消壁
重復(Minimizesduplication)3,盡可能清口析表達(Maximizesclarity)4.更少代碼元
素(Hasfewerelements)5.以上四個原則的重要程度依次降低。這組定義被稱做簡
單設計原則。
第一條強調的是外部需求,這是代碼實現最重要的;第二點就是代碼的模塊架構設
計,保證代碼的正交性,保證代碼更容易修改;第三點是代碼的可閱讀性,保證代
碼是容易閱讀的;最后一點才是保證代碼是簡潔的,在簡潔和表達力之間,我們更
看重表達力。
類和函數設計指導原則
C++是典型的面向對象編程語言,軟件工程界已經有很多OOP原則來指導我們編
寫大規模的,高可擴展的,可維護性的代碼:-高內聚,低耦合的基本原則-
SOLID原則-迪米特法則-"Tell,Don'task”原則-組合/聚合復用原則
保證靜態類型安全
我們希望C++應該是靜態類型安全的,這樣可以減少運行時的錯誤,提高代碼的健
壯性。但是由于C++的下面的特性存在,會破壞C++靜態類型安全,我們針對這部
分特性要仔細處理。?unions聯合體-類型轉換cast-縮窄轉換narrowing
conversions-類型退化typedecay-范圍錯誤rangeerrors-void*類型指針
我們可以通過約束這些特性的使用,或者使用C++的新特性,比如variant(C++17],
GSL的span,narrow_cast等來解決這些問題,提高C++代碼的健壯性°
遵循C++ISO標準
希望通過使用ISOC++標準的特性來編寫C++代碼,對于ISO標準中未定義的或者
編譯器實現的特性要謹慎使用,對于GCC等編譯器的提供的擴展特性也需要謹慎
使用,這些特性會導致代碼的可移植性比較差。
注意:如果模塊中需要使用相關的擴展特性來,那么盡可能將這些特性封裝成獨立
的接口,并且可以通過編譯選項關閉或者編譯這些特性。對于這些擴展特性的使用,
請模塊制定特性編程指南來指導這些特性的使用。
優先編譯時檢查錯誤
通過編譯器來優先保證代碼健壯性,而不是通過編寫錯誤處理代碼來處理編譯就可
以發現的異常,比如:
?通過const來保證數據的不變性,防止數據被無意修改。
?通過gskspan等來保證char數組不越界,而不是通過運行時的length檢查。
?通過static_assert來進行編譯時檢查。
使用命名空間來限定作用域
全局變量,全局常量和全局類型定義由于都屬于全局作用域,在項目中,使用第三
方庫中容易出現沖突。
命名空間將作用域細分為獨立的,具名的作用域,可有效地防止全局作用域的命名
沖突。1.class,struct等都具有自己的類作用域。2.具名的namespace可以實現
類作用域更上層的作用域。3.匿名namespace和static可以實現文件作用域。
對于沒有作用域的宏變量,宏函數強烈建議不使用。
作用域的一些缺點:1.雖然可以通過作用域來區分兩個命名相同的類型,但是還
是具有迷惑性。2.內聯命名空間會讓命名空間內部的成員擺脫限制,讓人迷惑。3.
通過多重嵌套來定義namespace,會讓完整的命名空間比較冗長。
所以,我們使用命名空間的建議如下:-對于變量,常量和類型定義盡可能使用
namespace,減少全局作用域的沖突■■不要在頭文件中使用usingnamespace-不要
使用內聯命名空間?■鼓勵在.cpp文件中通過匿名namespace或者static秦封裝,
防止不必要的定義通過API暴露出去。
優先使用C++特性而不是C特性
C++比起c語言更加類型安全,更加抽象。我們更推薦使用C++的語言特性來編程,
比如使用string而不是char*,使用vector而不是原生數組,使用namespace而不是
statico
2命名
通用命名
常見命名風格有:駝峰風格(CamelCase)大小寫字母混用,單詞連在一起,不同
單詞間通過單詞首字母大寫來分開。按連接后的首字母是否大寫,又分:大駝峰
(UperCamelCase)和小駝峰(lowerCamelCase)
內核風格(unix_like)單詞全小寫,用下劃線分割。如:'tesjresult'
匈牙利風格在‘大駝峰’的基礎上,加上前綴;前綴用于表達類型或用途。如:
ruiSavedCount\"bTested1
規則2.1.1標識符命名使用駝峰風格
不考慮匈牙利命名,在內核風格與駝峰風格之間,根據存量代碼的情況,我們選擇
駝峰風格。
類型命名風格
類類型,結構體類型,枚舉類型,聯合體類型等類型定大駝峰
義
函數(包括全局函數,作用域函數,成員函數)大駝峰(接口部分可加
前綴,如XXX一函數
名)
全局變量(包括全局和命名空間域下的變量,類靜態變小駝峰
量),局部變量,函數參數,類、結構體和聯合體中的成
員變量
常量(const),枚舉值k+大小寫混合
宏大寫+下劃線
命名空間全小寫
注意:上表中一常量一是指全局作用域、namespace域、類的靜態成員域下,以
const或constexpr修飾的基本數據類型、枚舉、字符串類型的變量。上表中—變
量—是指除常量定義以外的其他變量,均使用小駝峰風格。
文件命名
建議2.2.1C++文件以.cpp結尾,頭文件以.h結尾
我們推薦使用.h作為頭文件的后綴,這樣頭文件可以直接兼容C和C++。我們推
薦使用.cpp作為實現文件的后綴,這樣可以直接區分C++代碼,而不是C代碼。
目前業界還有一些其他的后綴的表示方法:
?頭文件:.hh,.hpp,.hxx
?cpp文件:.cc,.cxx,.C
對于本文檔,我們默認使用.h和.cpp作為后綴。
建議2.2.2C++文件名和類名保持一致
C++的頭文件和cpp文件名和類名保持一致,使用下劃線小寫風格。
如下:-database_connection.h-database_connection.cpp
結構體,命名空間,枚舉等定義的文件名類似。
函數命名
函數命名統一使用大駝峰風格,一般采用動詞或者動賓結構。接口部分可加前綴,
如XXX一函數名。
classList{
public:
voidAddElement(constElementselement);
ElementGetElement(constunsignedintindex)const;
boolIsEmpty()const;
boolMCC_GetClass();
};
namespaceutils
{void
DeleteUser();
}
類型命名
類型命名采用大駝峰命名風格。所有類型命名一一類、結構體、聯合體、類型定
義(typedef)、枚舉---使用相同約定,例如:
//cLasseSjstructsandunions
classUrlTable{...
classUrlTableTester{...
structUrlTableProperties{???
unionPacket{...
//typedefs
typedefstd::map<std:zstring^UrlTableProperties*>PropertiesMap;
//enums
enumUrlTableErrors{...
對于命名空間的命名,建議全小寫:
//namespace
namespaceosutils{
namespacefileutils{
)
)
建議2.4.1避免濫用typedef或者#define對基本類型起別名
除有明確的必要性,否則不要用typedef/#define對基本數據類型進行重定義。優
先使用<cstdint>頭文件中的基本類型:
有符號類型無符號類型描述
int8_tuint8_t寬度恰為8的有/無符號整數類型
intl6_tuintl6_t寬度恰為16的有/無符號整數類型
int32_tuint32_t寬度恰為32的有/無符號整數類型
int64_tuint64_t寬度恰為64的有/無符號整數類型
intptr_tuintptr_t足以保存指針的有/無符號整數類型
如果模塊有自己的定義,請使用統一的typedef來定義類型:
typedefsignedcharV0S_INT8;
typedefunsignedcharVOS_UINT8;
#if_WORDSIZE==64
typedefunsignedlongintVOS__UINTPTR;
#else
typedefunsignedintVOS_UINTPTR;
#endif
如果模塊為了封裝某個類型的信息,方便后續的擴展,可以使用typedef來重新定
義。
typedefuint8_tDevicelD;
//...
//若干版本后擴展成16-bit
typedefuintl6_tDevicelD;
有特殊作用的類型typedefvoid*Handle;注意:不要使用#define進行別名定
義,并且在C++11以后推薦使用using來定義類型。
除上述理由外,應避免給其本數值類型別名定義。因為類型別名可讀性并不好,隱
藏了基本數值類型信息,如位寬,是否帶符號。濫用舉例:
typedefuintl6___tMyCounter;
//...
intFoo(???)
{MyCounter
c;
while(c>=0)
{printf("counter=%d\n”,
c);
//...
)
//...
對,MyCounter,是否可能小于0,打印時用‘%d,還是,%u,都不是很直觀,極容易引入
上述類似缺陷。
變量命名
通用變量命名采用小駝峰,包括全局變量,函數形參,局部變量,成員變量。
std::stringtableName;//Good:推薦此風格
std::stringtablename;//Bad:禁Ik此風格
std::stringpath;//Good:乂仃?個單詞時,小駝峰為全小”
規則251類的成員變量命名使用小駝峰。
classFoo{
private:
std::stringfileName;//不添加任何作用域前綴或者,綴
};
當構造函數參數和成員變量重名時,可通過this,來引用成員變量。
classMyClass{
public:
MyClass(intmyVar):myVar(myVar){//OK,初始化列表允許同名入參初始化
同名成員
if(NeedNewVar()){
this->myVar=GetValue();//;'匕點?不吸漏掉,否則就成了給人冬
賦值
}
)
private:
intmyVar;
);
宏、常量、枚舉命名
宏采用全大寫,下劃線連接的格式。常量、枚舉值使用k+大小寫混合。函數局部
const常量和類的普通const成員變量,使用小駝峰命名風格。
#defineMAX(a,b)(((a)<(b))?(b):(a))//僅對宏命名舉例,并不推薦
用宏實現此類功能
enumTintcolor{//注意,枚舉類型名用大駝峰,其下面的取值是大小寫混合
kRed,
kDarkRed,
kGreen,
kLightGreen
};'
intFunc(...){
constunsignedintbuffersize=100;//函數局部常顯
char*p=newchar[bufferSize];
}.
namespaceutils{
constunsignedintkFileSize=200;//全局常最
}
3格式
盡管有些編程的排版風格因人而異,但是我們強烈建議和要求使用統一的編碼風格,
以便所有人都能夠輕松的閱讀和理解代碼,增強代碼的可維護性。
行寬
建議3.1.1行寬不超過120個字符
建議每行字符數不要超過120個。如果超過120個字符,請選擇合理的方式進行
換行。
例外:-如果一行注釋包含了超過120個字符的命令或URL,則可以保持一行,以
方便復制、粘貼和通過grep查找;-包含長路徑的include語句可以超出120個
字符,但是也需要盡量避免;-編譯預處理中的error信息可以超出一行。預處理
的error信息在一行便于閱讀和理解,即使超過120個字符。
#ifndefXXX_YYY_ZZZ
#errorHeaderaaaa/bbbb/cccc/abc.hmustonlybeincludedafterxxxx/yyy
y/zzzz/xyz.h,becausexxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#endif
縮進
規則3.2.1使用空格進行縮進,每次縮進2個空格
只允許使用空格(space)進行縮進,每次縮進為2個空格。
大括號
規則33:1除函數外,使用K&R縮進風格
函數左大括號跟隨語句放行末。右大括號獨占一行,除非后面跟著同一語句的剩
余部分,如do語句中的while,或者if語句的else/elseif,或者逗號、分號。
如:
structMyType{//跟隨語句放行末,前置1空格
}「?
intFoo(inta){//函數左大括號跟隨語句放行本
if(...){
}else{
}…
}
推薦這種風格的理由:
?代碼更緊湊;
?相比另起一行,放行末使代碼閱讀節奏感上更連續;
?符合后來語言的習慣,符合業界主流習慣;
?現代集成開發環境(IDE)都具有代碼縮進對齊顯示的輔助功能,大括號放在
行尾并不會對縮進和范圍產生理解上的影響。
對于空函數體,可以將大括號放在同一行:
classMyClass{
public:
MyClass():value(0){)
private:
intvalue;
};
函數聲明和定義
規則3.4.1函數聲明和定義的返回類型和函數名在同一行;函數參數列
表超出行寬時要換行并合理對齊
在聲明和定義函數的時候,函數的返回值類型應該和函數名在同一行;如果行寬度
允許,函數參數也應該放在一行;否則,函數參數應該換行,并進行合理對齊。
參數列表的左圓括號總是和函數名在同一行,不要單獨一行;右圓括號總是跟隨最
后一個參數。
換行舉例:
ReturnTypeFunctionName(ArgTypeparamNamel^ArgTypeparamName2){//G
ood:全在同一行
}…
ReturnTypeVeryVeryVeryLongFunctionName(ArgTypeparamNamel^//行寬
不滿足所有參數,進行換行
ArgTypeparamName2,//Good:
和上一行參數對齊
ArgTypeparamName3){
}…
ReturnTypeLongFunctionName(ArgTypeparamNamel,ArgTypeparamName2,//
行寬限制,進行換行
ArgTypeparamNameB,ArgTypeparamName4,ArgTypeparamNameB){/
/Good:換行后4空格縮進
}.
ReturnTypeReallyReallyReallyReallyLongFunctionName(//行寬
不滿足第1個參數,直接換行
ArgTypeparamNamel^ArgTypeparamName2JArgTypeparamName3){//Go
od:換行后4空格縮進
}
函數調用
規則3.5.1函數調用入參列表應放在一行,超出行寬換行時,保持參數
進行合理對齊
函數調用時、函數參數列表放在一行。參數列表如果超過行寬,需要換行并進行合
理的參數對齊。左圓括號總是跟函數名,右圓括號總是跟最后一個參數。
換行舉例:
ReturnTyperesult=FunctionName(paramNamel>paramName2);//Good:函
數參數放在一行
ReturnTyperesult=FunctionName(paramNamel,
paramName2j//Good:保
持與上方參數對齊
paramName3);
ReturnTyperesult=FunctionName(paramNamel,paramName2,
paramName3,paramName4?paramName5);//Good:參
數換行,4空格縮進
ReturnTyperesult=VeryVeryVeryLongFunctionName(//行寬不滿
足第1個參數,直接換行
paramNamel,paramName2,paramName3);//換行后,4
空格縮進
如果函數調用的參數存在內在關聯性,按照可理解性優先于格式排版要求,對參數
進行合理分組換行。
//Good:每行的參數代表一組相關性較強的數據結構,放在一行便于理解
intresult=DealWithStructureLikeParams(left.x^left.y,//表組.
相關參數
right.x,right.y);//表示另外
一組相關參數
if語句
規則3.6.1if語句必須要使用大括號
我們要求if語句都需要使用大括號,即便只有一條語句。
理由:-代碼邏輯直觀,易讀;-在已有條件語句代碼上增加新代碼時不容易出錯;
-對于在if語句中使用函數式宏時,有大括號保護不易出錯(如果宏定義時遺漏了
大括號)。
if(objectlsNotExist){//Good:單行條件語句也加大括號
returnCreateNewObject();
)
規則3.6.2禁止if/else/elseif寫在同一行
條件語句中,若有多個分支,應該寫在不同行。
如下是正確的寫法:
if(someConditions)
{DoSomething();
}else{//Good:else與if在不同行
}…
下面是不符合規范的案例:
if(someConditions){???}else{???}//Bad:eLse與if在同?行
循環語句
規則3.7.1循環語句要求使用大括號
和if語句類似,我們要求for/while循環語句必須加上的大括號,即使循環體是空
的,或者循環語句只有一條。
for(inti=0;i<someRange;i++)
{DoSomething();
)
如果循環體是空的,應該使用空的大括號,而不是使用單個分號。單個分號容易
被遺漏,也容易被誤認為是循環語句中的一部分。
for(inti=0;i<someRange;i++){}//Good:for循環體足"1,使用大
括號,而不是使用分號
while(someCondition){}//Good:while循環體是空,使用大括號,而不是使
用分號
while(someCondition){
continue;//Good:continue太小不邏輯,可以使用大括3也可以不使用
)
壞的例子:
for(inti=0;i<someRange;i++);//Bad:for循環體是空,也不要只
使用分號,要使用大括號
while(someCondition);//Bad:使用分號容%讓人誤解是while語句中的,部分
switch語句
規則3.8.1switch語句的case/default要縮進一層
switch語句的縮進風格如下:
switch(var){
case0://Good:縮進
DoSomethingl();//Good:縮進
break;
case1:{//Good:帶大括號格式
DoSomething2();
break;
)
default:
break;
)
switch(var){
case0://Bad:case未縮進
DoSomething();
break;
default://Bad:defauLt未縮進
break;
)
表達式
建議3.9.1表達式換行要保持換行的一致性,運算符放行末
較長的表達式,不滿足行寬要求的時候,需要在適當的地方換行。一般在較低優先
級運算符或連接符后面截斷,運算符或連接符放在行末。運算符、連接符放在行
末,表示"未結束,后續還有"。例:
//假設下面第一行已經不滿足行寬要求
if(currentvalue>threshold&&//Good:換行后,邏輯操作符放在行尾
someConditionsion)
{DoSomething();
}.
intresult=reallyReallyLongVariableNamel+//Good
reallyReallyLongVariableName2;
表達式換行后,注意保持合理對齊,或者4空格縮進。參考下面例子
intsum=longVaribleNamel+longVaribleName2+longVaribleName3+
longVaribleName4+longVaribleName5+longVaribleName6;//
Good:4空格縮進
intsum=longVaribleNamel+longVaribleName2+longVaribleName3+
longVaribleName4+longVaribleName5+longVaribleName6;//
Good:保持對齊
變量賦值
規則3.10.1多個變量定義和賦值語句不允許寫在一行
每行只有一個變量初始化的語句,更容易閱讀和理解。
intmaxCount=10;
boolisCompleted=false;
下面是不符合規范的示例:
intmaxCount=10;boolisCompleted=false;//Bad:多個變量初始化需要分
開放在多行,每行一個變量初始化
intx,y=0;//Bad:多個變量定義需要分行,每行一個
intpointX;
intpointY;
pointX=1;pointY=2;//Bad:多個變量賦值語句放同一行
例外:for循環頭、if初始化語句(C++17)、結構化綁定語句(C++17)中可以聲
明和初始化多個變量。這些語句中的多個變量聲明有較強關聯,如果強行分成多行
會帶來作用域不一致,聲明和初始化割裂等問題。
初始化
初始化包括結構體、聯合體、及數組的初始化
規則3.11.1初始化換行時要有縮進,并進行合理對齊
結構體或數組初始化時,如果換行應保持4空格縮進。從可讀性角度出發,選擇
換行點和對齊位置。
constintrank[]={
16,16,16,16,32,32,32,32,
64,64,64,64,32,32,32,32
};
指針與引用
建議3.12.1指針類型“*〃跟隨變量名或者類型,不要兩邊都留有或者都
沒有空格
指針命名:*靠左靠右都可以,但是不要兩邊都有或者都沒有空格。
int*p=NULL;//Good
int*p=NULL;//Good
int*p=NULL;//Bad
int*p=NULL;//Bad
例外:當變量被const修飾時,〃*”無法跟隨變量,此時也不要跟隨類型。
char*constVERSION="V100";
建議3.12.2引用類型“&〃跟隨變量名或者類型,不要兩邊都留有或者都
沒有空格
引用命名:&靠左靠右都可以,但是不要兩邊都有或者都沒有空格。
inti=8;
int&p=i;//Good
int&p=i;//Good
int&p=i;//Bad
int&p=i;//Bad
編譯預處理
規則3.13.1編譯預處理的"#"統一放在行首,嵌套編譯預處理語句時,
“留'不縮進
編譯預處理的“#"統一放在行首,即使編譯預處理的代碼是嵌入在函數體中的,“#"
也應該放在行首。
#ifdefined(_x86_64_)&&defined(_GCC_HAVE_SYNC_C0MPARE_AND_SWAP_16)
//Good:放荏行音
#defineATOMIC_X86_HAS_CMPXCHG16B1//Good:放在行首
#else
#defineATOMIC__X86_HAS_CMPXCHG16B0
#endif
intFunctionName(){
if(someThingError){
#ifdefHAS_SYSLOG//Good:即使在函數內部,“井”也放在行首
WriteToSysLog();
#else
WriteToFileLog();
#endif
}
)
內嵌的預處理語句"#"不縮進
#ifdefined(_x86_64_)&&defined(_GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)
#defineAT0MIC_X86_HAS_CMPXCHG16B1//Good:鹵各層次,他亍的讀
#else
#defineAT0MIC_X86_HAS_CMPXCHG16B0
#endif
空格和空行
建議3.14.1水平空格應該突出關鍵字和重要信息,避免不必要的留白
水平空格應該突出關鍵字和重要信息,每行代碼尾部不要加空格??傮w規則如下:
?i£switch,case,do,while,for等關鍵字之后加空格;
?小括號內部的兩側,不要加空格;
?大括號內部兩側有無空格,左右必須保持一致;
?一元操作符(&*+-?!)之后不要加空格;
?二元操作符(=+-<>*/%|&八<=>===!=)左右兩側加空格
?三目運算符(?:)符號兩側均需要空格
?前置和后置的自增、自減(++-)和變量之間不加空格
?結構體成員操作符(.->)前后不加空格
?逗號(,)前面不加空格,后面增加空格
?對于模板和類型轉換(<>)和類型之間不要添加空格
?域操作符(::)前后不要添加空格
?冒號(:)前后根據情況來判斷是否要添加空格
常規情況:
voidFoo(intb){//Good:大括號前應該留空格
inti=0;//Good:變量初始化時,=前后應該有空格,分號前面不要留空格
intbuf[kBufSize]={0};//Good:大括號內兩側都無空格
函數定義和函數調用:
intresult=Foo(argl,arg2);
z//Bad:逗號后面需要增加空格
intresult=Foo(argl,arg2);
八八//Bad:函數參數列表的左括號后面不應該有個
格,右括號前面不應該有空格
指針和取地址
x=*p;//Good:*操作符和指針p之間不加空:做
=&x;//Good:&操作符和變量x之間不加x=
r.y;//Good:通過.訪問成員變量時不加空格x=
r->y;//Good:通過->訪問成員變量時不加空格
操作符:
x=0;//Good:賦值操作的=前后都要加空格
x=-5;//Good:負數的符號和數值之前不要加空格
++X;//Good:前置和后置的++/一和變量之間不要加空格
X--;
if(x&&!y)//Good:伍爾操作符前后要加上空格,!操作和變量之間不要空格
v=w*x+y/z;//Good:二元操作符前后要加空格
v=w*(x+z);//Good:括號內的友達式前后不需要加空格
inta=(x<y)?x:y;//Good:三目運算符,?和:前后需要添加空格
循環和條件語句:
if(condition){//Good:if關鍵字和括號之間加空格,括號內條件語句前后不加
空格
}else{//Good:eLse關鍵字和大括號之間加空格
}…
while(condition){}//Good:whiLe關鍵字和括號之間加空格,括號內條件語
句前后不加空格
for(inti=0;i<someRange;++i){//Good:for關健字和括號之間加空格,
分號之后加空格
}
switch(condition){//Good:switch關鍵字后面有1空格
case0://Good:case語句條件和同號之間不加不,格
break;
default:
break;
}
模板和轉換
//尖括號(<and>)不與空格緊鄰,<前沒有空格,>和(之間也沒有.
vector<string>x;
y=static_cast<char*>(x);
//在類型與指針操作符之間留空格也可以,但要保持一致.
vector<char*>x;
域操作符
std::cout;//Good:命名空間訪問,不要留空格
intMyClass::GetValue()const{}//Good:對于成員函數定義,不要留空格
冒號
//添加空格的場景
//Good:類的派生需要留有空格
classSub:publicBase{
};
//構造函數初始化列表需要留有空格
MyClass::MyClass(intvar):someVar(var)
{DoSomething();
)
//位域表示也留有空格
structXX
{chara:
4;charb:
5;charc:
4;
};
//不添加空格的場景
//Good:對于public:,private:這種類訪問權限的冒號不用添加空格
classMyClass
{public:
MyClass(intvar);
private:
intsomeVar;
};
//對于switch-case的case和default后面的冒號不用添加空格
switch(value){
case1:
DoSomething();
break;
default:
break;
)
注意:當前的集成開發環境(IDE)可以設置刪除行尾的空格,請正確配置。
建議3.14.2合理安排空行,保持代碼緊湊
減少不必要的空行,可以顯示更多的代碼,方便代碼閱讀。下面有一些建議遵守的
規則:-根據上下內容的相關程度,合理安排空行;-函數內部、類型定義內部、
宏內部、初始化表達式內部,不使用連續空行-不使用連續3個空行,或更多-大
括號內的代碼塊行首之前和行尾之后不要加空行。
intFoo(){
}…
//Bad:兩個函數定義間超過了一個空行
intBar(){
)'
if(...){
//Bad:大括號內的代碼塊行首不要加入空行
//Bad:大括號內的代碼塊行尾不要加入空行
}
intFoo(...){
//Bad:函數體內行首不要加空行
}
類
規則3.15.1類訪問控制塊的聲明依次序是public:,protected:,private:,
每個都縮進1個空格
classMyClass:publicBaseClass
{public://注意沒有縮進
MyClass();//標準的4空格縮進
explicitMyClass(intvar);
-MyClass(){}
voidSomeFunction();
voidSomeFunctionThatDoesNothing(){
}
voidSetVar(intvar){someVar=var;}
intGetVar()const{returnsomeVar;}
private:
boolSomeInternalFunction();
intsomeVar;
intsomeOtherVar;
};
在各個部分中,建議將類似的聲明放在一起,并且建議以如下的順序:類型(包括
typedef,using和嵌套的結構體與類),常量,工廠函數,構造函數,賦值運算符,析構函
數,其它成員函數,數據成員。
規則3.15.2構造函數初始化列表放在同一行或按四格縮進并排多行
//如果所有變量能放在同一行:
MyClass::MyClass(intvan):someVar(var)
{DoSomething();
)
//如果不能放在同一行,
//必須置于冒號后,用維迸4個空格
MyClass::MyClass(intvar)
:someVar(var)someOtherVar(var+1){//Good:逗號后面留有空格
DoSomething();
}
//如果初始化列表需要置于多行,需要逐行對齊
MyClass::MyClass(intvar)
:someVar(var),//縮進4個空格
someOtherVar(var+1)
{DoSomething();
)
4注釋
一般的,盡量通過清晰的架構邏輯,好的符號命名來提高代碼可讀性;需要的時候,
才輔以注釋說明。注釋是為了幫助閱讀者快速讀懂代碼,所以要從讀者的角度出
發,按需注釋“
注釋內容要簡潔、明了、無二義性,信息全面且不冗余。
注釋跟代碼一樣重要。寫注釋時要換位思考,用注釋去表達此時讀者真正需要的
信息。在代碼的功能、意圖層次上進行注釋,即注釋解釋代碼難以表達的意圖,不
要重復代碼信息。修改代碼時,也要保證其相關注釋的一致性。只改代碼,不改注
釋是一種不文明行為,破壞了代碼與注釋的一致性,讓閱讀者迷惑、費解,甚至誤解。
注釋風格
在C++代碼中,使用/**/和〃都是可以的。按注釋的目的和位置,注釋可分為不
同的類型,如文件頭注釋、函數頭注釋、代碼注釋等等;同一類型的注釋應該保持統
一的風格。
注意:本文示例代碼中,大量使用‘〃'后置注釋只是為了更精確的描述問題,并
不代表這種注釋風格更好。
文件頭注釋
規則4.2.1文件頭注釋必須包含版權許可
/*
*Copyright(c)[2019][nameofcopyrighthoLder]
*[SoftwareName]isLicensedundertheMuLanPSLvl.
*Youcanusethissoftwareaccordingtothetermsandconditionsoft
heMuLanPSLvl.
*YoumayobtainacopyofMuLanPSLvlat:
*http://License.coscL./MuLanPSL
*THISSOFTWAREISPROVIDEDONAN"ASIS"BASIS,WITHOUTWARRANTIESOF
ANYKIND,EITHEREXPRESSOR
*IMPLIED,INCLUDINGBUTNOTLIMITEDTON0N-7NFR工NGEMENT,MERCHANTABIL
ITYORFITFORAPARTICULAR
*PURPOSE.
*SeetheMuLanPSLvlformoredetaiLs.
*/
函數頭注釋
規則431禁止空有格式的函數頭注釋
并不是所有的函數都需要函數頭注釋;函數簽名無法表達的信息,加函數頭注釋
輔助說明;
函數頭注釋統一放在函數聲明或定義上方,使用如下風格之一:使用〃寫函數頭
//單行函數頭
intFuncl(void);
//多行函數頭
//第二行
intFunc2(void);
使用/**/寫函數頭
/*單行函數頭*/
intFuncl(void);
/*
*另一種單行函數頭
*/
intFunc2(void);
/*
*多行函數頭
*第二行
*/
intFunc3(void);
函數盡量通過函數名自注釋,按需寫函數頭注釋。不要寫無用、信息冗余的函數
頭;不要寫空有格式的函數頭。
函數頭注釋內容可選,但不限于:功能說明、返回值,性能約束、用法、內存約定、
算法實現、可重入的要求等等。模塊對外頭文件中的函數接口聲明,其函數頭注
釋,應當將重要、有用的信息表達清楚。
例:
*返回實際寫入的字節數,-1表示寫入失敗
*注意,內存buf由調用者負責釋放
*/
intWriteString(constchar*buf,intlen);
壞的例子:
/*
*函數名:lAlriteString
*功能:寫入字符串
*參數:
*返回值:
*/
intWriteString(constchar*buf,intlen);
上面例子中的問題:
?參數、返回值,空有格式沒內容
?函數名信息冗余
?關鍵的buf由誰釋放沒有說清楚
代碼注釋
規則4.4.1代碼注釋放于對應代碼的上方或右邊
規則4.4.2注釋符與注釋內容間要有1空格;右置注釋與前面代碼至少
1空格
代碼上方的注釋,應該保持對應代碼一樣的縮進。選擇并統一使用如下風格之一:
使用〃
//這是單行注釋
DoSomething();
//這是多行注釋
//第二行
DoSomethingO;
使用/*''*/
/*這是單行注釋*/
DoSomething();
/*
*另一種方式的多行注釋
*第二行
*/
DoSomething();
代碼右邊的注釋,與代碼之間,至少留1空格,建議不超過4空格。通常使用擴
展后的TAB鍵即可實現1-4空格的縮進。
選擇并統一使用如下風格之一:
intfoo=100;//放右邊的注釋
intbar=200;/*放右邊的注釋:*/
右置格式在適當的時候,上下對齊會更美觀。對齊后的注釋,離左邊代碼最近的
那一行,保證1-4空格的間隔。例:
constintkConst=100;/*相關的同類注釋,可以考慮上下對齊*/
constintkAnotherConst=200;/*上卜對,齊時,與左側代碼保持間隔*/
當右置的注釋超過行寬時.,請考慮將注釋置于代碼上方。
規則4.4.3不用的代碼段直接刪除,不要注釋掉
被注釋掉的代碼,無法被正常維護;當企圖恢復使用這段代碼時,極有可能引入易
被忽略的缺陷。正確的做法是,不需要的代碼直接刪除掉。若再需要時,考慮移植
或重寫這段代碼。
這里說的注釋掉代碼,包括用/**/和〃,還包括#ifO,#ifdefNEVER.DEFINED
等等。
建議4.4.1正式交付給客戶的代碼不能包含TODO/TBD/FIXME注釋
TODO/TBD注釋一般用來描述已知待改進、待補充的修改點FIXME注釋一般用來
描述已知缺陷它們都應該有統一風格,方便文本搜索統一處理。如:
//TODOf<author-name>):補充XX處理
//FIXME.XX缺陷
5頭文件
頭文件職責
頭文件是模塊或文件的對外接口,頭文件的設計體現了大部分的系統設計。頭文
件中適合放置接口的聲明,不適合放置實現(內聯函數除外)。對于cpp文件中
內部才需要使用的函數、宏、枚舉、結構定義等不要放在頭文件中。頭文件應當
職責單一。頭文件過于復雜,依賴過于復雜還是導致編譯時間過長的主要原因。
建議5.L1每一個.cpp文件應有一個對應的.h文件,用于聲明需要對外
公開的類與接口
通常情況下,每個.cpp文件都有一個相應的.h,用于放置對外提供的函數聲明、宏
定義、類型定義等。如果一個.cpp文件不需要對外公布任何接口,則其就不應當
存在。例外:程序的入口(如main函數所在的文件),單元測試代碼,動態庫
代碼。
示例:
//Foo.h
#ifndefFOO_H
#defineFOO二H
classFoo
{public:
Foo();
voidFun();
private:
intvalue;
};
#endif
//Foo.cpp
#include"Foo.h',
namespace{//Good:對內函數的聲明放在.cpp文件的頭部,并聲明為匿名namespa
cestatic限制其作用域
voidBar()
(
)
}
voidFoo::Fun()
{Bar();
)
頭文件依賴
規則5.2.1禁止頭文件循環依賴
頭文件循環依賴,指a.h包含b.h,b.h包含c.h,c.h包含a.h,導致任何一個頭文
件修改,都導致所有包含了a.h/b.h/c.h的代碼全部重新編譯一遍。
而如果是單向依賴,如a.h包含b.h,b.h包含c.h,而c.h不包含任何頭文件,則
修改a.h不會導致包含了b.h/c.h的源代碼重新編譯。
頭文件循環依賴直接體現了架構設計上的不合理,可通過優化架構去避免。
規則5.2.2禁止包含用不到的頭文件
用不到的頭文件被包含的同時引入了不必要的依賴,增加了模塊或單元之間的耦合
度,只要該頭文件被修改,代碼就要重新編譯。
很多系統中頭文件包含關系復雜,開發人員為了省事起見,直接包含一切想到的頭
文件,甚至發布了一個god.h,其中包含了所有頭文件,然后發布給各個項目組使
用,這種只圖一時省事的做法,導致整個系統的編譯時間進一步惡化,并對后來人
的維護造成了巨大的麻煩。
規則5.2.3頭文件應當自包含
簡單的說,自包含就是任意一個頭文件均可獨立編譯。如果一個文件包含某個頭文
件,還要包含另外一個頭文件才能工作的話,給這個頭文件的用戶增添不必要的負
擔。
示例:如果a.h不是自包含的,需要包含b.h才能編譯,會帶來的危害:每個使用
a.h頭文件的.cpp文件,為了讓引入的a.h的內容編譯通過,都要包含額外的頭文
件b.h。額外的頭文件b.h必須在a.h之前進行包含,這在包含順序上產生了依賴。
規則5.2.4頭文件必須編寫#define保護,防止重復包含
為防止頭文件被重復包含,所有頭文件都應當使用#define保護;不要使用
#pragmaonce
定義包含保護符時,應該遵守如下規則:1)保護符使用唯一名稱;2)不要在受
保護部分的前后放置代碼或者注釋,文件頭注釋除外。
示例:假定VOS工程的timer模塊的timer.h,其目錄為
VOS/include/timer/Timer.h,應按如下方式保護:
#ifndefVOS_INCLUDE_TIMER_TIMER_H
#defineVOS二INCLUDE2nMER二TIMER%
#endif
也可以不用像上面添加路徑,但是要保證當前工程內宏是唯一的。
#ifndefTIMER_H
#defineTIMER_H
#endif
建議5.2.1禁止通過聲明的方式引用外部函數接口、變量
只能通過包含頭文件的方式使用其他模塊或文件提供的接口。通過extern聲明的
方式使用外部函數接口、變量,容易在外部接口改變時可能導致聲明和定義不一致。
同時這種隱式依賴,容易導致架構腐化。
不符合規范的案例:
//a.cpp內容
externintFun();//Bad:通過extern的方式使用外部函數
voidBar()
{inti=
Fun();
//b.cpp內容
intFun(){
//Dosomething
}
應該改為:
//a.cpp內容
#include"b.h"http://Good:通過包含頭文件的方式使用其他,cpp提供的接口
voidBar()
{inti=
Fun();
//b.h內容
intFun();
〃b.cpp內容
intFun(){
//Dosomething
)
例外,有些場景需要引用其內部函數,但并不想侵入代碼時,可以extern聲明方
式引用。如:針對某一內部函數進行單元測試時,可以通過extern聲明來引用被測
函數;當需要對某一函數進行打樁、打補丁處理時;允許extern聲明該函數。
規則5.2.5禁止在extern"C"中包含頭文件
在extern"C"中包含頭文件,有可能會導致extern"C"嵌套,部分編譯器對extern
“C”嵌套層次有限制,嵌套層次太多會編譯錯誤。
在C,C++混合
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
評論
0/150
提交評論