華為C語(yǔ)言通用編程規(guī)范_第1頁(yè)
華為C語(yǔ)言通用編程規(guī)范_第2頁(yè)
華為C語(yǔ)言通用編程規(guī)范_第3頁(yè)
華為C語(yǔ)言通用編程規(guī)范_第4頁(yè)
華為C語(yǔ)言通用編程規(guī)范_第5頁(yè)
已閱讀5頁(yè),還剩59頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

章節(jié) 內(nèi)容 0前言 目的重點(diǎn)關(guān)注約定例外1原則 好代碼的原則類和函數(shù)設(shè)計(jì)指導(dǎo)原則保證靜態(tài)類型安全遵循C++ISO標(biāo)準(zhǔn)優(yōu)先編譯時(shí)檢查錯(cuò)誤使用命名空間來(lái)限定作用域優(yōu)先使用C++特性而不是C特性2命名通用命名文件命名函數(shù)命名類型命名變量命名宏、常量、枚舉命名3格式行寬縮進(jìn)大括號(hào)函數(shù)聲明和定義函數(shù)調(diào)用if語(yǔ)句循環(huán)語(yǔ)句switch語(yǔ)句表達(dá)式變量賦值初始化指針和引用編譯預(yù)處理空格和空行類4注釋注釋風(fēng)格文件頭注釋函數(shù)頭注釋代碼注釋5頭文件頭文件職責(zé)頭文件依賴6作用域命名空間全局函數(shù)和靜態(tài)成員函數(shù)全局變量全局常量和靜態(tài)成員常量7類構(gòu)造、拷貝構(gòu)造、賦值和析構(gòu)函數(shù)繼承多重繼承重載8函數(shù)函數(shù)設(shè)計(jì)內(nèi)聯(lián)函數(shù)函數(shù)參數(shù)9C++其常量與初始化表達(dá)式類型轉(zhuǎn)換資源分配和釋放標(biāo)準(zhǔn)庫(kù)const的用法異常他特性模板宏10現(xiàn)代代碼簡(jiǎn)潔性和安全性提升智能指針Lambda接口C++特性。前言目的規(guī)則并不是完美的,通過(guò)禁止在特定情況下有用的特性,可能會(huì)對(duì)代碼實(shí)現(xiàn)造成影響。但是我們制定規(guī)則的目的—"為了大多數(shù)程序員可以得到更多的好處如果在團(tuán)隊(duì)運(yùn)作中認(rèn)為某個(gè)規(guī)則無(wú)法遵循,希望可以共同改進(jìn)該規(guī)則。參考該規(guī)范之前,希望您具有相應(yīng)的C++基礎(chǔ)能力,而不是通過(guò)該文檔來(lái)學(xué)習(xí)C++。1.了解C++的ISO標(biāo)準(zhǔn);2.熟知C++的基本語(yǔ)言特性,包括C++03/11/14/17相關(guān)特性;3.了解C++的標(biāo)準(zhǔn)庫(kù);重點(diǎn)關(guān)注約定C++的編程風(fēng)格,比如命名,排版等。C++的模塊化設(shè)計(jì),如何設(shè)計(jì)頭文件,類,接口和函數(shù)。C++相關(guān)特性的優(yōu)秀實(shí)踐,比如常量,類型轉(zhuǎn)換,資源管理,模板等。現(xiàn)代C++的優(yōu)秀實(shí)踐,包括C++11/14/17中可以提高代碼可維護(hù)性,提高代碼可靠性的相關(guān)約定。約定規(guī)則:編程時(shí)必須遵守的約定(must)建議:編程時(shí)應(yīng)該遵守的約定(should)本規(guī)范適用通用C++標(biāo)準(zhǔn),如果沒(méi)有特定的標(biāo)準(zhǔn)版本,適用所有的版本(C++03/11/14/17),例外無(wú)論是‘規(guī)則'還是'建議',都必須理解該條目這么規(guī)定的原因,并努力遵守。但是,有些規(guī)則和建議可能會(huì)有例外。在不違背總體原則,經(jīng)過(guò)充分考慮,有充足的理由的前提下,可以適當(dāng)違背規(guī)范中約定。例外破壞了代碼的一致性,請(qǐng)盡量避免。'規(guī)則’的例外應(yīng)該是極少的。下列情況,應(yīng)風(fēng)格一致性原則優(yōu)先:修改外部開(kāi)源代碼、第三方代碼時(shí),應(yīng)該遵守開(kāi)源代碼、第三方代碼已有規(guī)范,口持風(fēng)格結(jié)一某些特定M域:優(yōu)先參考其行業(yè)規(guī)范。1原則好代碼的原則我們參考KentBeck的簡(jiǎn)單設(shè)計(jì)四原則來(lái)指導(dǎo)我們的如何寫(xiě)出優(yōu)秀的代碼,如何有效地判斷我們的代碼是優(yōu)秀的。1.通過(guò)所有測(cè)試(Passesitstests)2.盡可能消除重復(fù)(Minimizesduplication)3.盡可能清晰表達(dá)(Maximizesclarity)4.更少代碼元素(Hasfewerelements)5.以上四個(gè)原則的重要程度依次降低。這組定義被稱做簡(jiǎn)單設(shè)計(jì)原則。第一條強(qiáng)調(diào)的是外部需求,這是代碼實(shí)現(xiàn)最重要的;第二點(diǎn)就是代碼的模塊架構(gòu)設(shè)計(jì),保證代碼的正交性,保證代碼更容易修改;第三點(diǎn)是代碼的可閱讀性,保證代碼是容易閱讀的;最后一點(diǎn)才是保證代碼是簡(jiǎn)潔的,在簡(jiǎn)潔和表達(dá)力之間,我們更看重表達(dá)力。類和函數(shù)設(shè)計(jì)指導(dǎo)原則C++是典型的面向?qū)ο缶幊陶Z(yǔ)言,軟件工程界已經(jīng)有很多OOP原則來(lái)指導(dǎo)我們編寫(xiě)大規(guī)模的,高可擴(kuò)展的,可維護(hù)性的代碼:-高內(nèi)聚,低耦合的基本原則-SOLID原則-迪米特法則-"Tell,Don,task"原則-組合/聚合復(fù)用原則保證靜態(tài)類型安全我們希望C++應(yīng)該是靜態(tài)類型安全的,這樣可以減少運(yùn)行時(shí)的錯(cuò)誤,提高代碼的健壯性。但是由于C++的下面的特性存在,會(huì)破壞C++靜態(tài)類型安全,我們針對(duì)這部分特性要仔細(xì)處理。?unions聯(lián)合體?類型轉(zhuǎn)換cast-縮窄轉(zhuǎn)換narrowingconversions-類型退化typedecay-范圍錯(cuò)誤rangeerrors-void*類型指針我們可以通過(guò)約束這些特性的使用,或者使用C++的新特性,比如variant(C++17),GSL的span,narrow_cast等來(lái)解決這些問(wèn)題,提高C++代碼的健壯性。遵循C++ISO標(biāo)準(zhǔn)希望通過(guò)使用ISOC++標(biāo)準(zhǔn)的特性來(lái)編寫(xiě)C++代碼,對(duì)于ISO標(biāo)準(zhǔn)中未定義的或者編譯器實(shí)現(xiàn)的特性要謹(jǐn)慎使用,對(duì)于GCC等編譯器的提供的擴(kuò)展特性也需要謹(jǐn)慎使用,這些特性會(huì)導(dǎo)致代碼的可移植性比較差。注意:如果模塊中需要使用相關(guān)的擴(kuò)展特性來(lái),那么盡可能將這些特性封裝成獨(dú)立的接口,并且可以通過(guò)編譯選項(xiàng)關(guān)閉或者編譯這些特性。對(duì)于這些擴(kuò)展特性的使用,請(qǐng)模塊制定特性編程指南來(lái)指導(dǎo)這些特性的使用。優(yōu)先編譯時(shí)檢查錯(cuò)誤通過(guò)編譯器來(lái)優(yōu)先保證代碼健壯性,而不是通過(guò)編寫(xiě)錯(cuò)誤處理代碼來(lái)處理編譯就可以發(fā)現(xiàn)的異常,比如:通過(guò)const來(lái)保證數(shù)據(jù)的不變性,防止數(shù)據(jù)被無(wú)意修改。通過(guò)gsl::span等來(lái)保證char數(shù)組不越界,而不是通過(guò)運(yùn)行時(shí)的length檢查。通過(guò)static_assert來(lái)進(jìn)行編譯時(shí)檢查。使用命名空間來(lái)限定作用域全局變量,全局常量和全局類型定義由于都屬于全局作用域,在項(xiàng)目中,使用第三方庫(kù)中容易出現(xiàn)沖突。命名空間將作用域細(xì)分為獨(dú)立的,具名的作用域,可有效地防止全局作用域的命名沖突。1.class,struct等都具有自己的類作用域。2.具名的namespace可以實(shí)現(xiàn)類作用域更上層的作用域。3.匿名namespace和static可以實(shí)現(xiàn)文件作用域。對(duì)于沒(méi)有作用域的宏變量,宏函數(shù)強(qiáng)烈建議不使用。作用域的一些缺點(diǎn):1.雖然可以通過(guò)作用域來(lái)區(qū)分兩個(gè)命名相同的類型,但是還是具有迷惑性。2.內(nèi)聯(lián)命名空間會(huì)讓命名空間內(nèi)部的成員擺脫限制,讓人迷惑。3.通過(guò)多重嵌套來(lái)定義namespace,會(huì)讓完整的命名空間比較冗長(zhǎng)。

