版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
章節(jié) 內(nèi)容 0前言 目的重點關(guān)注約定例外1原則 好代碼的原則類和函數(shù)設(shè)計指導(dǎo)原則保證靜態(tài)類型安全遵循C++ISO標準優(yōu)先編譯時檢查錯誤使用命名空間來限定作用域優(yōu)先使用C++特性而不是C特性2命名通用命名文件命名函數(shù)命名類型命名變量命名宏、常量、枚舉命名3格式行寬縮進大括號函數(shù)聲明和定義函數(shù)調(diào)用if語句循環(huán)語句switch語句表達式變量賦值初始化指針和引用編譯預(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è)計內(nèi)聯(lián)函數(shù)函數(shù)參數(shù)9C++其常量與初始化表達式類型轉(zhuǎn)換資源分配和釋放標準庫const的用法異常他特性模板宏10現(xiàn)代代碼簡潔性和安全性提升智能指針Lambda接口C++特性。前言目的規(guī)則并不是完美的,通過禁止在特定情況下有用的特性,可能會對代碼實現(xiàn)造成影響。但是我們制定規(guī)則的目的—"為了大多數(shù)程序員可以得到更多的好處如果在團隊運作中認為某個規(guī)則無法遵循,希望可以共同改進該規(guī)則。參考該規(guī)范之前,希望您具有相應(yīng)的C++基礎(chǔ)能力,而不是通過該文檔來學(xué)習(xí)C++。1.了解C++的ISO標準;2.熟知C++的基本語言特性,包括C++03/11/14/17相關(guān)特性;3.了解C++的標準庫;重點關(guān)注約定C++的編程風(fēng)格,比如命名,排版等。C++的模塊化設(shè)計,如何設(shè)計頭文件,類,接口和函數(shù)。C++相關(guān)特性的優(yōu)秀實踐,比如常量,類型轉(zhuǎn)換,資源管理,模板等。現(xiàn)代C++的優(yōu)秀實踐,包括C++11/14/17中可以提高代碼可維護性,提高代碼可靠性的相關(guān)約定。約定規(guī)則:編程時必須遵守的約定(must)建議:編程時應(yīng)該遵守的約定(should)本規(guī)范適用通用C++標準,如果沒有特定的標準版本,適用所有的版本(C++03/11/14/17),例外無論是‘規(guī)則'還是'建議',都必須理解該條目這么規(guī)定的原因,并努力遵守。但是,有些規(guī)則和建議可能會有例外。在不違背總體原則,經(jīng)過充分考慮,有充足的理由的前提下,可以適當違背規(guī)范中約定。例外破壞了代碼的一致性,請盡量避免。'規(guī)則’的例外應(yīng)該是極少的。下列情況,應(yīng)風(fēng)格一致性原則優(yōu)先:修改外部開源代碼、第三方代碼時,應(yīng)該遵守開源代碼、第三方代碼已有規(guī)范,口持風(fēng)格結(jié)一某些特定M域:優(yōu)先參考其行業(yè)規(guī)范。1原則好代碼的原則我們參考KentBeck的簡單設(shè)計四原則來指導(dǎo)我們的如何寫出優(yōu)秀的代碼,如何有效地判斷我們的代碼是優(yōu)秀的。1.通過所有測試(Passesitstests)2.盡可能消除重復(fù)(Minimizesduplication)3.盡可能清晰表達(Maximizesclarity)4.更少代碼元素(Hasfewerelements)5.以上四個原則的重要程度依次降低。這組定義被稱做簡單設(shè)計原則。第一條強調(diào)的是外部需求,這是代碼實現(xiàn)最重要的;第二點就是代碼的模塊架構(gòu)設(shè)計,保證代碼的正交性,保證代碼更容易修改;第三點是代碼的可閱讀性,保證代碼是容易閱讀的;最后一點才是保證代碼是簡潔的,在簡潔和表達力之間,我們更看重表達力。類和函數(shù)設(shè)計指導(dǎo)原則C++是典型的面向?qū)ο缶幊陶Z言,軟件工程界已經(jīng)有很多OOP原則來指導(dǎo)我們編寫大規(guī)模的,高可擴展的,可維護性的代碼:-高內(nèi)聚,低耦合的基本原則-SOLID原則-迪米特法則-"Tell,Don,task"原則-組合/聚合復(fù)用原則保證靜態(tài)類型安全我們希望C++應(yīng)該是靜態(tài)類型安全的,這樣可以減少運行時的錯誤,提高代碼的健壯性。但是由于C++的下面的特性存在,會破壞C++靜態(tài)類型安全,我們針對這部分特性要仔細處理。?unions聯(lián)合體?類型轉(zhuǎn)換cast-縮窄轉(zhuǎn)換narrowingconversions-類型退化typedecay-范圍錯誤rangeerrors-void*類型指針我們可以通過約束這些特性的使用,或者使用C++的新特性,比如variant(C++17),GSL的span,narrow_cast等來解決這些問題,提高C++代碼的健壯性。遵循C++ISO標準希望通過使用ISOC++標準的特性來編寫C++代碼,對于ISO標準中未定義的或者編譯器實現(xiàn)的特性要謹慎使用,對于GCC等編譯器的提供的擴展特性也需要謹慎使用,這些特性會導(dǎo)致代碼的可移植性比較差。注意:如果模塊中需要使用相關(guān)的擴展特性來,那么盡可能將這些特性封裝成獨立的接口,并且可以通過編譯選項關(guān)閉或者編譯這些特性。對于這些擴展特性的使用,請模塊制定特性編程指南來指導(dǎo)這些特性的使用。優(yōu)先編譯時檢查錯誤通過編譯器來優(yōu)先保證代碼健壯性,而不是通過編寫錯誤處理代碼來處理編譯就可以發(fā)現(xiàn)的異常,比如:通過const來保證數(shù)據(jù)的不變性,防止數(shù)據(jù)被無意修改。通過gsl::span等來保證char數(shù)組不越界,而不是通過運行時的length檢查。通過static_assert來進行編譯時檢查。使用命名空間來限定作用域全局變量,全局常量和全局類型定義由于都屬于全局作用域,在項目中,使用第三方庫中容易出現(xiàn)沖突。命名空間將作用域細分為獨立的,具名的作用域,可有效地防止全局作用域的命名沖突。1.class,struct等都具有自己的類作用域。2.具名的namespace可以實現(xiàn)類作用域更上層的作用域。3.匿名namespace和static可以實現(xiàn)文件作用域。對于沒有作用域的宏變量,宏函數(shù)強烈建議不使用。作用域的一些缺點:1.雖然可以通過作用域來區(qū)分兩個命名相同的類型,但是還是具有迷惑性。2.內(nèi)聯(lián)命名空間會讓命名空間內(nèi)部的成員擺脫限制,讓人迷惑。3.通過多重嵌套來定義namespace,會讓完整的命名空間比較冗長。
所以,我們使用命名空間的建議如下:-對于變量,常量和類型定義盡可能使用namespace,減少全局作用域的沖突-不要在頭文件中使用usingnamespace-不要使用內(nèi)聯(lián)命名空間-鼓勵在.cpp文件中通過匿名namespace或者static集封裝,防止不必要的定義通過API暴露出去。優(yōu)先使用C++特性而不是C特性C++比起c語言更加類型安全,更加抽象。我們更推薦使用C++的語言特性來編程,比如使用string而不是char*,使用vector而不是原生數(shù)組,使用namespace而不是statico通用命名常見命名風(fēng)格有:駝峰風(fēng)格(CamelCase)大小寫字母混用,單詞連在一起,不同單詞間通過單詞首字母大寫來分開。按連接后的首字母是否大寫,又分:大駝峰(UperCamelCase)和小駝峰(lowerCamelCase)內(nèi)核風(fēng)格(unix」ike)單詞全小寫,用下劃線分割?如:'test_result'匈牙利風(fēng)格在‘大駝峰’的基礎(chǔ)上,加上前綴;前綴用于表達類型或用途。如:^iSavedCount",^Tested,規(guī)則2.1.1標識符命名使用駝峰風(fēng)格命名風(fēng)格大駝峰大駝峰(接口部分可加前綴,如命名風(fēng)格大駝峰大駝峰(接口部分可加前綴,如XXX一函數(shù)名)小駝峰k+大小寫混合大寫+下劃線全小寫類型類類型,結(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作為實現(xiàn)文件的后綴,這樣可以直接區(qū)分C++代碼,而不是C代碼。目前業(yè)界還有一些其他的后綴的表示方法:頭文件:.hh,.hpp,.hxxcpp文件:.cc,.cxx,.C對于本文檔,我們默認使用.h和.cpp作為后綴。建議2.2.2C++文件名和類名保持一致C++的頭文件和cpp文件名和類名保持一致,使用下劃線小寫風(fēng)格。如下:-database_connection.h-database_connection.cpp結(jié)構(gòu)體,命名空間,枚舉等定義的文件名類似。函數(shù)命名函數(shù)命名統(tǒng)一使用大駝峰風(fē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{...對于命名空間的命名,建議全小寫://namespacenamespaceosutils{namespacefileutils{}}建議2.4.1避免濫用typedef或者#define對基本類型起別名除有明確的必要性,否則不要用typedef/#define對基本數(shù)據(jù)類型進行重定義。優(yōu)先使用<cstdint>頭文件中的基本類型:有符號類型無符號類型描述int8_tuint8_t寬度恰為8的有/無符號整數(shù)類型intl6_tuintl6_t寬度恰為16的有/無符號整數(shù)類型int32_tuint32_t寬度恰為32的有/無符號整數(shù)類型int64_tuint64_t寬度恰為64的有/無符號整數(shù)類型intptr_tuintptr_t足以保存指針的有/無符號整數(shù)類型如果模塊有自己的定義,請使用統(tǒng)一的typedef來定義類型:typedefsignedcharVOS_INT8;typedefunsignedcharVOS_U工NT8;#if_WORDSIZE==64typedefunsignedlongintVOS_UINTPTR;#elsetypedefunsignedint VOS_UINTPTR;#endif如果模塊為了封裝某個類型的信息,方便后續(xù)的擴展,可以使用typedef來重新定義。typedefuint8_tDevicelD;//...//若干版本后擴展成16-bittypedefuintl6_tDevicelD;有特殊作用的類型typedefvoid*Handle;注意:不要使用#define進行別名定義,并且在C++11以后推薦使用using來定義類型。除上述理由外,應(yīng)避免給其本數(shù)值類型別名定義。因為類型別名可讀性并不好,隱藏了基本數(shù)值類型信息,如位寬,是否帶符號。濫用舉例:typedefuintl6_tMyCounter;〃.…intFoo(???){MyCounterc;〃---while(c>=0){printf("counter=%d\n'\C);//...)//...)對,MyCounter,是否可能小于0,打印時用,%d,還是,%u,都不是很直觀,極容易引入上述類似缺陷。變量命名通用變量命名采用小駝峰,包括全局變量,函數(shù)形參,局部變量,成員變量。std::stringtableName;//Good:推薦此風(fēng)格std::stringtablename;//Bad:禁上此風(fēng)格std::stringpath;//Good:只仃?個中.詞時,小,花嶺為的小寫規(guī)則251類的成員變量命名使用小駝峰。classFoo{private:std::stringfileName;//不潦加任何作用域前綴或者后綴};當構(gòu)造函數(shù)參數(shù)和成員變量重名時,可通過this,來引用成員變量。classMyClass{public:MyClass(intmyVar):myVar(myVar){//OK,初始化列表允許同%人參初始化同名成員if(NeedNewVar()){this->myVar=GetValue();//';匕起不要漏掠.否則就成「給人醫(yī)賦值})private:intmyVar;};宏、常量、枚舉命名宏采用全大寫,下劃線連接的格式。常量、枚舉值使用k+大小寫混合。函數(shù)局部const常量和類的普通const成員變量,使用小駝峰命名風(fēng)格。ftdefineMAX(a,b)(((a)<(b))?(b):(a))//僅對宏命冬舉例,并不推薦用宏實現(xiàn)此類功能enumTintColor{ //注意,枚舉類型啟用大駝峰,其下面的取值是大小7混合kRedjkDarkRedjkGreen,kLightGreen};intFunc(...){constunsignedintbuffersize=100; //函數(shù)局部常/char*p=newchar[bufferSize];}.namespaceutils{constunsignedintkFileSize=200; //企局常"止)3格式盡管有些編程的排版風(fēng)格因人而異,但是我們強烈建議和要求使用統(tǒng)一的編碼風(fēng)格,以便所有人都能夠輕松的閱讀和理解代碼,增強代碼的可維護性。行寬建議3.1.1行寬不超過120個字符建議每行字符數(shù)不要超過120個。如果超過120個字符,請選擇合理的方式進行換行。例外:?如果一行注釋包含了超過120個字符的命令或URL,則可以保持一行,以方便復(fù)制、粘貼和通過grep查找;?包含長路徑的include語句可以超出120個字符,但是也需要盡量避免;?編譯預(yù)處理中的error信息可以超出一行。預(yù)處理的error信息在一行便于閱讀和理解,即使超過120個字符。#ifndefXXX_YYY_ZZZ#errorHeaderaaaa/bbbb/cccc/abc.hmustonlybeincludedafterxxxx/yyyy/zzzz/xyz.h,becausexxxxxxxxxxxxxxxxxxxxxxxxxxxxx#endif縮進規(guī)則3.2.1使用空格進行縮進,每次縮進2個空格只允許使用空格(space)進行縮進,每次縮進為2個空格。大括號規(guī)則3.3.1除函數(shù)外,使用K&R縮進風(fēng)格函數(shù)左大括號跟隨語句放行末。右大括號獨占一行,除非后面跟著同一語句的剩余部分,如do語句中的while,或者if語句的else/elseif,或者逗號、分號。如:structMyType{ //跟隨語?句放行末.前置1空格}「intFoo(inta){ //函數(shù)左人括號跟隨語句放行木if(...){}else{}')推薦這種風(fēng)格的理由:代碼更緊湊;相比另起一行,放行末使代碼閱讀節(jié)奏感上更連續(xù);符合后來語言的習(xí)慣,符合業(yè)界主流習(xí)慣;現(xiàn)代集成開發(fā)環(huán)境(IDE)都具有代碼縮進對齊顯示的輔助功能,大括號放在行尾并不會對縮進和范圍產(chǎn)生理解上的影響。對于空函數(shù)體,可以將大括號放在同一行:classMyClass{public:MyClass():value(0){}private:intvalue;};函數(shù)聲明和定義規(guī)則341函數(shù)聲明和定義的返回類型和函數(shù)名在同一行;函數(shù)參數(shù)列表超出行寬時要換行并合理對齊在聲明和定義函數(shù)的時候,函數(shù)的返回值類型應(yīng)該和函數(shù)名在同一行;如果行寬度允許,函數(shù)參數(shù)也應(yīng)該放在一行;否則,函數(shù)參數(shù)應(yīng)該換行,并進行合理對齊。參數(shù)列表的左圓括號總是和函數(shù)名在同一行,不要單獨一行;右圓括號總是跟隨最后一個參數(shù)。換行舉例:ReturnTypeFunctionName(ArgTypeparamNamel,ArgTypeparamName2){//Good:全港同一行}.ReturnTypeVeryVeryVeryLongFunctionName(ArgTypeparamNamel^不滿足所有參數(shù),進行換行ArgTypeparamNameZ^//Good:和上一行參數(shù)對齊ArgTypeparamName3){}…ReturnTypeLongFunctionName(ArgTypeparamNamel,ArgTypeparamName2,//行寬限制,進行換行ArgTypeparamName3,ArgTypeparamName4,ArgTypeparamName5){/Good:換行后4空格縮進}.ReturnTypeReallyReallyReallyReallyLongFunctionName(不滿足第1個參數(shù),直接換行ArgTypeparamNamel,ArgTypeparamName2,ArgTypeparamName3){//Good:換行房4空格縮進函數(shù)調(diào)用規(guī)則3.5.1函數(shù)調(diào)用入?yún)⒘斜響?yīng)放在一行,超出行寬換行時,保持參數(shù)進行合理對齊函數(shù)調(diào)用時,函數(shù)參數(shù)列表放在一行。參數(shù)列表如果超過行寬,需要換行并進行合理的參數(shù)對齊。左圓括號總是跟函數(shù)名,右圓括號總是跟最后一個參數(shù)。換行舉例:ReturnTyperesult=FunctionNameCparamNamel,paramName2);//Good:函數(shù)參數(shù)放在一行ReturnTyperesult=FunctionName(paramNameljparamName2, //Good:保持與上方參數(shù)對齊paramName3);ReturnTyperesult=FunctionName(paramNamel,paramName2,paramName3,paramName4,paramNameS); //Good:參數(shù)換行,4空格縮進ReturnTyperesult=VeryVeryVeryLongFunctionName( //行寬不滿足第1個參數(shù),直接換行paramNamel,paramName2,paramName3); //換行后,4空格縮進如果函數(shù)調(diào)用的參數(shù)存在內(nèi)在關(guān)聯(lián)性,按照可理解性優(yōu)先于格式排版要求,對參數(shù)進行合理分組換行。//Good:每行的參數(shù)代表一組相關(guān)性較強的數(shù)據(jù)結(jié)構(gòu).放在一行便于理解intresult=DealWithStructureLikeParams(left.x,left.y,//表,-相關(guān)參數(shù)right.x,right.y);//我示另外一組相關(guān)參數(shù)if語句規(guī)則3.6.1if語句必須要使用大括號我們要求if語句都需要使用大括號,即便只有一條語句。理由:-代碼邏輯直觀,易讀;-在已有條件語句代碼上增加新代碼時不容易出錯;-對于在if語句中使用函數(shù)式宏時,有大括號保護不易出錯(如果宏定義時遺漏了大括號)。if(objectlsNotExist){ //Good:單.仃條件語?句也加大括號returnCreateNewObject();)規(guī)則3.6.2禁止if/else/elseif寫在同一行條件語句中,若有多個分支,應(yīng)該寫在不同行。如下是正確的寫法:if(someConditions){DoSomething();}else{//Good:eLse與if在不同行}'下面是不符合規(guī)范的案例:if(someConditions){.??}else{.?.}//Bad:eLse與if在同一行循環(huán)語句規(guī)則3.7.1循環(huán)語句要求使用大括號和if語句類似,我們要求for/while循環(huán)語句必須加上的大括號,即使循環(huán)體是空的,或者循環(huán)語句只有一條。for(inti=0;i<someRange;i++){DoSomething();)如果循環(huán)體是空的,應(yīng)該使用空的大括號,而不是使用單個分號。單個分號容易被遺漏,也容易被誤認為是循環(huán)語句中的一部分。for(inti=0;i<someRange;i++){}//Good:for循環(huán)體是"E,使用大括號,而不是使用分號while(someCondition){}//Good:whiLe循環(huán)體是個:,使用大括號,而不是使用分號while(someCondition){continue;//Good:continue太小個邏輯,可以使用大括號-也可以不使用}壞的例子:for(inti=0;i<someRange;i++); //Bad:for循環(huán)體是個.也不要乂使用分號,要使用大括號while(someCondition);//Bad:使用分3容易讓人誤解是whtlu語句中的一部分switch語句規(guī)則3.8.1switch語句的case/default要縮進一層switch語句的縮進風(fēng)格如下:switch(van){case0: //Good:縮進DoSomethingl();//Good:縮進break;case1:{ //Good:帶大括號格式DoSomething2();break;)default:break;)switch(van){case0: //Bad:case未縮進DoSomething();break;default: //Bad:defauLt未縮進break;)表達式建議391表達式換行要保持換行的一致性,運算符放行末較長的表達式,不滿足行寬要求的時候,需要在適當?shù)牡胤綋Q行。一般在較低優(yōu)先級運算符或連接符后面截斷,運算符或連接符放在行末。運算符、連接符放在行末,表示"未結(jié)束,后續(xù)還有"。例://假設(shè)下面第一行已經(jīng)不滿足行寬要求if(currentvalue>threshold&&//Good:換行后,邏鉗操作汾放住行尾someConditionsion){DoSomething();}'intresult=reallyReallyLongVariableNamel+ //GoodreallyReallyLongVariableName2;表達式換行后,注意保持合理對齊,或者4空格縮進。參考下面例子intsum=longVaribleNamel+longVaribleName2+longVaribleName3+longVaribleName4+longVaribleNameS+longVaribleName6; //Good:4空格縮進intsum=longVaribleNamel+longVaribleName2+longVaribleName3+longVaribleName4+longVaribleNameS+longVaribleNameS;//Good:保持對齊變量賦值規(guī)則3.10.1多個變量定義和賦值語句不允許寫在一行每行只有一個變量初始化的語句,更容易閱讀和理解。intmaxCount=10;boolisCompleted=false;下面是不符合規(guī)范的示例:intmaxCount=10;boolisCompleted=false;//Bad: ''公址初始化7,開放在多行,每行一個變量初始化intx,y=0;//Bad:多個變量定義需要分行,每行一個intpointX;intpointY;pointX=1;pointY=2;//Bad:多個變賦值語旬放同一行例外:for循環(huán)頭、if初始化語句(C++17)、結(jié)構(gòu)化綁定語句(C++17)中可以聲明和初始化多個變量。這些語句中的多個變量聲明有較強關(guān)聯(lián),如果強行分成多行會帶來作用域不一致,聲明和初始化割裂等問題。初始化初始化包括結(jié)構(gòu)體、聯(lián)合體、及數(shù)組的初始化規(guī)則3.11.1初始化換行時要有縮進,并進行合理對齊結(jié)構(gòu)體或數(shù)組初始化時,如果換行應(yīng)保持4空格縮進。從可讀性角度出發(fā),選擇換行點和對齊位置。constintrank[]={16,16,16,16,32,32,32,32,64,64,64,64,32,32,32,32};指針與引用建議3.12.1指針類型“*”跟隨變量名或者類型,不要兩邊都留有或者都沒有空格指針命名:*靠左靠右都可以,但是不要兩邊都有或者都沒有空格。int*p=NULL;//Goodint*p=NULL;//Goodint*p=NULL;//Badint*p=NULL;//Bad例外:當變量被const修飾時,〃*”無法跟隨變量,此時也不要跟隨類型。char*constVERSION="V100";建議3.12.2引用類型〃&〃跟隨變量名或者類型,不要兩邊都留有或者都沒有空格引用命名:&靠左靠右都可以,但是不要兩邊都有或者都沒有空格。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ù)處理的"#"統(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ù)處理語句"#"不縮進#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)鍵字之后加空格;小括號內(nèi)部的兩側(cè),不要加空格;大括號內(nèi)部兩側(cè)有無空格,左右必須保持一致;一元操作符(&*+--!)之后不要加空格;二元操作符(=+-<>*/%I&A<=>===!=)左右兩側(cè)加空格三目運算符(?:)符號兩側(cè)均需要空格前置和后置的自增、自減(++-)和變量之間不加空格結(jié)構(gòu)體成員操作符(.->)前后不加空格逗號。前面不加空格,后面增加空格對于模板和類型轉(zhuǎn)換(<>)和類型之間不要添加空格域操作符(::)前后不要添加空格冒號(:)前后根據(jù)情況來判斷是否要添加空格常規(guī)情況:voidFoo(intb){//Good:大括號前應(yīng)該留“X格inti=0;//Good:變埴初始化時,=前儲應(yīng)該有空格,分心前而不要招個:格intbuf[kBufSize]={0}; //Good:大括號內(nèi)兩側(cè)都無空格函數(shù)定義和函數(shù)調(diào)用:intresult=Foo(argl,arg2);A//Bad:逗號后HH需哽增加空格intresult=Foo(argl,arg2);A 八//Bad:函數(shù)與數(shù)列發(fā)的左括號后面不應(yīng)該行個格,右括號前面不應(yīng)該有空格指針和取地址x=*p; //Good:*操作符和指針p之間不加空稱p=&x; //Good:&操作符和變獻x之間不加安格x=r.y;//Good:通過.訪問成員變吊;時不加空格x=r->y;//Good:通過->訪問成員變量:時不加空格操作符:x=0; //Good:賦值操作的=前后都要加々格x=-5; //Good:負數(shù)的符—值之前不要加空格++X; //Good:的置和后置的++/--和變;1;二之間不要加格if(x&&!y)//Good:布爾操作符前后要加上空格.!操作和變量之間不要空格v=w*x+y/z;//Good:二元操作符前儲要加空格v=w*(x+z); //Good:括號內(nèi)的衣達式前后不需要加“。格inta=(x<y)?x:y;//Good:rH運算符,?和:前腦需要添加審格循環(huán)和條件語句:if(condition){//Good:if關(guān)鍵不和括號之間加空格,括號內(nèi)條件語句前腦不加空格}else{ //Good:eLse關(guān)鍵字和大括號之間加審格while(condition){} //Good:whiLe關(guān)鍵字和括號之間加々格,括號內(nèi)條件語句前后不加空格for(inti=0;i<someRange;++i){//Good:for關(guān)鍵'丁和括號之間加空格.分號之后加空格}一switch(condition){//Good:switch關(guān)犍:后面行1個格case0: //Good:case語句條件和H號之間不加個:格break;default:break;)模板和轉(zhuǎn)換//尖括號(<and>)不與空格緊鄰,<前沒有空格,>和(之間也沒有.vector<string>x;y=static_cast<char*>(x);//在類型與指針操作符之間留空格也可以,但要保持一致.vector<char*>x;域操作符std::cout;//Good:命%i個:問訪問,41更:界”。格intMyClass::GetValue()const{}//Good:〈寸「成員函數(shù)定義,不變部"、格冒號//添加空格的場景//Good:類的派生需要留有空格classSub:publicBase{};//構(gòu)造函數(shù)初始化列表需要留有空格MyClass::MyClass(intvar):someVar(var){DoSomething();}一//位域表示也留有空格structXX{chara:4;charb:5;charc:4;};//不添加空格的場景//Good:對于pubLic:jprivate:這種類訪問權(quán)限的冒號不用添加空格classMyClass{public:MyClass(intvar);private:intsomeVar;};//對于switch-case的case和defQuit后面的冒號不用添加空格switch(value){case1:DoSomething();break;default:break;)注意:當前的集成開發(fā)環(huán)境(IDE)可以設(shè)置刪除行尾的空格,請正確配置。建議3.14.2合理安排空行,保持代碼緊湊減少不必要的空行,可以顯示更多的代碼,方便代碼閱讀。下面有一些建議遵守的規(guī)則:-根據(jù)上下內(nèi)容的相關(guān)程度,合理安排空行;-函數(shù)內(nèi)部、類型定義內(nèi)部、宏內(nèi)部、初始化表達式內(nèi)部,不使用連續(xù)空行-不使用連續(xù)3個空行,或更多-大括號內(nèi)的代碼塊行首之前和行尾之后不要加空行。intFoo(){)//Bad:兩個函數(shù)定義間超過了一個空行intBar(){}…if(...){//Bad:大括號內(nèi)的代碼塊行首不要加入空行" //Bad:大括號內(nèi)的代碼塊行尾不要加入空行}intFoo(...){//Bad:函數(shù)體內(nèi)行首不要加空行類規(guī)則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:boolSomelnternalFunction();intsomeVar;intsomeOtherVar;};在各個部分中,建議將類似的聲明放在一起,并且建議以如下的順序:類型(包括typedef,using和嵌套的結(jié)構(gòu)體與類),常量,工廠函數(shù),構(gòu)造函數(shù),賦值運算符,析構(gòu)函數(shù),其它成員函數(shù),數(shù)據(jù)成員。規(guī)則3.15.2構(gòu)造函數(shù)初始化列表放在同一行或按四格縮進并排多行//加果所有變量能放在同一行:MyClass::MyClass(intvar):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注釋一般的,盡量通過清晰的架構(gòu)邏輯,好的符號命名來提高代碼可讀性;需要的時候,才輔以注釋說明。注釋是為了幫助閱讀者快速讀懂代碼,所以要從讀者的角度出發(fā),按需注釋“注釋內(nèi)容要簡潔、明了、無二義性,信息全面且不冗余。注釋跟代碼一樣重要。寫注釋時要換位思考,用注釋去表達此時讀者真正需要的信息。在代碼的功能、意圖層次上進行注釋,即注釋解釋代碼難以表達的意圖,不要重復(fù)代碼信息。修改代碼時,也要保證其相關(guān)注釋的一致性。只改代碼,不改注釋是一種不文明行為,破壞了代碼與注釋的一致性,讓閱讀者迷惑、費解,甚至誤解。注釋風(fēng)格在C++代碼中,使用/**/和〃都是可以的。按注釋的目的和位置,注釋可分為不同的類型,如文件頭注釋、函數(shù)頭注釋、代碼注釋等等;同一類型的注釋應(yīng)該保持統(tǒng)一的風(fēng)格。注意:本文示例代碼中,大量使用‘〃'后置注釋只是為了更精確的描述問題,并不代表這種注釋風(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ù)簽名無法表達的信息、,加函數(shù)頭注釋輔助說明;函數(shù)頭注釋統(tǒng)一放在函數(shù)聲明或定義上方,使用如下風(fēng)格之一:使用〃寫函數(shù)頭//單行函數(shù)頭intFuncl(void);//多行函數(shù)頭//第二行intFunc2(void);使用/* */寫函數(shù)頭/*單行函數(shù)頭*/intFuncl(void);/*另一種單行函數(shù)頭/intFunc2(void);多行函數(shù)頭第二行/intFunc3(void);函數(shù)盡量通過函數(shù)名自注釋,按需寫函數(shù)頭注釋。不要寫無用、信息冗余的函數(shù)頭;不要寫空有格式的函數(shù)頭。函數(shù)頭注釋內(nèi)容可選,但不限于:功能說明、返回值,性能約束、用法、內(nèi)存約定、算法實現(xiàn)、可重入的要求等等。模塊對外頭文件中的函數(shù)接口聲明,其函數(shù)頭注釋,應(yīng)當將重要、有用的信息表達清楚。例:返回實際寫入的字節(jié)數(shù),-1表示寫入失敗注意,內(nèi)存buf由調(diào)用者負責(zé)釋放/intWriteString(constchar*buf,intlen);壞的例子:函數(shù)名:MriteString功能:寫入字符串參數(shù):返回值:/intWriteString(constchar*buf>intlen);上面例子中的問題:參數(shù)、返回值,空有格式?jīng)]內(nèi)容函數(shù)名信息冗余關(guān)鍵的buf由誰釋放沒有說清楚代碼注釋規(guī)則441代碼注釋放于對應(yīng)代碼的上方或右邊規(guī)則4.4.2注釋符與注釋內(nèi)容間要有1空格;右置注釋與前面代碼至少1空格代碼上方的注釋,應(yīng)該保持對應(yīng)代碼一樣的縮進。選擇并統(tǒng)一使用如下風(fēng)格之一:使用〃//這是單行注釋DoSomething();//這是多行注釋//第二行DoSomething();使用/*''*//*這是單行注釋*/DoSomething();/*另一種方式的多行注釋第二行/DoSomething();代碼右邊的注釋,與代碼之間,至少留1空格,建議不超過4空格。通常使用擴展后的TAB鍵即可實現(xiàn)1-4空格的縮進。選擇并統(tǒng)一使用如下風(fēng)格之一:intfoo=100;//放心邊的「intbar=200;/*放。邊的注釋*/右置格式在適當?shù)臅r候,上下對齊會更美觀。對齊后的注釋,離左邊代碼最近的那一行,保證1-4空格的間隔。例:constintkConst=100; /*相關(guān)的同類注釋,可以考慮/:卜一對齊*/constintkAnotherConst=200; /*_L:卜對齊時,'j左側(cè)代碼保持間隔*/當右置的注釋超過行寬時,請考慮將注釋置于代碼上方。規(guī)則4.4.3不用的代碼段直接刪除,不要注釋掉被注釋掉的代碼,無法被正常維護;當企圖恢復(fù)使用這段代碼時,極有可能引入易被忽略的缺陷。正確的做法是,不需要的代碼直接刪除掉。若再需要時,考慮移植或重寫這段代碼。這里說的注釋掉代碼,包括用/**/和〃,還包括#ifO,#ifdefNEVER_DEFlNED等等。建議4.4.1正式交付給客戶的代碼不能包含TODO/TBD/FIXME注釋TODO/TBD注釋一般用來描述已知待改進、待補充的修改點FIXME注釋一般用來描述已知缺陷它們都應(yīng)該有統(tǒng)一風(fēng)格,方便文本搜索統(tǒng)一處理。如://TODOf<author-name>):補充XX處理//FIXME:XX缺陷5頭文件頭文件職責(zé)頭文件是模塊或文件的對外接口,頭文件的設(shè)計體現(xiàn)了大部分的系統(tǒng)設(shè)計。頭文件中適合放置接口的聲明,不適合放置實現(xiàn)(內(nèi)聯(lián)函數(shù)除外)。對于cpp文件中內(nèi)部才需要使用的函數(shù)、宏、枚舉、結(jié)構(gòu)定義等不要放在頭文件中。頭文件應(yīng)當職責(zé)單一。頭文件過于復(fù)雜,依賴過于復(fù)雜還是導(dǎo)致編譯時間過長的主要原因。建議5.1.1每一個.cpp文件應(yīng)有一個對應(yīng)的.h文件,用于聲明需要對外公開的類與接口通常情況下,每個.cpp文件都有一個相應(yīng)的.h,用于放置對外提供的函數(shù)聲明、宏定義、類型定義等。如果一個.cpp文件不需要對外公布任何接口,則其就不應(yīng)當存在。例外:程序的入口(如main函數(shù)所在的文件),單元測試代碼,動態(tài)庫代碼。示例://Foo.h#ifndefFOO_H#defineFOO二HclassFoo{public:Foo();voidFun();private:intvalue;};#endif//Foo.cpp#include"Foo.h"namespace{//Good:對內(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)致任何一個頭文件修改,都導(dǎo)致所有包含了a.h/b.h/c.h的代碼全部重新編譯一遍。而如果是單向依賴,如a.h包谷b.h,b.h包含c.h,而c.h不包含任何頭文件,則修改a.h不會導(dǎo)致包含了b.h/c.h的源代碼重新編譯。頭文件循環(huán)依賴直接體現(xiàn)了架構(gòu)設(shè)計上的不合理,可通過優(yōu)化架構(gòu)去避免。規(guī)則5.2.2禁止包含用不到的頭文件用不到的頭文件被包含的同時引入了不必要的依賴,增加了模塊或單元之間的耦合度,只要該頭文件被修改,代碼就要重新編譯。很多系統(tǒng)中頭文件包含關(guān)系復(fù)雜,開發(fā)人員為了省事起見,直接包含一切想到的頭文件,甚至發(fā)布了一個god.h,其中包含了所有頭文件,然后發(fā)布給各個項目組使用,這種只圖一時省事的做法,導(dǎo)致整個系統(tǒng)的編譯時間進一步惡化,并對后來人的維護造成了巨大的麻煩。規(guī)則5.2.3頭文件應(yīng)當自包含簡單的說,自包含就是任意一個頭文件均可獨立編譯。如果一個文件包含某個頭文件,還要包含另外一個頭文件才能工作的話,給這個頭文件的用戶增添不必要的負擔。示例:如果a.h不是自包含的,需要包含b.h才能編譯,會帶來的危害:每個使用a.h頭文件的.cpp文件,為了讓引入的a.h的內(nèi)容編譯通過,都要包含額外的頭文件b.ho額外的頭文件b.h必須在a.h之前進行包含,這在包含順序上產(chǎn)生了依賴。規(guī)則524頭文件必須編寫#define保護,防止重復(fù)包含為防止頭文件被重復(fù)包含,所有頭文件都應(yīng)當使用#define保護;不要使用#pragmaonce定義包含保護符時,應(yīng)該遵守如下規(guī)則:1)保護符使用唯一名稱;2)不要在受保護部分的前后放置代碼或者注釋,文件頭注釋除外。示例:假定VOS工程的timer模塊的timer.h,其目錄為VOS/include/timer/Timer.h,應(yīng)按如下方式保護:#ifndefVOS_INCLUDE_TIMERETIMER_H#defineVOS_INCLUDE_TIMER_TIMER__H#endif也可以不用像上面添加路徑,但是要保證當前工程內(nèi)宏是唯一的°#ifndefTIMER」#defineTIMER_H#endif建議5.2.1禁止通過聲明的方式引用外部函數(shù)接口、變量只能通過包含頭文件的方式使用其他模塊或文件提供的接口。通過extern聲明的方式使用外部函數(shù)接口、變量,容易在外部接口改變時可能導(dǎo)致聲明和定義不一致同時這種隱式依賴,容易導(dǎo)致架構(gòu)腐化。不符合規(guī)范的案例://a.cpp內(nèi)容externintFun();//Bad:通過extern的方式使用外部函數(shù)voidBar(){inti=Fun();},//b.cpp內(nèi)容intFun(){//Dosomething}應(yīng)該改為:〃a.cpp內(nèi)容#include"b.h" //Good:通過包含頭文件的方式使用其他,cpp提供的接UvoidBar(){inti=Fun();)//b.h內(nèi)容intFun();〃b.cpp內(nèi)容intFun(){//Dosomething)例外,有些場景需要引用其內(nèi)部函數(shù),但并不想侵入代碼時,可以extern聲明方式引用。如:針對某一內(nèi)部函數(shù)進行單元測試時,可以通過extern聲明來引用被測函數(shù);當需要對某一函數(shù)進行打樁、打補丁處理時,允許extern聲明該函數(shù)。規(guī)則5.2.5禁止在extern"C"中包含頭文件在extern"C”中包含頭文件,有可能會導(dǎo)致extern"C"嵌套,部分編譯器對extern"C"嵌套層次有限制,嵌套層次太多會編譯錯誤。在C,C++混合編程的情況下,在extern"C”中包含頭文件,可能會導(dǎo)致被包含頭文件的原有意圖遭到破壞,比如鏈接規(guī)范被不正確地更改。示例,存在a.h和b.h兩個頭文件://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ù)處理器展開b.h,將會得到extern"CM{voidFoo(int);voidB();)按照a.h作者的本意,函數(shù)Foo是一個C++自由函數(shù),其鏈接規(guī)范為"C++"。但在b.h中,由于#include"a.h"被放到了extern"C"的內(nèi)部,函數(shù)Foo的鏈接規(guī)范被不正確地更改了。例外:如果在C++編譯環(huán)境中,想引用純C的頭文件,這些C頭文件并沒有extern"C"修飾。非侵入式的做法是,在extern"C"中去包含C頭文件。建議5.2.2盡量避免使用前置聲明,而是通過#include來包含頭文件前置聲明(forwarddeclaration)是類、函數(shù)和模板的純粹聲明,沒伴隨著其定義。?優(yōu)點:前置聲明能夠節(jié)省編譯時間,多余的#include會迫使編譯器展開更多的文件,處理更多的輸入。前置聲明能夠節(jié)省不必要的重新編譯的時間。#include使代碼因為頭文件中無關(guān)的改動而被重新編譯多次。?缺點:.前置聲明隱藏了依賴關(guān)系,頭文件改動時,用戶的代碼會跳過必要的重新編譯過程。.前置聲明可能會被庫的后續(xù)更改所破壞。前置聲明函數(shù)或模板有時會妨礙頭文件開發(fā)者變動其API.例如擴大形參類型,加個自帶默認參數(shù)的模板形參等等。.前置聲明來自命名空間std::的symbol時,其行為未定義(在C++11標準規(guī)范中明確說明)。.前置聲明了不少來自頭文件的symbol時,就會比單單一行的include冗長。.僅僅為了能前置聲明而重構(gòu)代碼(比如用指針成員代替對象成員)會使代碼變得更慢更復(fù)雜。.很難判斷什么時候該用前置聲明,什么時候該用#include,某些場景下面前置聲明和#include互換以后會導(dǎo)致意想不到的結(jié)果。所以我們盡可能避免使用前置聲明,而是使用#include頭文件來保證依賴關(guān)系。建議523頭文件包含順序:首先是.cpp相應(yīng)的.h文件,其它頭文件按照穩(wěn)定度排序使用標準的頭文件包含順序可增強可讀性,避免隱藏依賴,建議按照穩(wěn)定度排序:cpp對應(yīng)的頭文件,C/C++標準庫,系統(tǒng)庫的.h,其他庫的.h,本項目內(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放在最前面可以保證當Foo.h遺漏某些必要的庫,或者有錯誤時,F(xiàn)oo.cpp的構(gòu)建會立刻中止,減少編譯時間。對于頭文件中包含順序也參照此建議。例外:平臺特定代碼需要條件編譯,這些代碼可以放到其它includes之后。#include"foo/public/FooServer.h"#include"base/Port.h"http://ForLANG_CXX11.#ifdefLANG_CXX11#include<initializer_list>#endif//LANG_CXX11~6作用域命名空間命名空間里的內(nèi)容不縮進。建議6.1.1對于cpp文件中不需要導(dǎo)出的變量,常量或者函數(shù),請使用匿名namespace封裝或者用static修飾在C++2003標準規(guī)范中,使用static修飾文件作用域的變量,函數(shù)等被標記為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來處理C++的作用域,而不需要同時使用static和namespace來管理。4.static修飾的函數(shù)不能用來實例化模板,而匿名namespace可以。但是不要在.h中使用中使用匿名namespace或者statico//Foo.cppnamespace{constintkMaxCount=20;voidInternalFun(){};)voidFoo::Fun(){inti=kMaxCount;InternalFun();)規(guī)則6.1.1不要在頭文件中或者#include之前使用using導(dǎo)入命名空間說明:使用using導(dǎo)入命名空間會影響后續(xù)代碼,易造成符號沖突,所以不要在頭文件以及源文件中的#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)用不明確}對于在頭文件中使用using導(dǎo)入單個符號或定義別名,允許在模塊自定義名字空間中使用,但禁止在全局名字空間中使用。//foo.h#include<fancy/string>usingfancy::string;//Bad,禁止向全局H'F空網(wǎng)導(dǎo)入符‘3namespacefoo{usingfancy::string;//Good,可以企模塊門定義M'F不間中與A彷causingMyVector=fancy::vector<int>;//Good,C++11可在H定義各‘產(chǎn)不間中定義別名}全局函數(shù)和靜態(tài)成員函數(shù)建議621優(yōu)先使用命名空間來管理全局函數(shù),如果和某個class有直接關(guān)系的,可以使用靜態(tài)成員函數(shù)說明:非成員函數(shù)放在名字空間內(nèi)可避免污染全局作用域,也不要用類+靜態(tài)成員方法來簡單管理全局函數(shù)。如果某個全局函數(shù)和某個類有緊密聯(lián)系,那么可以作為類的靜態(tài)成員函數(shù)。如果你需要定義一些全局函數(shù),給某個cpp文件使用,那么請使用匿名namespace來管理。namespacemynamespace{intAdd(inta,intb);}classFile{public:staticFileCreateTempFile(conststd::string&fileName);};全局常量和靜態(tài)成員常量建議631優(yōu)先使用命名空間來管理全局常量,如果和某個class有直接關(guān)系的,可以使用靜態(tài)成員常量說明:全局常量放在命名空間內(nèi)可避免污染全局作用域,也不要用類+靜態(tài)成員常量來簡單管理全局常量。如果某個全局常量和某個類有緊密聯(lián)系,那么可以作為類的靜態(tài)成員常量。如果你需要定義一些全局常量,只給某個cpp文件使用,那么請使用匿名namespace來管理。namespacemynamespace{constintkMaxSize=100;)classFile{public:staticconststd::stringkName;};全局變量建議641盡量避免使用全局變量,考慮使用單例模式說明:全局變量是可以修改和讀取的,那么這樣會導(dǎo)致業(yè)務(wù)代碼和這個全局變量產(chǎn)生數(shù)據(jù)耦合。intcounter=0;//a.cppcounter++;//b?cppcounter++;//c.cppcout<<counter<<endl;使用單實例模式classCounter{public:staticCountersGetlnstance(){staticCountercounter;returncounter;}//單實例實現(xià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();實現(xiàn)單例模式以后,實現(xiàn)了全局唯一一個實例,和全局變量同樣的效果,并且單實例提供了更好的封裝性。例外:有的時候全局變量的作用域僅僅是模塊內(nèi)部,這樣進程空間里面就會有多個全局變量實例,每個模塊持有一份,這種場景下是無法使用單例模式解決的。7類如果僅有數(shù)據(jù)成員,使用結(jié)構(gòu)體,其他使用類構(gòu)造,拷貝構(gòu)造,賦值和析構(gòu)函數(shù)構(gòu)造,拷貝,移動和析構(gòu)函數(shù)提供了對象的生命周期管理方法:-構(gòu)造函數(shù)(constructor):X()?拷貝構(gòu)造函數(shù)(copyconstructor):X(constX&)■拷貝賦值操作符(copyassignment):operator=(constX&)■移動構(gòu)造函數(shù)(moveconstructor):X(X&&)C++11以后提供?移動賦值操作符(moveassignment):operator=(X&&)C++12以后提臾?析構(gòu)函數(shù)(destructor):~X()規(guī)則7.1.1類的成員變量必須顯式初始化說明:如果類有成員變量,沒有定義構(gòu)造函數(shù),又沒有定義默認構(gòu)造函數(shù),編譯器將自動生成一個構(gòu)造函數(shù),但編譯器生成的構(gòu)造函數(shù)并不會對成員變量進行初始化,對象狀態(tài)處于一種不確定性。例外:-如果類的成員變量具有默認構(gòu)造函數(shù),那么可以不需要顯式初始化。示例:如下代碼沒有構(gòu)造函數(shù),私有數(shù)據(jù)成員無法初始化:classMessage{public:voidProcessOutMsg(){//???)private:unsignedintmsgID;unsignedintmsgLength;unsignedchar*msgBuffer;std::stringsomeldentifier;};Messagemessage;//message成員變心沒仃初始化message.ProcessOutNlsg(); //后續(xù)使用存;‘//因此,有必要定義默認構(gòu)造函數(shù),如下:classMessage{public:Message():msg1D(0),msgLength(O){voidProcessOutMsg(){//...)private:unsignedintmsgID;unsignedintmsgLength;unsignedchar*msgBuffer;std::stringsomeldentifier;///1■仃默認構(gòu)造函數(shù),不需要、泣式初始化};建議7.1.1成員變量優(yōu)先使用聲明時初始化(C++11)和構(gòu)造函數(shù)初始化列表初始化說明:C++11的聲明時初始化可以一目了然的看出成員初始值,應(yīng)當優(yōu)先使用。如果成員初始化值和構(gòu)造函數(shù)相關(guān),或者不支持C++11,則應(yīng)當優(yōu)先使用構(gòu)造函數(shù)初始化列表來初始化成員。相比起在構(gòu)造函數(shù)體中對成員賦值,初始化列表的代碼更簡潔,執(zhí)行性能更好,而且可以對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說明:單參數(shù)構(gòu)造函數(shù)如果沒有用explicit聲明,則會成為隱式轉(zhuǎn)換函數(shù)。示例:classFoo{public:explicitFoo(conststring&name):name(name){}private:stringname;};voidProcessFoo(constFoo&foo){}intmain(void){std::stringtest="test";ProcessFoo(test);//編譯僅逋過return0;)上面的代碼編譯不通過,因為ProcessFoo需要的參數(shù)是Foo類型,傳入的string類型不匹配。如果將Foo構(gòu)造函數(shù)的explicit關(guān)鍵字移除,那么調(diào)用ProcessFoo傳入的string就會觸發(fā)隱式轉(zhuǎn)換,生成一個臨時的Foo對象。往往這種隱式轉(zhuǎn)換是讓人迷惑的,并且容易隱藏Bug,得到了一個不期望的類型轉(zhuǎn)換。所以對于單參數(shù)的構(gòu)造函數(shù)是要求explicit聲明。規(guī)則7.1.3如果不需要拷貝構(gòu)造函數(shù)、賦值操作符/移動構(gòu)造函數(shù)、賦值操作符,請明確禁止說明:如果用戶不定義,編譯器默認會生成拷貝構(gòu)造函數(shù)和拷貝賦值操作符,移動構(gòu)造和移動賦值操作符(移動語義的函數(shù)C++11以后才有)。如果我們不要使用拷貝構(gòu)造函數(shù),或者賦值操作符,請明確拒絕:將拷貝構(gòu)造函數(shù)或者賦值操作符設(shè)置為private,并且不實現(xiàn):classFoo{private:Foo(constFoo&);Foo&operator=(constFoo&);};使用C++11提供的delete,請參見后面現(xiàn)代C++的相關(guān)章節(jié)。規(guī)則7.1.4拷貝構(gòu)造和拷貝賦值操作符應(yīng)該是成對出現(xiàn)或者禁止拷貝構(gòu)造函數(shù)和拷貝賦值操作符都是具有拷貝語義的,應(yīng)該同時出現(xiàn)或者禁止。//同時出現(xiàn)classFoo{public:Foo(constFoo&);Foo&operator=(constFoo&);}///同時default,C++11支持classFoo{public:Foo(constFoo&)=default;Foo&operator=(constFoo&)=default;};//同時禁止,C++11可以使用deleteclassFoo{private:Foo(constFoo&);Foo&operator=(constFoo&);};規(guī)則7.1.5移動構(gòu)造和移動賦值操作符應(yīng)該是成對出現(xiàn)或者禁止在C++11中增加了move操作,如果需要某個類支持移動操作,那么需要實現(xiàn)移動構(gòu)造和移動賦值操作符。移動構(gòu)造函數(shù)和移動賦值操作符都是具有移動語義的,應(yīng)該同時出現(xiàn)或者禁?止。//同時出現(xiàn)classFoo{public:Foo(Foo&&);Foo&operator=(Foo&&);//同時defauLt,C++11支持classFoo{public:Foo(Foo&&)=default;Foo&operator=(Foo&&)=default;};//同時禁止,使用C++11的deleteclassFoo{public:Foo(Foo&&)=delete;Foo&operator=(Foo&&)=delete;};規(guī)則7.1.6禁止在構(gòu)造函數(shù)和析構(gòu)函數(shù)中調(diào)用虛函數(shù)說明:在構(gòu)造函數(shù)和析構(gòu)函數(shù)中調(diào)用當前對象的虛函數(shù),會導(dǎo)致未實現(xiàn)多態(tài)的行為在C++中,一個基類一次只構(gòu)造一個完整的對象。示例:類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();};當執(zhí)行如下語句:Subsub;會先執(zhí)行Sub的構(gòu)造函數(shù),但首先調(diào)用Base的構(gòu)造函數(shù),由于Base的構(gòu)造函數(shù)調(diào)用虛函數(shù)Log,此時Log還是基類的版本,只有基類構(gòu)造完成后,才會完成派生類的構(gòu)造,從而導(dǎo)致未實現(xiàn)多態(tài)的行為。同樣的道理也適用于析構(gòu)函數(shù)。繼承規(guī)則7.2.1基類的析構(gòu)函數(shù)應(yīng)該聲明為virtual說明:只有基類析構(gòu)函數(shù)是virtual,通過多態(tài)調(diào)用的時候才能保證派生類的析構(gòu)函數(shù)被調(diào)用。示例:基類的析構(gòu)函數(shù)沒有聲明為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ù)沒有聲明為virtual,當對象被銷毀時,只會調(diào)用基類的析構(gòu)函數(shù),不會調(diào)用派生類Sub的析構(gòu)函數(shù),導(dǎo)致內(nèi)存泄漏。規(guī)則7.2.2禁止虛函數(shù)使用缺省參數(shù)值說明:在C++中,虛函數(shù)是動態(tài)綁定的,但函數(shù)的缺省參數(shù)卻是在編譯時就靜態(tài)綁定的。這意味著你最終執(zhí)行的函數(shù)是一個定義在派生類,但使用了基類中的缺省參數(shù)值的虛函數(shù)。為了避免虛函數(shù)重載時,因參數(shù)聲明不一致給使用者帶來的困惑和由此導(dǎo)致的問題,規(guī)定所有虛函數(shù)均不允許聲明缺省參數(shù)值。示例:虛函數(shù)display缺省參數(shù)值text是由編譯時刻決定的,而非運行時刻,沒有達到多態(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. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年KTV會員管理系統(tǒng)采購合同3篇
- 2024適用復(fù)雜情況磚渣產(chǎn)品采購合同2篇
- 2025年南京市共有產(chǎn)權(quán)住房買賣合同(公平共享版)3篇
- 2024高端裝備制造技術(shù)轉(zhuǎn)讓合同標的及技術(shù)培訓(xùn)協(xié)議
- 2024綠化工程節(jié)水灌溉系統(tǒng)安裝與維護勞務(wù)分包合同書2篇
- 多媒體技術(shù)基礎(chǔ)(山東聯(lián)盟)知到智慧樹章節(jié)測試課后答案2024年秋青島恒星科技學(xué)院
- 2024男方離婚協(xié)議書:包含贍養(yǎng)費及子女教育金支付合同3篇
- 2024甲乙雙方汽車租賃保險責(zé)任劃分合同
- 稅務(wù)知識培訓(xùn)課件
- 博物館管道網(wǎng)絡(luò)協(xié)議
- 英語八年級英語上冊閱讀理解100及答案經(jīng)典
- 兒科佝僂病中醫(yī)診療規(guī)范診療指南2023版
- 糖尿病高血壓護理查房
- 維修工作流程圖
- 小學(xué)綜合實踐活動試卷考試質(zhì)量分析
- 水泥采購?fù)稑朔桨福夹g(shù)標)
- 鋁型材采購技術(shù)規(guī)范
- 物業(yè)投訴處理培訓(xùn)課件
- 《春秋》導(dǎo)讀學(xué)習(xí)通章節(jié)答案期末考試題庫2023年
- 物流無人機垂直起降場選址與建設(shè)規(guī)范(征求意見稿)
- 2023年湖南成人學(xué)位英語考試真題
評論
0/150
提交評論