所以,我們使用命名空間的建議如下:-對(duì)于變量,常量和類型定義盡可能使用namespace,減少全局作用域的沖突-不要在頭文件中使用usingnamespace-不要使用內(nèi)聯(lián)命名空間-鼓勵(lì)在.cpp文件中通過(guò)匿名namespace或者static集封裝,防止不必要的定義通過(guò)API暴露出去。優(yōu)先使用C++特性而不是C特性C++比起c語(yǔ)言更加類型安全,更加抽象。我們更推薦使用C++的語(yǔ)言特性來(lái)編程,比如使用string而不是char*,使用vector而不是原生數(shù)組,使用namespace而不是statico通用命名常見(jiàn)命名風(fēng)格有:駝峰風(fēng)格(CamelCase)大小寫(xiě)字母混用,單詞連在一起,不同單詞間通過(guò)單詞首字母大寫(xiě)來(lái)分開(kāi)。按連接后的首字母是否大寫(xiě),又分:大駝峰(UperCamelCase)和小駝峰(lowerCamelCase)內(nèi)核風(fēng)格(unix」ike)單詞全小寫(xiě),用下劃線分割?如:'test_result'匈牙利風(fēng)格在‘大駝峰’的基礎(chǔ)上,加上前綴;前綴用于表達(dá)類型或用途。如:^iSavedCount",^Tested,規(guī)則2.1.1標(biāo)識(shí)符命名使用駝峰風(fēng)格命名風(fēng)格大駝峰大駝峰(接口部分可加前綴,如命名風(fēng)格大駝峰大駝峰(接口部分可加前綴,如XXX一函數(shù)名)小駝峰k+大小寫(xiě)混合大寫(xiě)+下劃線全小寫(xiě)類型類類型,結(jié)構(gòu)體類型,枚舉類型,聯(lián)合體類型等類型定義函數(shù)(包括全局函數(shù),作用域函數(shù),成員函數(shù))全局變量(包括全局和命名空間域下的變量,類靜態(tài)變量),局部變量,函數(shù)參數(shù),類、結(jié)構(gòu)體和聯(lián)合體中的成員變量常量(const),枚舉值宏命名空間注意:上表中—常量—是指全局作用域、namespace域、類的靜態(tài)成員域下,以const或constexpr修飾的基本數(shù)據(jù)類型、枚舉、字符串類型的變量。上表中_變量—是指除常量定義以外的其他變量,均使用小駝峰風(fēng)格。文件命名建議2.2.1C++文件以.cpp結(jié)尾,頭文件以.h結(jié)尾我們推薦使用.h作為頭文件的后綴,這樣頭文件可以直接兼容C和C++。我們推薦使用.cpp作為實(shí)現(xiàn)文件的后綴,這樣可以直接區(qū)分C++代碼,而不是C代碼。目前業(yè)界還有一些其他的后綴的表示方法:頭文件:.hh,.hpp,.hxxcpp文件:.cc,.cxx,.C對(duì)于本文檔,我們默認(rèn)使用.h和.cpp作為后綴。建議2.2.2C++文件名和類名保持一致C++的頭文件和cpp文件名和類名保持一致,使用下劃線小寫(xiě)風(fēng)格。如下:-database_connection.h-database_connection.cpp結(jié)構(gòu)體,命名空間,枚舉等定義的文件名類似。函數(shù)命名函數(shù)命名統(tǒng)一使用大駝峰風(fēng)格,一般采用動(dòng)詞或者動(dòng)賓結(jié)構(gòu)。接口部分可加前綴,如XXX一函數(shù)名。classList{public:voidAddElement(constElementselement);ElementGetElement(constunsignedintindex)const;boolIsEmpty()const;boolMCC_GetClass(););namespaceutils{voidDeletellser();}類型命名類型命名采用大駝峰命名風(fēng)格。所有類型命名一一類、結(jié)構(gòu)體、聯(lián)合體、類型定義(typedef)、枚舉 使用相同約定,例如://cLasseSjstructsandunionsclassUrlTable{?,?classUrlTableTester{...structUrlTableProperties{???unionPacket{???//typedefstypedefstd::map<std:zstring^UrlTableProperties*>PropertiesMap;//enumsenumUrlTableErrors{...對(duì)于命名空間的命名,建議全小寫(xiě)://namespacenamespaceosutils{namespacefileutils{}}建議2.4.1避免濫用typedef或者#define對(duì)基本類型起別名除有明確的必要性,否則不要用typedef/#define對(duì)基本數(shù)據(jù)類型進(jìn)行重定義。優(yōu)先使用<cstdint>頭文件中的基本類型:有符號(hào)類型無(wú)符號(hào)類型描述int8_tuint8_t寬度恰為8的有/無(wú)符號(hào)整數(shù)類型intl6_tuintl6_t寬度恰為16的有/無(wú)符號(hào)整數(shù)類型int32_tuint32_t寬度恰為32的有/無(wú)符號(hào)整數(shù)類型int64_tuint64_t寬度恰為64的有/無(wú)符號(hào)整數(shù)類型intptr_tuintptr_t足以保存指針的有/無(wú)符號(hào)整數(shù)類型如果模塊有自己的定義,請(qǐng)使用統(tǒng)一的typedef來(lái)定義類型:typedefsignedcharVOS_INT8;typedefunsignedcharVOS_U工NT8;#if_WORDSIZE==64typedefunsignedlongintVOS_UINTPTR;#elsetypedefunsignedint VOS_UINTPTR;#endif如果模塊為了封裝某個(gè)類型的信息,方便后續(xù)的擴(kuò)展,可以使用typedef來(lái)重新定義。typedefuint8_tDevicelD;//...//若干版本后擴(kuò)展成16-bittypedefuintl6_tDevicelD;有特殊作用的類型typedefvoid*Handle;注意:不要使用#define進(jìn)行別名定義,并且在C++11以后推薦使用using來(lái)定義類型。除上述理由外,應(yīng)避免給其本數(shù)值類型別名定義。因?yàn)轭愋蛣e名可讀性并不好,隱藏了基本數(shù)值類型信息,如位寬,是否帶符號(hào)。濫用舉例:typedefuintl6_tMyCounter;〃.…intFoo(???){MyCounterc;〃---while(c>=0){printf("counter=%d\n'\C);//...)//...)對(duì),MyCounter,是否可能小于0,打印時(shí)用,%d,還是,%u,都不是很直觀,極容易引入上述類似缺陷。變量命名通用變量命名采用小駝峰,包括全局變量,函數(shù)形參,局部變量,成員變量。std::stringtableName;//Good:推薦此風(fēng)格std::stringtablename;//Bad:禁上此風(fēng)格std::stringpath;//Good:只仃?個(gè)中.詞時(shí),小,花嶺為的小寫(xiě)規(guī)則251類的成員變量命名使用小駝峰。classFoo{private:std::stringfileName;//不潦加任何作用域前綴或者后綴};當(dāng)構(gòu)造函數(shù)參數(shù)和成員變量重名時(shí),可通過(guò)this,來(lái)引用成員變量。classMyClass{public:MyClass(intmyVar):myVar(myVar){//OK,初始化列表允許同%人參初始化同名成員if(NeedNewVar()){this->myVar=GetValue();//';匕起不要漏掠.否則就成「給人醫(yī)賦值})private:intmyVar;};宏、常量、枚舉命名宏采用全大寫(xiě),下劃線連接的格式。常量、枚舉值使用k+大小寫(xiě)混合。函數(shù)局部const常量和類的普通const成員變量,使用小駝峰命名風(fēng)格。ftdefineMAX(a,b)(((a)<(b))?(b):(a))//僅對(duì)宏命冬舉例,并不推薦用宏實(shí)現(xiàn)此類功能enumTintColor{ //注意,枚舉類型啟用大駝峰,其下面的取值是大小7混合kRedjkDarkRedjkGreen,kLightGreen};intFunc(...){constunsignedintbuffersize=100; //函數(shù)局部常/char*p=newchar[bufferSize];}.namespaceutils{constunsignedintkFileSize=200; //企局常"止)3格式盡管有些編程的排版風(fēng)格因人而異,但是我們強(qiáng)烈建議和要求使用統(tǒng)一的編碼風(fēng)格,以便所有人都能夠輕松的閱讀和理解代碼,增強(qiáng)代碼的可維護(hù)性。行寬建議3.1.1行寬不超過(guò)120個(gè)字符建議每行字符數(shù)不要超過(guò)120個(gè)。如果超過(guò)120個(gè)字符,請(qǐng)選擇合理的方式進(jìn)行換行。例外:?如果一行注釋包含了超過(guò)120個(gè)字符的命令或URL,則可以保持一行,以方便復(fù)制、粘貼和通過(guò)grep查找;?包含長(zhǎng)路徑的include語(yǔ)句可以超出120個(gè)字符,但是也需要盡量避免;?編譯預(yù)處理中的error信息可以超出一行。預(yù)處理的error信息在一行便于閱讀和理解,即使超過(guò)120個(gè)字符。#ifndefXXX_YYY_ZZZ#errorHeaderaaaa/bbbb/cccc/abc.hmustonlybeincludedafterxxxx/yyyy/zzzz/xyz.h,becausexxxxxxxxxxxxxxxxxxxxxxxxxxxxx#endif縮進(jìn)規(guī)則3.2.1使用空格進(jìn)行縮進(jìn),每次縮進(jìn)2個(gè)空格只允許使用空格(space)進(jìn)行縮進(jìn),每次縮進(jìn)為2個(gè)空格。大括號(hào)規(guī)則3.3.1除函數(shù)外,使用K&R縮進(jìn)風(fēng)格函數(shù)左大括號(hào)跟隨語(yǔ)句放行末。右大括號(hào)獨(dú)占一行,除非后面跟著同一語(yǔ)句的剩余部分,如do語(yǔ)句中的while,或者if語(yǔ)句的else/elseif,或者逗號(hào)、分號(hào)。如:structMyType{ //跟隨語(yǔ)?句放行末.前置1空格}「intFoo(inta){ //函數(shù)左人括號(hào)跟隨語(yǔ)句放行木if(...){}else{}')推薦這種風(fēng)格的理由:代碼更緊湊;相比另起一行,放行末使代碼閱讀節(jié)奏感上更連續(xù);符合后來(lái)語(yǔ)言的習(xí)慣,符合業(yè)界主流習(xí)慣;現(xiàn)代集成開(kāi)發(fā)環(huán)境(IDE)都具有代碼縮進(jìn)對(duì)齊顯示的輔助功能,大括號(hào)放在行尾并不會(huì)對(duì)縮進(jìn)和范圍產(chǎn)生理解上的影響。對(duì)于空函數(shù)體,可以將大括號(hào)放在同一行:classMyClass{public:MyClass():value(0){}private:intvalue;};函數(shù)聲明和定義規(guī)則341函數(shù)聲明和定義的返回類型和函數(shù)名在同一行;函數(shù)參數(shù)列表超出行寬時(shí)要換行并合理對(duì)齊在聲明和定義函數(shù)的時(shí)候,函數(shù)的返回值類型應(yīng)該和函數(shù)名在同一行;如果行寬度允許,函數(shù)參數(shù)也應(yīng)該放在一行;否則,函數(shù)參數(shù)應(yīng)該換行,并進(jìn)行合理對(duì)齊。參數(shù)列表的左圓括號(hào)總是和函數(shù)名在同一行,不要單獨(dú)一行;右圓括號(hào)總是跟隨最后一個(gè)參數(shù)。換行舉例:ReturnTypeFunctionName(ArgTypeparamNamel,ArgTypeparamName2){//Good:全港同一行}.ReturnTypeVeryVeryVeryLongFunctionName(ArgTypeparamNamel^不滿足所有參數(shù),進(jìn)行換行ArgTypeparamNameZ^//Good:和上一行參數(shù)對(duì)齊ArgTypeparamName3){}…ReturnTypeLongFunctionName(ArgTypeparamNamel,ArgTypeparamName2,//行寬限制,進(jìn)行換行ArgTypeparamName3,ArgTypeparamName4,ArgTypeparamName5){/Good:換行后4空格縮進(jìn)}.ReturnTypeReallyReallyReallyReallyLongFunctionName(不滿足第1個(gè)參數(shù),直接換行ArgTypeparamNamel,ArgTypeparamName2,ArgTypeparamName3){//Good:換行房4空格縮進(jìn)函數(shù)調(diào)用規(guī)則3.5.1函數(shù)調(diào)用入?yún)⒘斜響?yīng)放在一行,超出行寬換行時(shí),保持參數(shù)進(jìn)行合理對(duì)齊函數(shù)調(diào)用時(shí),函數(shù)參數(shù)列表放在一行。參數(shù)列表如果超過(guò)行寬,需要換行并進(jìn)行合理的參數(shù)對(duì)齊。左圓括號(hào)總是跟函數(shù)名,右圓括號(hào)總是跟最后一個(gè)參數(shù)。換行舉例:ReturnTyperesult=FunctionNameCparamNamel,paramName2);//Good:函數(shù)參數(shù)放在一行ReturnTyperesult=FunctionName(paramNameljparamName2, //Good:保持與上方參數(shù)對(duì)齊paramName3);ReturnTyperesult=FunctionName(paramNamel,paramName2,paramName3,paramName4,paramNameS); //Good:參數(shù)換行,4空格縮進(jìn)ReturnTyperesult=VeryVeryVeryLongFunctionName( //行寬不滿足第1個(gè)參數(shù),直接換行paramNamel,paramName2,paramName3); //換行后,4空格縮進(jìn)如果函數(shù)調(diào)用的參數(shù)存在內(nèi)在關(guān)聯(lián)性,按照可理解性優(yōu)先于格式排版要求,對(duì)參數(shù)進(jìn)行合理分組換行。//Good:每行的參數(shù)代表一組相關(guān)性較強(qiáng)的數(shù)據(jù)結(jié)構(gòu).放在一行便于理解intresult=DealWithStructureLikeParams(left.x,left.y,//表,-相關(guān)參數(shù)right.x,right.y);//我示另外一組相關(guān)參數(shù)if語(yǔ)句規(guī)則3.6.1if語(yǔ)句必須要使用大括號(hào)我們要求if語(yǔ)句都需要使用大括號(hào),即便只有一條語(yǔ)句。理由:-代碼邏輯直觀,易讀;-在已有條件語(yǔ)句代碼上增加新代碼時(shí)不容易出錯(cuò);-對(duì)于在if語(yǔ)句中使用函數(shù)式宏時(shí),有大括號(hào)保護(hù)不易出錯(cuò)(如果宏定義時(shí)遺漏了大括號(hào))。if(objectlsNotExist){ //Good:?jiǎn)?仃條件語(yǔ)?句也加大括號(hào)returnCreateNewObject();)規(guī)則3.6.2禁止if/else/elseif寫(xiě)在同一行條件語(yǔ)句中,若有多個(gè)分支,應(yīng)該寫(xiě)在不同行。如下是正確的寫(xiě)法:if(someConditions){DoSomething();}else{//Good:eLse與if在不同行}'下面是不符合規(guī)范的案例:if(someConditions){.??}else{.?.}//Bad:eLse與if在同一行循環(huán)語(yǔ)句規(guī)則3.7.1循環(huán)語(yǔ)句要求使用大括號(hào)和if語(yǔ)句類似,我們要求for/while循環(huán)語(yǔ)句必須加上的大括號(hào),即使循環(huán)體是空的,或者循環(huán)語(yǔ)句只有一條。for(inti=0;i<someRange;i++){DoSomething();)如果循環(huán)體是空的,應(yīng)該使用空的大括號(hào),而不是使用單個(gè)分號(hào)。單個(gè)分號(hào)容易被遺漏,也容易被誤認(rèn)為是循環(huán)語(yǔ)句中的一部分。for(inti=0;i<someRange;i++){}//Good:for循環(huán)體是"E,使用大括號(hào),而不是使用分號(hào)while(someCondition){}//Good:whiLe循環(huán)體是個(gè):,使用大括號(hào),而不是使用分號(hào)while(someCondition){continue;//Good:continue太小個(gè)邏輯,可以使用大括號(hào)-也可以不使用}壞的例子:for(inti=0;i<someRange;i++); //Bad:for循環(huán)體是個(gè).也不要乂使用分號(hào),要使用大括號(hào)while(someCondition);//Bad:使用分3容易讓人誤解是whtlu語(yǔ)句中的一部分switch語(yǔ)句規(guī)則3.8.1switch語(yǔ)句的case/default要縮進(jìn)一層switch語(yǔ)句的縮進(jìn)風(fēng)格如下:switch(van){case0: //Good:縮進(jìn)DoSomethingl();//Good:縮進(jìn)break;case1:{ //Good:帶大括號(hào)格式DoSomething2();break;)default:break;)switch(van){case0: //Bad:case未縮進(jìn)DoSomething();break;default: //Bad:defauLt未縮進(jìn)break;)表達(dá)式建議391表達(dá)式換行要保持換行的一致性,運(yùn)算符放行末較長(zhǎng)的表達(dá)式,不滿足行寬要求的時(shí)候,需要在適當(dāng)?shù)牡胤綋Q行。一般在較低優(yōu)先級(jí)運(yùn)算符或連接符后面截?cái)?,運(yùn)算符或連接符放在行末。運(yùn)算符、連接符放在行末,表示"未結(jié)束,后續(xù)還有"。例://假設(shè)下面第一行已經(jīng)不滿足行寬要求if(currentvalue>threshold&&//Good:換行后,邏鉗操作汾放住行尾someConditionsion){DoSomething();}'intresult=reallyReallyLongVariableNamel+ //GoodreallyReallyLongVariableName2;表達(dá)式換行后,注意保持合理對(duì)齊,或者4空格縮進(jìn)。參考下面例子intsum=longVaribleNamel+longVaribleName2+longVaribleName3+longVaribleName4+longVaribleNameS+longVaribleName6; //Good:4空格縮進(jìn)intsum=longVaribleNamel+longVaribleName2+longVaribleName3+longVaribleName4+longVaribleNameS+longVaribleNameS;//Good:保持對(duì)齊變量賦值規(guī)則3.10.1多個(gè)變量定義和賦值語(yǔ)句不允許寫(xiě)在一行每行只有一個(gè)變量初始化的語(yǔ)句,更容易閱讀和理解。intmaxCount=10;boolisCompleted=false;下面是不符合規(guī)范的示例:intmaxCount=10;boolisCompleted=false;//Bad: ''公址初始化7,開(kāi)放在多行,每行一個(gè)變量初始化intx,y=0;//Bad:多個(gè)變量定義需要分行,每行一個(gè)intpointX;intpointY;pointX=1;pointY=2;//Bad:多個(gè)變賦值語(yǔ)旬放同一行例外:for循環(huán)頭、if初始化語(yǔ)句(C++17)、結(jié)構(gòu)化綁定語(yǔ)句(C++17)中可以聲明和初始化多個(gè)變量。這些語(yǔ)句中的多個(gè)變量聲明有較強(qiáng)關(guān)聯(lián),如果強(qiáng)行分成多行會(huì)帶來(lái)作用域不一致,聲明和初始化割裂等問(wèn)題。初始化初始化包括結(jié)構(gòu)體、聯(lián)合體、及數(shù)組的初始化規(guī)則3.11.1初始化換行時(shí)要有縮進(jìn),并進(jìn)行合理對(duì)齊結(jié)構(gòu)體或數(shù)組初始化時(shí),如果換行應(yīng)保持4空格縮進(jìn)。從可讀性角度出發(fā),選擇換行點(diǎn)和對(duì)齊位置。constintrank[]={16,16,16,16,32,32,32,32,64,64,64,64,32,32,32,32};指針與引用建議3.12.1指針類型“*”跟隨變量名或者類型,不要兩邊都留有或者都沒(méi)有空格指針命名:*靠左靠右都可以,但是不要兩邊都有或者都沒(méi)有空格。int*p=NULL;//Goodint*p=NULL;//Goodint*p=NULL;//Badint*p=NULL;//Bad例外:當(dāng)變量被const修飾時(shí),〃*”無(wú)法跟隨變量,此時(shí)也不要跟隨類型。char*constVERSION="V100";建議3.12.2引用類型〃&〃跟隨變量名或者類型,不要兩邊都留有或者都沒(méi)有空格引用命名:&靠左靠右都可以,但是不要兩邊都有或者都沒(méi)有空格。inti=8;int&p=i; //Goodint&p=i; //Goodint&p=i;//Badint&p=i;//Bad編譯預(yù)處理規(guī)則3.13.1編譯預(yù)處理的"#"統(tǒng)一放在行首,嵌套編譯預(yù)處理語(yǔ)句時(shí),"*’不縮進(jìn)編譯預(yù)處理的"#"統(tǒng)一放在行首,即使編譯預(yù)處理的代碼是嵌入在函數(shù)體中的,"#"也應(yīng)該放在行首。#ifdefined(_x86_64_)&&defined(_GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)//Good: 放茬行皆#defineATOMIC_X86_HAS_CMPXCHG16B1//Good: 放在行首#else#defineATOMIC__X86_HAS_CMPXCHG16B0#endifintFunctionName(){if(someThingError){#ifdefHAS_SYSLOG //Good:即便在函數(shù)內(nèi)部,也放在行首WriteToSysLog();#elseWriteToFileLog();#endif}}內(nèi)嵌的預(yù)處理語(yǔ)句"#"不縮進(jìn)#ifdefined(_x86_64_)&&defined(_GCC_HAVE_SYNC_C0MPARE_AND_SWAP_16)ftdefineATOMIC_X86_HAS_CMPXCHG16B1//Good:區(qū)分層次,便于閱讀#else#defineATOMIC__X86_HAS_CMPXCHG16B0#endif空格和空行建議3.14.1水平空格應(yīng)該突出關(guān)鍵字和重要信息,避免不必要的留白水平空格應(yīng)該突出關(guān)鍵字和重要信息,每行代碼尾部不要加空格??傮w規(guī)則如下:if,switch,case,do,while,for等關(guān)鍵字之后加空格;小括號(hào)內(nèi)部的兩側(cè),不要加空格;大括號(hào)內(nèi)部?jī)蓚?cè)有無(wú)空格,左右必須保持一致;一元操作符(&*+--!)之后不要加空格;二元操作符(=+-<>*/%I&A<=>===!=)左右兩側(cè)加空格三目運(yùn)算符(?:)符號(hào)兩側(cè)均需要空格前置和后置的自增、自減(++-)和變量之間不加空格結(jié)構(gòu)體成員操作符(.->)前后不加空格逗號(hào)。前面不加空格,后面增加空格對(duì)于模板和類型轉(zhuǎn)換(<>)和類型之間不要添加空格域操作符(::)前后不要添加空格冒號(hào)(:)前后根據(jù)情況來(lái)判斷是否要添加空格常規(guī)情況:voidFoo(intb){//Good:大括號(hào)前應(yīng)該留“X格inti=0;//Good:變埴初始化時(shí),=前儲(chǔ)應(yīng)該有空格,分心前而不要招個(gè):格intbuf[kBufSize]={0}; //Good:大括號(hào)內(nèi)兩側(cè)都無(wú)空格函數(shù)定義和函數(shù)調(diào)用:intresult=Foo(argl,arg2);A//Bad:逗號(hào)后HH需哽增加空格intresult=Foo(argl,arg2);A 八//Bad:函數(shù)與數(shù)列發(fā)的左括號(hào)后面不應(yīng)該行個(gè)格,右括號(hào)前面不應(yīng)該有空格指針和取地址x=*p; //Good:*操作符和指針p之間不加空稱p=&x; //Good:&操作符和變獻(xiàn)x之間不加安格x=r.y;//Good:通過(guò).訪問(wèn)成員變吊;時(shí)不加空格x=r->y;//Good:通過(guò)->訪問(wèn)成員變量:時(shí)不加空格操作符:x=0; //Good:賦值操作的=前后都要加々格x=-5; //Good:負(fù)數(shù)的符—值之前不要加空格++X; //Good:的置和后置的++/--和變;1;二之間不要加格if(x&&!y)//Good:布爾操作符前后要加上空格.!操作和變量之間不要空格v=w*x+y/z;//Good:二元操作符前儲(chǔ)要加空格v=w*(x+z); //Good:括號(hào)內(nèi)的衣達(dá)式前后不需要加“。格inta=(x<y)?x:y;//Good:rH運(yùn)算符,?和:前腦需要添加審格循環(huán)和條件語(yǔ)句:if(condition){//Good:if關(guān)鍵不和括號(hào)之間加空格,括號(hào)內(nèi)條件語(yǔ)句前腦不加空格}else{ //Good:eLse關(guān)鍵字和大括號(hào)之間加審格while(condition){} //Good:whiLe關(guān)鍵字和括號(hào)之間加々格,括號(hào)內(nèi)條件語(yǔ)句前后不加空格for(inti=0;i<someRange;++i){//Good:for關(guān)鍵'丁和括號(hào)之間加空格.分號(hào)之后加空格}一switch(condition){//Good:switch關(guān)犍:后面行1個(gè)格case0: //Good:case語(yǔ)句條件和H號(hào)之間不加個(gè):格break;default:break;)模板和轉(zhuǎn)換//尖括號(hào)(<and>)不與空格緊鄰,<前沒(méi)有空格,>和(之間也沒(méi)有.vector<string>x;y=static_cast<char*>(x);//在類型與指針操作符之間留空格也可以,但要保持一致.vector<char*>x;域操作符std::cout;//Good:命%i個(gè):問(wèn)訪問(wèn),41更:界”。格intMyClass::GetValue()const{}//Good:〈寸「成員函數(shù)定義,不變部"、格冒號(hào)//添加空格的場(chǎng)景//Good:類的派生需要留有空格classSub:publicBase{};//構(gòu)造函數(shù)初始化列表需要留有空格MyClass::MyClass(intvar):someVar(var){DoSomething();}一//位域表示也留有空格structXX{chara:4;charb:5;charc:4;};//不添加空格的場(chǎng)景//Good:對(duì)于pubLic:jprivate:這種類訪問(wèn)權(quán)限的冒號(hào)不用添加空格classMyClass{public:MyClass(intvar);private:intsomeVar;};//對(duì)于switch-case的case和defQuit后面的冒號(hào)不用添加空格switch(value){case1:DoSomething();break;default:break;)注意:當(dāng)前的集成開(kāi)發(fā)環(huán)境(IDE)可以設(shè)置刪除行尾的空格,請(qǐng)正確配置。建議3.14.2合理安排空行,保持代碼緊湊減少不必要的空行,可以顯示更多的代碼,方便代碼閱讀。下面有一些建議遵守的規(guī)則:-根據(jù)上下內(nèi)容的相關(guān)程度,合理安排空行;-函數(shù)內(nèi)部、類型定義內(nèi)部、宏內(nèi)部、初始化表達(dá)式內(nèi)部,不使用連續(xù)空行-不使用連續(xù)3個(gè)空行,或更多-大括號(hào)內(nèi)的代碼塊行首之前和行尾之后不要加空行。intFoo(){)//Bad:兩個(gè)函數(shù)定義間超過(guò)了一個(gè)空行intBar(){}…if(...){//Bad:大括號(hào)內(nèi)的代碼塊行首不要加入空行" //Bad:大括號(hào)內(nèi)的代碼塊行尾不要加入空行}intFoo(...){//Bad:函數(shù)體內(nèi)行首不要加空行類規(guī)則3.15.1類訪問(wèn)控制塊的聲明依次序是public:,protected:,private:,每個(gè)都縮進(jìn)1個(gè)空格classMyClass:publicBaseClass{public://注意沒(méi)有縮進(jìn)MyClass();//標(biāo)準(zhǔn)的4空格縮進(jìn)explicitMyClass(intvar);~MyClass(){}voidSomeFunction();voidSomeFunctionThatDoesNothing(){)一voidSetVar(intvar){someVar=var;}intGetVar()const{returnsomeVar;}private:boolSomelnternalFunction();intsomeVar;intsomeOtherVar;};在各個(gè)部分中,建議將類似的聲明放在一起,并且建議以如下的順序:類型(包括typedef,using和嵌套的結(jié)構(gòu)體與類),常量,工廠函數(shù),構(gòu)造函數(shù),賦值運(yùn)算符,析構(gòu)函數(shù),其它成員函數(shù),數(shù)據(jù)成員。規(guī)則3.15.2構(gòu)造函數(shù)初始化列表放在同一行或按四格縮進(jìn)并排多行//加果所有變量能放在同一行:MyClass::MyClass(intvar):someVar(var){DoSomething();}」//如果不能放在同一行,//必須置于冒號(hào)后,并縮進(jìn)4個(gè)空格MyClass::MyClass(intvar):someVar(var),someOtherVar(var+1){//Good:逗號(hào)后面留行空格DoSomething();)一//如果初始化列表需要置于多行》需要逐行對(duì)齊MyClass::MyClass(intvar):someVar(var), //縮進(jìn)4個(gè)空格someOtherVar(var+1){DoSomething();)4注釋一般的,盡量通過(guò)清晰的架構(gòu)邏輯,好的符號(hào)命名來(lái)提高代碼可讀性;需要的時(shí)候,才輔以注釋說(shuō)明。注釋是為了幫助閱讀者快速讀懂代碼,所以要從讀者的角度出發(fā),按需注釋“注釋內(nèi)容要簡(jiǎn)潔、明了、無(wú)二義性,信息全面且不冗余。注釋跟代碼一樣重要。寫(xiě)注釋時(shí)要換位思考,用注釋去表達(dá)此時(shí)讀者真正需要的信息。在代碼的功能、意圖層次上進(jìn)行注釋,即注釋解釋代碼難以表達(dá)的意圖,不要重復(fù)代碼信息。修改代碼時(shí),也要保證其相關(guān)注釋的一致性。只改代碼,不改注釋是一種不文明行為,破壞了代碼與注釋的一致性,讓閱讀者迷惑、費(fèi)解,甚至誤解。注釋風(fēng)格在C++代碼中,使用/**/和〃都是可以的。按注釋的目的和位置,注釋可分為不同的類型,如文件頭注釋、函數(shù)頭注釋、代碼注釋等等;同一類型的注釋?xiě)?yīng)該保持統(tǒng)一的風(fēng)格。注意:本文示例代碼中,大量使用‘〃'后置注釋只是為了更精確的描述問(wèn)題,并不代表這種注釋風(fēng)格更好。文件頭注釋規(guī)則4.2.1文件頭注釋必須包含版權(quán)許可/*Copyright(c)[2019][nameofcopyrighthoLder][SoftwareName]isLicensedundertheMuLanPSLvl.YoucanusethissoftwareaccordingtothetermsandconditionsoftheMuLanPSLvl.YoumayobtainacopyofMuLanPSLvlat:http://License.coscL./MuLanPSLTHISSOFTWAREISPROVIDEDONAN"ASIS11BASIS,WITHOUTWARRANTIESOFANYKIND,EITHEREXPRESSORIMPLIED,INCLUDINGBUTNOTLIMITEDTONON-INFRINGEMENT,MERCHANTABILITYORFITFORAPARTICULARPURPOSE.SeetheMuLanPSLvlformoredetaiLs.V函數(shù)頭注釋規(guī)則4.3.1禁止空有格式的函數(shù)頭注釋并不是所有的函數(shù)都需要函數(shù)頭注釋;函數(shù)簽名無(wú)法表達(dá)的信息、,加函數(shù)頭注釋輔助說(shuō)明;函數(shù)頭注釋統(tǒng)一放在函數(shù)聲明或定義上方,使用如下風(fēng)格之一:使用〃寫(xiě)函數(shù)頭//單行函數(shù)頭intFuncl(void);//多行函數(shù)頭//第二行intFunc2(void);使用/* */寫(xiě)函數(shù)頭/*單行函數(shù)頭*/intFuncl(void);/*另一種單行函數(shù)頭/intFunc2(void);多行函數(shù)頭第二行/intFunc3(void);函數(shù)盡量通過(guò)函數(shù)名自注釋,按需寫(xiě)函數(shù)頭注釋。不要寫(xiě)無(wú)用、信息冗余的函數(shù)頭;不要寫(xiě)空有格式的函數(shù)頭。函數(shù)頭注釋內(nèi)容可選,但不限于:功能說(shuō)明、返回值,性能約束、用法、內(nèi)存約定、算法實(shí)現(xiàn)、可重入的要求等等。模塊對(duì)外頭文件中的函數(shù)接口聲明,其函數(shù)頭注釋,應(yīng)當(dāng)將重要、有用的信息表達(dá)清楚。例:返回實(shí)際寫(xiě)入的字節(jié)數(shù),-1表示寫(xiě)入失敗注意,內(nèi)存buf由調(diào)用者負(fù)責(zé)釋放/intWriteString(constchar*buf,intlen);壞的例子:函數(shù)名:MriteString功能:寫(xiě)入字符串參數(shù):返回值:/intWriteString(constchar*buf>intlen);上面例子中的問(wèn)題:參數(shù)、返回值,空有格式?jīng)]內(nèi)容函數(shù)名信息冗余關(guān)鍵的buf由誰(shuí)釋放沒(méi)有說(shuō)清楚代碼注釋規(guī)則441代碼注釋放于對(duì)應(yīng)代碼的上方或右邊規(guī)則4.4.2注釋符與注釋內(nèi)容間要有1空格;右置注釋與前面代碼至少1空格代碼上方的注釋,應(yīng)該保持對(duì)應(yīng)代碼一樣的縮進(jìn)。選擇并統(tǒng)一使用如下風(fēng)格之一:使用〃//這是單行注釋DoSomething();//這是多行注釋//第二行DoSomething();使用/*''*//*這是單行注釋*/DoSomething();/*另一種方式的多行注釋第二行/DoSomething();代碼右邊的注釋,與代碼之間,至少留1空格,建議不超過(guò)4空格。通常使用擴(kuò)展后的TAB鍵即可實(shí)現(xiàn)1-4空格的縮進(jìn)。選擇并統(tǒng)一使用如下風(fēng)格之一:intfoo=100;//放心邊的「intbar=200;/*放。邊的注釋*/右置格式在適當(dāng)?shù)臅r(shí)候,上下對(duì)齊會(huì)更美觀。對(duì)齊后的注釋,離左邊代碼最近的那一行,保證1-4空格的間隔。例:constintkConst=100; /*相關(guān)的同類注釋,可以考慮/:卜一對(duì)齊*/constintkAnotherConst=200; /*_L:卜對(duì)齊時(shí),'j左側(cè)代碼保持間隔*/當(dāng)右置的注釋超過(guò)行寬時(shí),請(qǐng)考慮將注釋置于代碼上方。規(guī)則4.4.3不用的代碼段直接刪除,不要注釋掉被注釋掉的代碼,無(wú)法被正常維護(hù);當(dāng)企圖恢復(fù)使用這段代碼時(shí),極有可能引入易被忽略的缺陷。正確的做法是,不需要的代碼直接刪除掉。若再需要時(shí),考慮移植或重寫(xiě)這段代碼。這里說(shuō)的注釋掉代碼,包括用/**/和〃,還包括#ifO,#ifdefNEVER_DEFlNED等等。建議4.4.1正式交付給客戶的代碼不能包含TODO/TBD/FIXME注釋TODO/TBD注釋一般用來(lái)描述已知待改進(jìn)、待補(bǔ)充的修改點(diǎn)FIXME注釋一般用來(lái)描述已知缺陷它們都應(yīng)該有統(tǒng)一風(fēng)格,方便文本搜索統(tǒng)一處理。如://TODOf<author-name>):補(bǔ)充XX處理//FIXME:XX缺陷5頭文件頭文件職責(zé)頭文件是模塊或文件的對(duì)外接口,頭文件的設(shè)計(jì)體現(xiàn)了大部分的系統(tǒng)設(shè)計(jì)。頭文件中適合放置接口的聲明,不適合放置實(shí)現(xiàn)(內(nèi)聯(lián)函數(shù)除外)。對(duì)于cpp文件中內(nèi)部才需要使用的函數(shù)、宏、枚舉、結(jié)構(gòu)定義等不要放在頭文件中。頭文件應(yīng)當(dāng)職責(zé)單一。頭文件過(guò)于復(fù)雜,依賴過(guò)于復(fù)雜還是導(dǎo)致編譯時(shí)間過(guò)長(zhǎng)的主要原因。建議5.1.1每一個(gè).cpp文件應(yīng)有一個(gè)對(duì)應(yīng)的.h文件,用于聲明需要對(duì)外公開(kāi)的類與接口通常情況下,每個(gè).cpp文件都有一個(gè)相應(yīng)的.h,用于放置對(duì)外提供的函數(shù)聲明、宏定義、類型定義等。如果一個(gè).cpp文件不需要對(duì)外公布任何接口,則其就不應(yīng)當(dāng)存在。例外:程序的入口(如main函數(shù)所在的文件),單元測(cè)試代碼,動(dòng)態(tài)庫(kù)代碼。示例://Foo.h#ifndefFOO_H#defineFOO二HclassFoo{public:Foo();voidFun();private:intvalue;};#endif//Foo.cpp#include"Foo.h"namespace{//Good:對(duì)內(nèi)函數(shù)的市明放在.cpp文件的頭部,并聲明為匿名namespace或若static限制其作用域voidBar(){)}voidFoo::Fun(){Bar();)頭文件依賴規(guī)則5.2.1禁止頭文件循環(huán)依賴頭文件循環(huán)依賴,指a.h包含b.h,b.h包含c.h,c.h包含a.h,導(dǎo)致任何一個(gè)頭文件修改,都導(dǎo)致所有包含了a.h/b.h/c.h的代碼全部重新編譯一遍。而如果是單向依賴,如a.h包谷b.h,b.h包含c.h,而c.h不包含任何頭文件,則修改a.h不會(huì)導(dǎo)致包含了b.h/c.h的源代碼重新編譯。頭文件循環(huán)依賴直接體現(xiàn)了架構(gòu)設(shè)計(jì)上的不合理,可通過(guò)優(yōu)化架構(gòu)去避免。規(guī)則5.2.2禁止包含用不到的頭文件用不到的頭文件被包含的同時(shí)引入了不必要的依賴,增加了模塊或單元之間的耦合度,只要該頭文件被修改,代碼就要重新編譯。很多系統(tǒng)中頭文件包含關(guān)系復(fù)雜,開(kāi)發(fā)人員為了省事起見(jiàn),直接包含一切想到的頭文件,甚至發(fā)布了一個(gè)god.h,其中包含了所有頭文件,然后發(fā)布給各個(gè)項(xiàng)目組使用,這種只圖一時(shí)省事的做法,導(dǎo)致整個(gè)系統(tǒng)的編譯時(shí)間進(jìn)一步惡化,并對(duì)后來(lái)人的維護(hù)造成了巨大的麻煩。規(guī)則5.2.3頭文件應(yīng)當(dāng)自包含簡(jiǎn)單的說(shuō),自包含就是任意一個(gè)頭文件均可獨(dú)立編譯。如果一個(gè)文件包含某個(gè)頭文件,還要包含另外一個(gè)頭文件才能工作的話,給這個(gè)頭文件的用戶增添不必要的負(fù)擔(dān)。示例:如果a.h不是自包含的,需要包含b.h才能編譯,會(huì)帶來(lái)的危害:每個(gè)使用a.h頭文件的.cpp文件,為了讓引入的a.h的內(nèi)容編譯通過(guò),都要包含額外的頭文件b.ho額外的頭文件b.h必須在a.h之前進(jìn)行包含,這在包含順序上產(chǎn)生了依賴。規(guī)則524頭文件必須編寫(xiě)#define保護(hù),防止重復(fù)包含為防止頭文件被重復(fù)包含,所有頭文件都應(yīng)當(dāng)使用#define保護(hù);不要使用#pragmaonce定義包含保護(hù)符時(shí),應(yīng)該遵守如下規(guī)則:1)保護(hù)符使用唯一名稱;2)不要在受保護(hù)部分的前后放置代碼或者注釋,文件頭注釋除外。示例:假定VOS工程的timer模塊的timer.h,其目錄為VOS/include/timer/Timer.h,應(yīng)按如下方式保護(hù):#ifndefVOS_INCLUDE_TIMERETIMER_H#defineVOS_INCLUDE_TIMER_TIMER__H#endif也可以不用像上面添加路徑,但是要保證當(dāng)前工程內(nèi)宏是唯一的°#ifndefTIMER」#defineTIMER_H#endif建議5.2.1禁止通過(guò)聲明的方式引用外部函數(shù)接口、變量只能通過(guò)包含頭文件的方式使用其他模塊或文件提供的接口。通過(guò)extern聲明的方式使用外部函數(shù)接口、變量,容易在外部接口改變時(shí)可能導(dǎo)致聲明和定義不一致同時(shí)這種隱式依賴,容易導(dǎo)致架構(gòu)腐化。不符合規(guī)范的案例://a.cpp內(nèi)容externintFun();//Bad:通過(guò)extern的方式使用外部函數(shù)voidBar(){inti=Fun();},//b.cpp內(nèi)容intFun(){//Dosomething}應(yīng)該改為:〃a.cpp內(nèi)容#include"b.h" //Good:通過(guò)包含頭文件的方式使用其他,cpp提供的接UvoidBar(){inti=Fun();)//b.h內(nèi)容intFun();〃b.cpp內(nèi)容intFun(){//Dosomething)例外,有些場(chǎng)景需要引用其內(nèi)部函數(shù),但并不想侵入代碼時(shí),可以extern聲明方式引用。如:針對(duì)某一內(nèi)部函數(shù)進(jìn)行單元測(cè)試時(shí),可以通過(guò)extern聲明來(lái)引用被測(cè)函數(shù);當(dāng)需要對(duì)某一函數(shù)進(jìn)行打樁、打補(bǔ)丁處理時(shí),允許extern聲明該函數(shù)。規(guī)則5.2.5禁止在extern"C"中包含頭文件在extern"C”中包含頭文件,有可能會(huì)導(dǎo)致extern"C"嵌套,部分編譯器對(duì)extern"C"嵌套層次有限制,嵌套層次太多會(huì)編譯錯(cuò)誤。在C,C++混合編程的情況下,在extern"C”中包含頭文件,可能會(huì)導(dǎo)致被包含頭文件的原有意圖遭到破壞,比如鏈接規(guī)范被不正確地更改。示例,存在a.h和b.h兩個(gè)頭文件://a.h內(nèi)容#ifdef_cplusplusvoidFoo(int);#defineA(value)Foo(value)#elsevoidA(int)#endif//b.h內(nèi)容#ifdef_cplusplusextern'"C"{#endif#include"a.h"voidB();#ifdef_cplusplus)#endif使用C++預(yù)處理器展開(kāi)b.h,將會(huì)得到extern"CM{voidFoo(int);voidB();)按照a.h作者的本意,函數(shù)Foo是一個(gè)C++自由函數(shù),其鏈接規(guī)范為"C++"。但在b.h中,由于#include"a.h"被放到了extern"C"的內(nèi)部,函數(shù)Foo的鏈接規(guī)范被不正確地更改了。例外:如果在C++編譯環(huán)境中,想引用純C的頭文件,這些C頭文件并沒(méi)有extern"C"修飾。非侵入式的做法是,在extern"C"中去包含C頭文件。建議5.2.2盡量避免使用前置聲明,而是通過(guò)#include來(lái)包含頭文件前置聲明(forwarddeclaration)是類、函數(shù)和模板的純粹聲明,沒(méi)伴隨著其定義。?優(yōu)點(diǎn):前置聲明能夠節(jié)省編譯時(shí)間,多余的#include會(huì)迫使編譯器展開(kāi)更多的文件,處理更多的輸入。前置聲明能夠節(jié)省不必要的重新編譯的時(shí)間。#include使代碼因?yàn)轭^文件中無(wú)關(guān)的改動(dòng)而被重新編譯多次。?缺點(diǎn):.前置聲明隱藏了依賴關(guān)系,頭文件改動(dòng)時(shí),用戶的代碼會(huì)跳過(guò)必要的重新編譯過(guò)程。.前置聲明可能會(huì)被庫(kù)的后續(xù)更改所破壞。前置聲明函數(shù)或模板有時(shí)會(huì)妨礙頭文件開(kāi)發(fā)者變動(dòng)其API.例如擴(kuò)大形參類型,加個(gè)自帶默認(rèn)參數(shù)的模板形參等等。.前置聲明來(lái)自命名空間std::的symbol時(shí),其行為未定義(在C++11標(biāo)準(zhǔn)規(guī)范中明確說(shuō)明)。.前置聲明了不少來(lái)自頭文件的symbol時(shí),就會(huì)比單單一行的include冗長(zhǎng)。.僅僅為了能前置聲明而重構(gòu)代碼(比如用指針成員代替對(duì)象成員)會(huì)使代碼變得更慢更復(fù)雜。.很難判斷什么時(shí)候該用前置聲明,什么時(shí)候該用#include,某些場(chǎng)景下面前置聲明和#include互換以后會(huì)導(dǎo)致意想不到的結(jié)果。所以我們盡可能避免使用前置聲明,而是使用#include頭文件來(lái)保證依賴關(guān)系。建議523頭文件包含順序:首先是.cpp相應(yīng)的.h文件,其它頭文件按照穩(wěn)定度排序使用標(biāo)準(zhǔn)的頭文件包含順序可增強(qiáng)可讀性,避免隱藏依賴,建議按照穩(wěn)定度排序:cpp對(duì)應(yīng)的頭文件,C/C++標(biāo)準(zhǔn)庫(kù),系統(tǒng)庫(kù)的.h,其他庫(kù)的.h,本項(xiàng)目?jī)?nèi)其他的.h。舉例,F(xiàn)oo.cpp中包含頭文件的次序如下:#include"Foo/Foo.h"#include<cstdlib>#include<string>#include<linux/list.h>#include<linux/time.h>#include"platform/Base.h"#include"platform/Framework.h#include"project/public/Log.h將Foo.h放在最前面可以保證當(dāng)Foo.h遺漏某些必要的庫(kù),或者有錯(cuò)誤時(shí),F(xiàn)oo.cpp的構(gòu)建會(huì)立刻中止,減少編譯時(shí)間。對(duì)于頭文件中包含順序也參照此建議。例外:平臺(tái)特定代碼需要條件編譯,這些代碼可以放到其它includes之后。#include"foo/public/FooServer.h"#include"base/Port.h"http://ForLANG_CXX11.#ifdefLANG_CXX11#include<initializer_list>#endif//LANG_CXX11~6作用域命名空間命名空間里的內(nèi)容不縮進(jìn)。建議6.1.1對(duì)于cpp文件中不需要導(dǎo)出的變量,常量或者函數(shù),請(qǐng)使用匿名namespace封裝或者用static修飾在C++2003標(biāo)準(zhǔn)規(guī)范中,使用static修飾文件作用域的變量,函數(shù)等被標(biāo)記為deprecated特性,所以更推薦使用匿名namespace0主要原因如下:1.static在C++中已經(jīng)賦予了太多的含義,靜態(tài)函數(shù)成員變量,靜態(tài)成員函數(shù),靜態(tài)全局變量,靜態(tài)函數(shù)局部變量,每一種都有特殊的處理。2.static只能保證變量,常量和函數(shù)的文件作用域,但是namespace還可以封裝類型等。3.統(tǒng)一namespace來(lái)處理C++的作用域,而不需要同時(shí)使用static和namespace來(lái)管理。4.static修飾的函數(shù)不能用來(lái)實(shí)例化模板,而匿名namespace可以。但是不要在.h中使用中使用匿名namespace或者statico//Foo.cppnamespace{constintkMaxCount=20;voidInternalFun(){};)voidFoo::Fun(){inti=kMaxCount;InternalFun();)規(guī)則6.1.1不要在頭文件中或者#include之前使用using導(dǎo)入命名空間說(shuō)明:使用using導(dǎo)入命名空間會(huì)影響后續(xù)代碼,易造成符號(hào)沖突,所以不要在頭文件以及源文件中的#include之前使用using導(dǎo)入命名空間。示例://頭文件a.hnamespacenamespacea{intFun(int);}//頭文件b.hnamespacenamespaceb{intFun(int);)usingnamespacenamespaceb;voidG(){Fun(l);)//源代碼a.cpp#include"a.h"usingnamespacenamespacea;#include"b.h"voidmain(){G();//usingnamespacenamespaceafyincLude"b.h”之前.5發(fā)歧義:namespacea::Fun,namespaceb::Fun調(diào)用不明確}對(duì)于在頭文件中使用using導(dǎo)入單個(gè)符號(hào)或定義別名,允許在模塊自定義名字空間中使用,但禁止在全局名字空間中使用。//foo.h#include<fancy/string>usingfancy::string;//Bad,禁止向全局H'F空網(wǎng)導(dǎo)入符‘3namespacefoo{usingfancy::string;//Good,可以企模塊門(mén)定義M'F不間中與A彷causingMyVector=fancy::vector<int>;//Good,C++11可在H定義各‘產(chǎn)不間中定義別名}全局函數(shù)和靜態(tài)成員函數(shù)建議621優(yōu)先使用命名空間來(lái)管理全局函數(shù),如果和某個(gè)class有直接關(guān)系的,可以使用靜態(tài)成員函數(shù)說(shuō)明:非成員函數(shù)放在名字空間內(nèi)可避免污染全局作用域,也不要用類+靜態(tài)成員方法來(lái)簡(jiǎn)單管理全局函數(shù)。如果某個(gè)全局函數(shù)和某個(gè)類有緊密聯(lián)系,那么可以作為類的靜態(tài)成員函數(shù)。如果你需要定義一些全局函數(shù),給某個(gè)cpp文件使用,那么請(qǐng)使用匿名namespace來(lái)管理。namespacemynamespace{intAdd(inta,intb);}classFile{public:staticFileCreateTempFile(conststd::string&fileName);};全局常量和靜態(tài)成員常量建議631優(yōu)先使用命名空間來(lái)管理全局常量,如果和某個(gè)class有直接關(guān)系的,可以使用靜態(tài)成員常量說(shuō)明:全局常量放在命名空間內(nèi)可避免污染全局作用域,也不要用類+靜態(tài)成員常量來(lái)簡(jiǎn)單管理全局常量。如果某個(gè)全局常量和某個(gè)類有緊密聯(lián)系,那么可以作為類的靜態(tài)成員常量。如果你需要定義一些全局常量,只給某個(gè)cpp文件使用,那么請(qǐng)使用匿名namespace來(lái)管理。namespacemynamespace{constintkMaxSize=100;)classFile{public:staticconststd::stringkName;};全局變量建議641盡量避免使用全局變量,考慮使用單例模式說(shuō)明:全局變量是可以修改和讀取的,那么這樣會(huì)導(dǎo)致業(yè)務(wù)代碼和這個(gè)全局變量產(chǎn)生數(shù)據(jù)耦合。intcounter=0;//a.cppcounter++;//b?cppcounter++;//c.cppcout<<counter<<endl;使用單實(shí)例模式classCounter{public:staticCountersGetlnstance(){staticCountercounter;returncounter;}//單實(shí)例實(shí)現(xiàn)簡(jiǎn)單舉例voidIncrease(){value++;}voidPrint()const{std::cout<<value<<std::endl;}private:Counter():value(0){}private:intvalue;};//a.cppCounter::GetInstance().Increase();//b.cppCounter::GetInstance().Increase();//C.cppCounter::GetInstance().Print();實(shí)現(xiàn)單例模式以后,實(shí)現(xiàn)了全局唯一一個(gè)實(shí)例,和全局變量同樣的效果,并且單實(shí)例提供了更好的封裝性。例外:有的時(shí)候全局變量的作用域僅僅是模塊內(nèi)部,這樣進(jìn)程空間里面就會(huì)有多個(gè)全局變量實(shí)例,每個(gè)模塊持有一份,這種場(chǎng)景下是無(wú)法使用單例模式解決的。7類如果僅有數(shù)據(jù)成員,使用結(jié)構(gòu)體,其他使用類構(gòu)造,拷貝構(gòu)造,賦值和析構(gòu)函數(shù)構(gòu)造,拷貝,移動(dòng)和析構(gòu)函數(shù)提供了對(duì)象的生命周期管理方法:-構(gòu)造函數(shù)(constructor):X()?拷貝構(gòu)造函數(shù)(copyconstructor):X(constX&)■拷貝賦值操作符(copyassignment):operator=(constX&)■移動(dòng)構(gòu)造函數(shù)(moveconstructor):X(X&&)C++11以后提供?移動(dòng)賦值操作符(moveassignment):operator=(X&&)C++12以后提臾?析構(gòu)函數(shù)(destructor):~X()規(guī)則7.1.1類的成員變量必須顯式初始化說(shuō)明:如果類有成員變量,沒(méi)有定義構(gòu)造函數(shù),又沒(méi)有定義默認(rèn)構(gòu)造函數(shù),編譯器將自動(dòng)生成一個(gè)構(gòu)造函數(shù),但編譯器生成的構(gòu)造函數(shù)并不會(huì)對(duì)成員變量進(jìn)行初始化,對(duì)象狀態(tài)處于一種不確定性。例外:-如果類的成員變量具有默認(rèn)構(gòu)造函數(shù),那么可以不需要顯式初始化。示例:如下代碼沒(méi)有構(gòu)造函數(shù),私有數(shù)據(jù)成員無(wú)法初始化:classMessage{public:voidProcessOutMsg(){//???)private:unsignedintmsgID;unsignedintmsgLength;unsignedchar*msgBuffer;std::stringsomeldentifier;};Messagemessage;//message成員變心沒(méi)仃初始化message.ProcessOutNlsg(); //后續(xù)使用存;‘//因此,有必要定義默認(rèn)構(gòu)造函數(shù),如下:classMessage{public:Message():msg1D(0),msgLength(O){voidProcessOutMsg(){//...)private:unsignedintmsgID;unsignedintmsgLength;unsignedchar*msgBuffer;std::stringsomeldentifier;///1■仃默認(rèn)構(gòu)造函數(shù),不需要、泣式初始化};建議7.1.1成員變量?jī)?yōu)先使用聲明時(shí)初始化(C++11)和構(gòu)造函數(shù)初始化列表初始化說(shuō)明:C++11的聲明時(shí)初始化可以一目了然的看出成員初始值,應(yīng)當(dāng)優(yōu)先使用。如果成員初始化值和構(gòu)造函數(shù)相關(guān),或者不支持C++11,則應(yīng)當(dāng)優(yōu)先使用構(gòu)造函數(shù)初始化列表來(lái)初始化成員。相比起在構(gòu)造函數(shù)體中對(duì)成員賦值,初始化列表的代碼更簡(jiǎn)潔,執(zhí)行性能更好,而且可以對(duì)const成員和引用成員初始化。classMessage{public:Message():msgLength(0){//Good,優(yōu)先使用初始化列發(fā)msgBuffer=NULL;//Bad,不推薦在構(gòu)造函數(shù)中賦值}private:unsignedintmsgID{0};//Good,C++11中使用unsignedintmsgLength;unsignedchar*msgBuffer;};規(guī)則7.1.2為避免隱式轉(zhuǎn)換,將單參數(shù)構(gòu)造函數(shù)聲明為explicit說(shuō)明:?jiǎn)螀?shù)構(gòu)造函數(shù)如果沒(méi)有用explicit聲明,則會(huì)成為隱式轉(zhuǎn)換函數(shù)。示例:classFoo{public:explicitFoo(conststring&name):name(name){}private:stringname;};voidProcessFoo(constFoo&foo){}intmain(void){std::stringtest="test";ProcessFoo(test);//編譯僅逋過(guò)return0;)上面的代碼編譯不通過(guò),因?yàn)镻rocessFoo需要的參數(shù)是Foo類型,傳入的string類型不匹配。如果將Foo構(gòu)造函數(shù)的explicit關(guān)鍵字移除,那么調(diào)用ProcessFoo傳入的string就會(huì)觸發(fā)隱式轉(zhuǎn)換,生成一個(gè)臨時(shí)的Foo對(duì)象。往往這種隱式轉(zhuǎn)換是讓人迷惑的,并且容易隱藏Bug,得到了一個(gè)不期望的類型轉(zhuǎn)換。所以對(duì)于單參數(shù)的構(gòu)造函數(shù)是要求explicit聲明。規(guī)則7.1.3如果不需要拷貝構(gòu)造函數(shù)、賦值操作符/移動(dòng)構(gòu)造函數(shù)、賦值操作符,請(qǐng)明確禁止說(shuō)明:如果用戶不定義,編譯器默認(rèn)會(huì)生成拷貝構(gòu)造函數(shù)和拷貝賦值操作符,移動(dòng)構(gòu)造和移動(dòng)賦值操作符(移動(dòng)語(yǔ)義的函數(shù)C++11以后才有)。如果我們不要使用拷貝構(gòu)造函數(shù),或者賦值操作符,請(qǐng)明確拒絕:將拷貝構(gòu)造函數(shù)或者賦值操作符設(shè)置為private,并且不實(shí)現(xiàn):classFoo{private:Foo(constFoo&);Foo&operator=(constFoo&);};使用C++11提供的delete,請(qǐng)參見(jiàn)后面現(xiàn)代C++的相關(guān)章節(jié)。規(guī)則7.1.4拷貝構(gòu)造和拷貝賦值操作符應(yīng)該是成對(duì)出現(xiàn)或者禁止拷貝構(gòu)造函數(shù)和拷貝賦值操作符都是具有拷貝語(yǔ)義的,應(yīng)該同時(shí)出現(xiàn)或者禁止。//同時(shí)出現(xiàn)classFoo{public:Foo(constFoo&);Foo&operator=(constFoo&);}///同時(shí)default,C++11支持classFoo{public:Foo(constFoo&)=default;Foo&operator=(constFoo&)=default;};//同時(shí)禁止,C++11可以使用deleteclassFoo{private:Foo(constFoo&);Foo&operator=(constFoo&);};規(guī)則7.1.5移動(dòng)構(gòu)造和移動(dòng)賦值操作符應(yīng)該是成對(duì)出現(xiàn)或者禁止在C++11中增加了move操作,如果需要某個(gè)類支持移動(dòng)操作,那么需要實(shí)現(xiàn)移動(dòng)構(gòu)造和移動(dòng)賦值操作符。移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值操作符都是具有移動(dòng)語(yǔ)義的,應(yīng)該同時(shí)出現(xiàn)或者禁?止。//同時(shí)出現(xiàn)classFoo{public:Foo(Foo&&);Foo&operator=(Foo&&);//同時(shí)defauLt,C++11支持classFoo{public:Foo(Foo&&)=default;Foo&operator=(Foo&&)=default;};//同時(shí)禁止,使用C++11的deleteclassFoo{public:Foo(Foo&&)=delete;Foo&operator=(Foo&&)=delete;};規(guī)則7.1.6禁止在構(gòu)造函數(shù)和析構(gòu)函數(shù)中調(diào)用虛函數(shù)說(shuō)明:在構(gòu)造函數(shù)和析構(gòu)函數(shù)中調(diào)用當(dāng)前對(duì)象的虛函數(shù),會(huì)導(dǎo)致未實(shí)現(xiàn)多態(tài)的行為在C++中,一個(gè)基類一次只構(gòu)造一個(gè)完整的對(duì)象。示例:類Base是基類,Sub是派生類classBase{public:Base();virtualvoidLog()=0; //不同的派4:類調(diào)用不同的11志仁件};Base::Base(){ //快類構(gòu)造函數(shù)Log(); //調(diào)用虛函數(shù)Log}classSub:publicBase{public:virtualvoidLog();};當(dāng)執(zhí)行如下語(yǔ)句:Subsub;會(huì)先執(zhí)行Sub的構(gòu)造函數(shù),但首先調(diào)用Base的構(gòu)造函數(shù),由于Base的構(gòu)造函數(shù)調(diào)用虛函數(shù)Log,此時(shí)Log還是基類的版本,只有基類構(gòu)造完成后,才會(huì)完成派生類的構(gòu)造,從而導(dǎo)致未實(shí)現(xiàn)多態(tài)的行為。同樣的道理也適用于析構(gòu)函數(shù)。繼承規(guī)則7.2.1基類的析構(gòu)函數(shù)應(yīng)該聲明為virtual說(shuō)明:只有基類析構(gòu)函數(shù)是virtual,通過(guò)多態(tài)調(diào)用的時(shí)候才能保證派生類的析構(gòu)函數(shù)被調(diào)用。示例:基類的析構(gòu)函數(shù)沒(méi)有聲明為virtual導(dǎo)致了內(nèi)存泄漏。classBase{public:virtualstd::stringgetVersion()=0;~Base(){std::cout<<"-Base"<<std::endl;}};classSub:publicBase{public:Sub():numbers(NULL){}~Sub(){delete[]numbers;std::cout<<"~Sub"<<std::endl;intInit(){constsize_tnumbercount=100;numbers=new(std::nothrow)int[numberCount];if(numbers==NULL){return-1;))std::stringgetVersion(){returnstd::string("hello!");}private:int*numbers;};intmain(intargc,char*args[]){Base*b=newSub();deleteb;return0;}由于基類Base的析構(gòu)函數(shù)沒(méi)有聲明為virtual,當(dāng)對(duì)象被銷(xiāo)毀時(shí),只會(huì)調(diào)用基類的析構(gòu)函數(shù),不會(huì)調(diào)用派生類Sub的析構(gòu)函數(shù),導(dǎo)致內(nèi)存泄漏。規(guī)則7.2.2禁止虛函數(shù)使用缺省參數(shù)值說(shuō)明:在C++中,虛函數(shù)是動(dòng)態(tài)綁定的,但函數(shù)的缺省參數(shù)卻是在編譯時(shí)就靜態(tài)綁定的。這意味著你最終執(zhí)行的函數(shù)是一個(gè)定義在派生類,但使用了基類中的缺省參數(shù)值的虛函數(shù)。為了避免虛函數(shù)重載時(shí),因參數(shù)聲明不一致給使用者帶來(lái)的困惑和由此導(dǎo)致的問(wèn)題,規(guī)定所有虛函數(shù)均不允許聲明缺省參數(shù)值。示例:虛函數(shù)display缺省參數(shù)值text是由編譯時(shí)刻決定的,而非運(yùn)行時(shí)刻,沒(méi)有達(dá)到多態(tài)的目的:classBase{public:virtualvoidDisplay(conststd::stringstext="Base!"){std::cout<<text<<std::endl;}virtual~Base(){}};classSub:publicBase{public:virtualvoidDisplay(conststd::stringstext="Sub!"){std::cout<<text<<std::endl;virtual~Sub(){}};intmain(){Base*base=newSub();Sub*sub=newSub();base->Display()

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論