



版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
谷歌C++編程風(fēng)格指南[版本:3.180]BenjyWeinberger
CraigSilverstein
GregoryEitzmann
MarkMentovai
TashanaLandray翻譯:鄭州大學(xué)趙峻(僅供參考)目錄TOC\o"1-5"\h\zー、 背景 1二、 正文 1\o"CurrentDocument"頭文件(HeaderFiles) 1\o"CurrentDocument"1#define保護(hù)(#includeguard) 1\o"CurrentDocument"頭文件的依賴關(guān)系(HeaderFileDependencies) 2\o"CurrentDocument"內(nèi)聯(lián)函數(shù)(InlineFunctions) 2\o"CurrentDocument"內(nèi)聯(lián)頭文件(The-inl.hFiles) 3\o"CurrentDocument"5 函數(shù)參數(shù)次序(FunctionParameterOrdering) 3\o"CurrentDocument"包含的命名和次序(NamesandOrderofincIudes) 3作用域(Scoping) 41名稱空間(Namespaces) 4\o"CurrentDocument"2類嵌套(NestedClasses) 73 外部函數(shù)、靜態(tài)成員函數(shù)和全局函數(shù)(Nonmember,StaticMember,and\o"CurrentDocument"GIobaIFunctions) 7\o"CurrentDocument"4 局部變量(LocalVariables) 8\o"CurrentDocument"5 靜態(tài)變量和全局變量(StaticandGlobalVariables) 8\o"CurrentDocument"類(Classes) 9\o"CurrentDocument"I 在構(gòu)造函數(shù)中完成工作(DoingWorkinConstructors) 9\o"CurrentDocument"2 默認(rèn)構(gòu)造函數(shù)(DefaultConstructor) 9\o"CurrentDocument"3 顯式構(gòu)造函數(shù)(ExplicitConstructors) 10\o"CurrentDocument"4 復(fù)制構(gòu)造函數(shù)(CopyConstructos) 10\o"CurrentDocument"5 結(jié)構(gòu)體與類(StructsvsClasses) 11\o"CurrentDocument"6 繼承(Inheritance) 11\o"CurrentDocument"7 多重繼承(MuItipIeInheritance) 12\o"CurrentDocument"接口(Interface) 12\o"CurrentDocument"9 運(yùn)算符重載(OperatorOverloading) 12\o"CurrentDocument"10 訪問控制(AccessControl) 13\o"CurrentDocument"11 聲明次序(DeclarationOrder) 13\o"CurrentDocument"12 定義簡短函數(shù)(WriteShortFunctions) 14\o"CurrentDocument"谷歌經(jīng)驗(yàn)技巧(Google-SpecificMagic) 14\o"CurrentDocument".1智能指針(SmartPointers) 14\o"CurrentDocument".2CPPIint 14其他C++特性(OtherC++Fetures) 151 弓I用參數(shù)(ReferenceArguments) 15\o"CurrentDocument"2 函數(shù)重載(FunctionOverloading) 15\o"CurrentDocument"3 默認(rèn)參數(shù)(DefaultArguments) 164 可變長度數(shù)組和內(nèi)存申請(Variable-LengthArraysandaIIoca())16TOC\o"1-5"\h\z5?5 "〇エノし(irIends) 16\o"CurrentDocument"6 異常處理(Excpetions) 17\o"CurrentDocument"7 運(yùn)行時(shí)類型信息(Run-TimeTypeInformation,RTTI) 18\o"CurrentDocument"8 類型轉(zhuǎn)換(Casting) 18\o"CurrentDocument"9 流(Streams) 18\o"CurrentDocument"10前置自增和前置自減(PreincrementandPredecrement) 19\o"CurrentDocument"11 const修飾符的使用(Useofconst) 20\o"CurrentDocument"12整型類型(IntegerTypes) 20\o"CurrentDocument"13 64位兼容性(64-bitPortabiIity) 21\o"CurrentDocument"14預(yù)處理宏(PreprocessorMacros) 22\o"CurrentDocument"15 〇和空(0andNULL) 23\o"CurrentDocument"16存儲(chǔ)容量運(yùn)算符(sizeof) 23\o"CurrentDocument"17增強(qiáng)庫(Boost) 23\o"CurrentDocument"18 C++Ox庫 24\o"CurrentDocument"命名(Naming) 24\o"CurrentDocument"1 一般命名規(guī)則(GeneralNamingRules) 242 ッエFFロ卩(lIIeNames)??????????????????????????????????????????????????????????????????????????????25\o"CurrentDocument"3 類型命名(TypeNames) 25\o"CurrentDocument"4 變量命名(VariableNames) 26\o"CurrentDocument"5 常重命名(ConstantNames) 26\o"CurrentDocument"6 函數(shù)命名(FunctionNames): 27\o"CurrentDocument"7 名稱空間的命名(NamespaceNames) 27\o"CurrentDocument"8 枚舉器的命名(EnumeratorNames) 27\o"CurrentDocument"9 な命名(MacroNames) 27\o"CurrentDocument"10命名規(guī)則的例外情況(ExceptionstoNamingRules) 28\o"CurrentDocument"7B注釋(Comments) 28\o"CurrentDocument"1 注釋風(fēng)格(CommentStyIe) 28\o"CurrentDocument"2文件注釋(FiIeComments) 28\o"CurrentDocument"3 類注釋(ClassComments) 29\o"CurrentDocument"4 函數(shù)注釋(FunctionComments) 29\o"CurrentDocument"5 變量注釋(VariableComments) 30\o"CurrentDocument"以空、真/假、數(shù)字作為參數(shù)(NULL、true/false、1,2,3…) 30\o"CurrentDocument"7 注釋的標(biāo)點(diǎn)、拼寫和語法(Punctuation,SpelIingandGramma) 31\o"CurrentDocument"8TODO注釋(TODOComments) 31\o"CurrentDocument"9 廢棄性注釋(DeprecationComments) 32\o"CurrentDocument"編碼格式(Formatting) 32\o"CurrentDocument"8.1 行長度(LineLength) 32\o"CurrentDocument"8.2 非ASCII碼字符(Non-ASCIICharacters) 33\o"CurrentDocument"8.3 窗格還是制表符(Spacesvs.Tabs) 33\o"CurrentDocument"8.4 函數(shù)聲明與定義(FunctionDeclarationsandDefinitions) 33\o"CurrentDocument"8.5 函數(shù)調(diào)用(FunctionCalls) 34\o"CurrentDocument"8.6 條件語句(ConditonaIs) 34\o"CurrentDocument"8.7 循環(huán)和多分支語句(LoopsandSwitchStatements) 35\o"CurrentDocument"8.8 指針與引用表達(dá)式(PointerandReferenceExpressions) 36\o"CurrentDocument"8.9 布刁く表達(dá)式(BooleanExpressions) 36\o"CurrentDocument"8.10 返回值(ReturnVaIues) 37\o"CurrentDocument"8.11 變量和數(shù)組的初始化(VariableandArray Initial ization) 37\o"CurrentDocument"8.12 預(yù)處理指令(PreprocessorDirectives) 37\o"CurrentDocument"8.13 類格式(ClassFormat) 37\o"CurrentDocument"8.14 構(gòu)造函數(shù)初始化列表(ConstructorInitializer Lists) 38\o"CurrentDocument"8.15名稱空間格式(NamespaceFormatting) 38\o"CurrentDocument"8.16 水平空白(HorizontaIWhitespace) 38\o"CurrentDocument"8.17 垂直空白(VerticalWhitespace) 39\o"CurrentDocument"9.本規(guī)則的例外情況(ExceptonstotheRules) 39\o"CurrentDocument"1現(xiàn)存不一致代碼(ExistingNon-conformant Code) 40\o"CurrentDocument"2Windows代百馬(WindowsCode) 40\o"CurrentDocument"結(jié)束語(PartingWords) 40ー、冃景C++是很多谷歌開源項(xiàng)目的主開發(fā)語言。正如每一個(gè)C++程序員所知,C++擁有很多強(qiáng)大的特性,但與此同時(shí)帶也來了很大的復(fù)雜性,這就導(dǎo)致C++代碼極易出現(xiàn)問題且很難閱讀和維護(hù)。本指南的目標(biāo)就是根據(jù)大量經(jīng)驗(yàn),描述C++編碼過程中建議和不提倡的編碼規(guī)則,以便控制其復(fù)雜性。這些規(guī)則的運(yùn)用使你在高效而創(chuàng)造性地使用C++的同時(shí),又能很好是保持代碼的可維護(hù)性。風(fēng)格,或者說可讀性,也就是我們C++編碼的慣例。使用“風(fēng)格”似乎有些用詞不當(dāng),畢竟慣例遠(yuǎn)遠(yuǎn)不止源文件的格式。保持代碼可維護(hù)性的ー種方法就是強(qiáng)調(diào)編碼的一致性。程序員能快速查看并理解其他程序員的代碼是很重要的。維持ー種統(tǒng)?風(fēng)格并遵照慣例意味著我們可以簡單地使用'’模式匹配”來推斷大量符號(hào)的含義和其不變性。建立通用、慣例和模式使代碼更容易理解。也許有時(shí)有必要改變我們的ー貫風(fēng)格,盡管如此,我們還是盡量保持?致性的好。本指南的另ー個(gè)論點(diǎn)是C++特性臃腫。C++是ー門包含有大量高級(jí)特性的巨型語言。某些情況下,我們限制甚至禁止使用某些特性。我們這樣做僅僅是想使代碼簡單并避免由這些特性引起的大量常見錯(cuò)誤和問題。本指南將列出這些特性并指出為什么限制它們的使用。谷歌所有開源項(xiàng)口都符合本指南中的要求。注意:本指南不是C++入門指導(dǎo),我們假設(shè)讀者對C++已經(jīng)非常熟悉。二、正文.頭文件(HeaderFiles)通常,每個(gè)源文件(.ccfile)文件都應(yīng)該有一個(gè)與之關(guān)聯(lián)的頭文件(.hfile)文件。當(dāng)然,有一些常見例外,比如只有一個(gè)main()函數(shù)的單元測試和小源文件。正確地使用頭文件可以使代碼的可讀性、體積和性能有一個(gè)大的提升。通過下面的規(guī)則,你將了解大量使用頭文件的缺陷。1#define保護(hù)(#includeguard)每個(gè)頭文件都應(yīng)該有一個(gè)#define保護(hù)以防止它被多次包含。而且符號(hào)的命名最好是以下形式:<PROJECT>_<PATH>_<FILE>_H_?盡管保護(hù)各不相同,但它們應(yīng)該以完整的項(xiàng)目資源目錄為基礎(chǔ)。比如foo項(xiàng)目中的foo/src/bar/baz.h文件應(yīng)該這樣保護(hù):#ifndefFOO_BAR_BAZ_H_?defineFOO_BAR_BAZ_H一#endif//FOO_BAR_BAZ_H_1.2頭文件的依賴關(guān)系(HeaderFileDependencies)當(dāng)ー個(gè)前置聲明足夠時(shí),不要使用#include。當(dāng)你包含ー個(gè)頭文件時(shí),便引入了頭文件依賴關(guān)系,每當(dāng)這個(gè)頭文件改變時(shí),代碼都需要重新編譯。而且,如果這些頭文件還包含其他頭文件,任何改變都將導(dǎo)致包含該頭文件的代碼重新編譯。因此,我們提倡最小化包含,尤其是頭文件包含頭文件的情形。你可以通過使用前置聲明來明顯地減少自定義頭文件包含其他頭文件的數(shù)量。比如,如果你的頭文件使用File類而不需要知道FUe類的聲明,就可以前置聲明File類,而不需要使用#include''file/base/file.h,z如何在不訪問其定義的情況下使用類Foo呢?.聲明數(shù)據(jù)成員Foo?或者Foo&;.聲明以Fo。為參數(shù),和/或返回值的函數(shù)。(有一?個(gè)例外:如果參數(shù)是Fo。或者constFoo&,有一個(gè)隱式單參數(shù)構(gòu)造函數(shù),這種情況下我們需要引入完全定義來支持自動(dòng)類型轉(zhuǎn)換)。.聲明靜態(tài)的F。。數(shù)據(jù)成員,這是因?yàn)殪o態(tài)數(shù)據(jù)成員在類定義外定義。另一方面,如果你的類是F。。的子類或者包括ー個(gè)F。。類型的數(shù)據(jù)成員,你必須包含該頭文件。有時(shí)使用指針成員(或使用更好的智能指針)代替對象成員更合理。然而,這會(huì)使代碼的可讀性復(fù)雜化并影響性能,所以,當(dāng)僅以最小化包含文件數(shù)為目的時(shí),盡量不要這么做。通常,.cc文件需要知道其所用類的具體實(shí)現(xiàn),因此需要包含ー些頭文件。注意:如果你在?cc文件中使用Foo標(biāo)識(shí)符,你應(yīng)該自己定義Foo,要么通過ー個(gè)#include命令,要么通過ー個(gè)前置聲明。然而有一個(gè)例外:如果在myfile.cc中使用Foo?在myfile.h中#include(或者前置聲明)Foo也可以。內(nèi)聯(lián)函數(shù)(InlineFunctions)當(dāng)函數(shù)很小(比如10行或者更少)時(shí)オ定義函數(shù)為內(nèi)聯(lián)。內(nèi)聯(lián)定義:通過內(nèi)聯(lián),編譯器會(huì)在調(diào)用處將函數(shù)展開為代碼,而不通過通常的函數(shù)調(diào)用機(jī)制。利:由于內(nèi)聯(lián)函數(shù)通常很小,所以可產(chǎn)生更高效的目標(biāo)代碼。盡量內(nèi)聯(lián)類成員訪問和修改函數(shù)(gettersandsetter)和其他ー些簡短,對性能要求關(guān)鍵的函數(shù)。弊:過量使用內(nèi)聯(lián)將使程序性能受損。視其規(guī)模,內(nèi)聯(lián)ー個(gè)函數(shù)即可能使其代碼量增加,也可能使其代碼量減少。內(nèi)聯(lián)ー個(gè)小的類成員訪問函數(shù)(getter)通常會(huì)減小其代碼量,然而內(nèi)聯(lián)一個(gè)很大的函數(shù)則會(huì)顯著增加其代碼量。現(xiàn)代處理器由于使用了指令緩存,在運(yùn)行小規(guī)模代碼時(shí)將更快。結(jié)論:ー個(gè)好的經(jīng)驗(yàn)法則是,當(dāng)ー個(gè)函數(shù)超過10行時(shí),不要使用內(nèi)聯(lián)。注意析構(gòu)函數(shù),它們常常因不明顯的成員或者基類析構(gòu)調(diào)用比看起來的規(guī)模大。另ー個(gè)有用的經(jīng)驗(yàn)法則是,內(nèi)聯(lián)ー個(gè)包含有循環(huán)或者開關(guān)指令的函數(shù)常常是不劃算的(除非在極特殊情況オ會(huì)執(zhí)行這些循環(huán)和開關(guān)指令)。知道這一點(diǎn)很重要:ー些函數(shù)常常不能被內(nèi)聯(lián),即使被聲明成這樣,比如虛函數(shù)和遞歸函數(shù)常常不能被內(nèi)聯(lián)。內(nèi)聯(lián)ー個(gè)虛函數(shù)的主要原因是將它們的定義放在類定義中,或者在類定義中說明它們的行為,比如類成員訪問器和修改器。內(nèi)聯(lián)頭文件(The-inI.hFiles)如有必要,你可以使用-ini前綴來定義復(fù)雜的內(nèi)聯(lián)函數(shù)。應(yīng)該在頭文件中定義內(nèi)聯(lián)函數(shù),這樣,編譯器才能將其代碼復(fù)制到調(diào)用處。然而,實(shí)現(xiàn)代碼通常應(yīng)該包含在?cc文件:頭文件一般不包含實(shí)現(xiàn)代碼,除非為了改善可讀性或者出于性能的考慮。如果一個(gè)內(nèi)聯(lián)函數(shù)的定義很短,包含很少(如果有的話)的邏輯判斷,則應(yīng)在頭文件中實(shí)現(xiàn)它們。比如,類成員訪問器和修改器應(yīng)該在類定義中實(shí)現(xiàn)。為方便定義和調(diào)用,ー些復(fù)雜的內(nèi)聯(lián)函數(shù)也可在頭文件中定義,當(dāng)這些函數(shù)使頭文件變得太過臃腫時(shí),增加一個(gè)ini.h文件來單獨(dú)定義它們。ini.h頭文件使內(nèi)聯(lián)函數(shù)的實(shí)現(xiàn)和類的定義分開,同時(shí)又允許在需要的時(shí)候包含其實(shí)現(xiàn)。-inl.h的另一個(gè)用途是定義函數(shù)模板。這將使你的函數(shù)模板定義可讀性更好。另外,不要忘了,-ini.h文件也是需要#define保護(hù)的。函數(shù)參數(shù)次序(FunctionParameterOrdering)當(dāng)定義ー個(gè)函數(shù)時(shí),其參數(shù)次序應(yīng)該是:輸入、輸出。ー個(gè)C/C++函數(shù)的參數(shù)無外乎輸入、輸出或者兼具兩者。輸入?yún)?shù)常常是數(shù)值或者常引用,而輸出或者出入?yún)?shù)則是非const指針。定義參數(shù)次序時(shí),通常將所有輸入?yún)?shù)置于輸出參數(shù)之前。尤其注意,不要簡單在把新參數(shù)加在參數(shù)列表最后,要將新輸入?yún)?shù)置于所有輸出參數(shù)之前。然而,這不是ー個(gè)一成不變的規(guī)則,比如出入?yún)?shù)(一般是類或結(jié)構(gòu)體)。包含的命名和次序(NamesandOrderofincIudes)為增加可讀性并避免隱蔽的依賴關(guān)系,請使用標(biāo)準(zhǔn)的包含次序:c庫、C++庫、其他庫頭文件、自定義頭文件。所有項(xiàng)目的頭文件應(yīng)該按其資源目錄降序排列,且不要使用Unix的簡略目錄表示法(.表示當(dāng)前目錄,..表示父目錄)。比如,google-awesome-project/src/base/logging.h丿セ該區(qū)樣被自含:?include''base/logging.h/z如果dir/foo.cc的主要功能是實(shí)現(xiàn)和測試dir2/foo2.h中的內(nèi)容,可以這樣安排:dir2/foo2.hC系統(tǒng)文件C++系統(tǒng)文件其他庫頭文件本項(xiàng)目頭文件這種首選次序可以減少隱蔽的依賴關(guān)系。我們希望每ー個(gè)頭文件都可以獨(dú)立編譯。最簡單的方法就是確保它們在.CC文件中是第一次被包含。dir/foo.cc和dir2/foo2.h通常在ー個(gè)目錄中(比如base/hasictypes_test.ccbase/basictypes.h)?當(dāng)然,也可以在不同目錄中。各部分內(nèi),最好按字母表順序排列。比如google-awesome-project/src/f00/internal/fooserver.cc可以以祥排序包含文件:#include''foo/public/fooserver,h” //首選位置?include<sys/types.h>?include<unistd.h>?include<hash_map>?include<vector>include''base/basictypes.hz,include''base/commandlineflags.h"?include''foo/public/bar.hz,.作用域(Scoping).1名稱空間(Namespaces)在.cc文件中,通常鼓勵(lì)匿名名稱空間。當(dāng)需要命名ー個(gè)名稱空間時(shí),可以基于項(xiàng)目或者它的路徑。不要使用using指令。定義:名稱空間將全局作用域分成獨(dú)立且命名的子作用域,因此對于防止命名沖突很有幫助。利:名稱空間提供了一條補(bǔ)充由類提供的分層命名的軸線。舉個(gè)例子,如果兩個(gè)不同的項(xiàng)目有一個(gè)同名的全局類F。。,這些符號(hào)在編譯或者運(yùn)行時(shí)就有可能沖突。如果把它們的代碼放在各自項(xiàng)目的名稱空間中,projectl:Foo和project2::Foo就是不同的符號(hào),也不會(huì)發(fā)生沖突了。弊:名稱空間可能引起混亂,因?yàn)樗峁┝祟~外的分層命名軸線以補(bǔ)充由類提供的另ー條。在頭文件中使用匿名名稱空間很容易違反C++的一次定義規(guī)則(OneDefinitionRule,ODR)結(jié)論:請按下面的規(guī)則使用名稱空間:?匿名名稱空間:.在C++中,匿名名稱空間是允許甚至是被鼓勵(lì)的:在.cc文件中,為防止運(yùn)行時(shí)命名沖突:
namespace{/Znamespace{/Z這通常包含在ー個(gè).cc文件中//名稱空間的內(nèi)容不應(yīng)該縮進(jìn)enum{kUnused,kEOF,kError}//常見語句boolAtEof(){returnpos==kEOF;レ/使用我們的名稱空間EOF.}//namespace然爾,與特定類相關(guān)的文件作用域聲明可能在類中被作為類型、靜態(tài)數(shù)據(jù)成員或者靜態(tài)成員函數(shù)而不是在匿名名稱空間中定義。通常使用注釋//namespace來結(jié)束ー個(gè)匿名空間的定義。.不要在頭文件中使用匿名名稱空間。?命名名稱空間:命名名稱空間的使用規(guī)則如下:.名稱空間應(yīng)該包圍#include后的所有代碼,包括gflags(ー種調(diào)試工具,由微軟發(fā)布,用于檢測內(nèi)存泄漏)定義、聲明和來自其他名稱空間的前置類聲明。/Z位于頭文件namespacemynamespace{/Z所有的聲明都應(yīng)該在名稱空間作用域內(nèi)//注意這里沒有縮進(jìn)classMyClass{public:voidFoo(););}//namespacemynamespace/Z位于.cc文件Namespacemynamespace{/Z所有函數(shù)的定義位于名稱空間的作用域內(nèi)voidMyClass::Foo(){}}//namespacemynamespace通常.cc文件可能包括更復(fù)雜的代碼,比如從其他名稱空間引入的類引用。?include,、a.h〃DEFINEbool(someflag,false,z/dummyflag");classC;/Z類C的全局前置聲明namespaceb{...codeforb.../Z代碼應(yīng)該左對齊}//namespaceb.不要在std名稱空間中聲明任何東西,甚至是標(biāo)準(zhǔn)庫類的前置聲明。在std名稱空間中聲明名稱是未確定行為或者不可移植的。要從標(biāo)準(zhǔn)庫中聲明名稱,包含相應(yīng)的頭文件即可。.不可以使用using指令(u5/7)g?d/「ectルeノ將一個(gè)名稱空間中的所有名稱引入。//禁止ー這有可能引起名稱空間沖突usingnamespacef〇〇;.在源文件、頭文件中的函數(shù)、方法或者類中可以使用using聲明體力g-dec/o「〇萬〇用//在源文件中允許/Z但在頭文件中必須在函數(shù)、方法或者類中using::foo::bar;.名稱空間別名可在源文件中、頭文件的全局名稱空間中,或者函數(shù)和方法中任意使用。/Z源文件中常用名稱的受限訪問namespacefbz=::foo::bar::baz;/Z頭文件中常用名稱的受限訪問Namespacelibrarian{//下面的別名在所有包含此頭文件的都可使用(限于!ibrarian//名稱空間)//因此,別名應(yīng)該與項(xiàng)H保持一致性namespacepd_s=::pipeline_diagnostics::sidetable;inlinevoidmy_inline_function(){/Z位于函數(shù)或者方法內(nèi)的名稱空間別名namespacefbz=::foo::bar::baz;)}//namespacelibrarian注意:頭文件中的別名在所有包含它的文件中都可見,所以公共頭文件(其他項(xiàng)目可以使用)和那些間接被它們包含的頭文件,應(yīng)該避免定義別名,畢竟,通常的目標(biāo)是盡量保持公共API簡小。2.2類嵌套(NestedCIasses)盡管你可以使用公共的嵌套類來提供接口,但請盡量使用名稱空間來保持聲明的局部性。定義:類可以在其內(nèi)部定義另ー個(gè)類(常稱為成員類(MemberC/ass))。classFoo{private://Baエ是ー個(gè)成員類,嵌套定義于Fo。classBar{)利:當(dāng)嵌套類僅被外部類使用時(shí),這種定義方法很有用,這樣可以避免在外部定義類引起的作用域混亂。可以前置聲明嵌套類而將其實(shí)現(xiàn)放在源文件中來避免其定義出現(xiàn)在外部類的聲明中,畢竟嵌套類的定義只與其實(shí)現(xiàn)相關(guān)。弊:嵌套類僅可被前置聲明于外部類內(nèi)部。這樣,任何操縱F。。::Bar?指針的頭文件都必須包含F(xiàn)oo類的全部聲明。結(jié)論:除非嵌套類是接口的一部分(比如一個(gè)包括若干供選擇方法的類),否則不要將其定義為公共成員。2.3外部函數(shù)、靜態(tài)成員函數(shù)和全局函數(shù)(Nonmember,StaticMember,andGIobaIFunctions)盡量使用名稱空間內(nèi)的外部函數(shù)或者靜態(tài)成員函數(shù),盡量少使用甚至不使用全局函數(shù)。利:一些情況下,外部和靜態(tài)成員函數(shù)很有用。將一個(gè)外部函數(shù)放在ー個(gè)名稱空間內(nèi)可以防止全局名稱空間混亂。弊:外部函數(shù)和靜態(tài)成員函數(shù)作為ー個(gè)新類的成員可能更好,尤其是當(dāng)它們訪問外部資源或者有很大的相關(guān)性時(shí)。結(jié)論:有時(shí),定義ー個(gè)與對象無關(guān)的函數(shù)(即外部函數(shù)或者靜態(tài)成員函數(shù))是很有用甚至必要的。外部函數(shù)不應(yīng)該依賴全局變量,而且應(yīng)該定義在ー個(gè)名稱空間內(nèi)。與類不同,名稱空間在定義函數(shù)的同時(shí)可以共享共變量,而定義靜態(tài)成員函數(shù)的類則不允許共享其靜態(tài)數(shù)據(jù)。共同為某個(gè)類提供ー項(xiàng)功能而在同一個(gè)編譯單元內(nèi)定義的函數(shù)可以導(dǎo)致不必要的耦合和連接時(shí)相關(guān)性,當(dāng)其他編譯單元宜接調(diào)用時(shí),靜態(tài)成員函數(shù)尤其如此。這時(shí),可以考慮提取出ー個(gè)新類,或者將這些函數(shù)放在不同庫中的不同名稱空間內(nèi)。如果必須定義ー個(gè)只在其源文件內(nèi)使用的外部函數(shù),可以使用匿名名稱空間或者靜態(tài)鏈接(比如staticintFoo〇{...})來限制其作用域。4局部變量(LocalVariabIes)盡量縮小函數(shù)內(nèi)部變量的作用域并在定義它們時(shí)執(zhí)行初始化。雖然C++允許在函數(shù)內(nèi)部任意定義變量,但我們建議盡量縮小其作用域,盡量在需要時(shí)ォ定義。這樣方便讀者找到變量聲明并知道其類型及初始化情況。特別地,初始化應(yīng)該代替聲明和賦值。例如:inti;i=f();/Z糟糕ー初始化和聲明分開intj=g();//提倡一聲明的同時(shí)初始化注意:GCC正確地實(shí)現(xiàn)了for循環(huán)(for(inti=0;i<10;i++),i的作用域僅限于for循環(huán)體內(nèi),所以在同一作用域內(nèi),你可以重復(fù)使用i。if和while語句中的局部變量也ー樣。比如:while(constchar*p=strchr(str,,/,))str=p+1;警告:如果變量是一個(gè)類,它的構(gòu)造函數(shù)將在每次進(jìn)入作用域時(shí)被調(diào)用并創(chuàng)建實(shí)例,它的析構(gòu)函數(shù)也將在每次退出作用域時(shí)被調(diào)用。/Z低效的實(shí)現(xiàn)for(inti=0;i<1000000;++i){Foof;/Z這個(gè)類的構(gòu)造函數(shù)和析構(gòu)函數(shù)將被調(diào)用1000000f.DoSomething();)將一個(gè)在循環(huán)內(nèi)使用的變量定義在循環(huán)外將更高效:foof;/Z這個(gè)類的構(gòu)造函數(shù)和析構(gòu)函數(shù)僅被調(diào)用ー次for(inti=0;i<1000000;++i){f?DoSomething();5靜態(tài)變量和全局變量(StaticandGlobaIVariables)類的靜態(tài)和全局使用是被禁止的:這些變量可能由于不確定的構(gòu)造和析構(gòu)次序而引起難于發(fā)現(xiàn)的缺陷。有靜態(tài)存儲(chǔ)期間,包括全局變量、靜態(tài)變量、靜態(tài)類成員和靜態(tài)函數(shù)變量的類必須是老式平坦數(shù)據(jù)類型(POD),包括int,char,float,或者指針,或者數(shù)組'結(jié)構(gòu)體。在C++中,類對靜態(tài)成員的構(gòu)造和初始化僅有部分規(guī)定,甚至每次編譯都有可能不同,這很容易導(dǎo)致難以察覺的缺陷。因此,除了不允許定義全局類變量外,我們也不允許使用一個(gè)函數(shù)來初始化一個(gè)全局變量,除非這個(gè)函數(shù)(比如geteny(),getpid())不依賴任何其他全局變量。同樣,類的析構(gòu)順序恰恰和其構(gòu)造順序相反。由于構(gòu)造順序尚且不確定,何況析構(gòu)順序呢。舉個(gè)例子,ー個(gè)程序行將結(jié)束的時(shí)候,ー個(gè)靜態(tài)變量已經(jīng)被銷毀,但代碼仍在運(yùn)行(也許是另ー個(gè)線程)并試圖訪問它,但失敗了。或者ー個(gè)靜態(tài)string變量的析構(gòu)可能在另ー個(gè)包含有該變量的引用的變量析構(gòu)中被執(zhí)行。因此,只允許僅僅包含POD數(shù)據(jù)的靜態(tài)變量。顯然vector(代替C數(shù)組),或者string(用constchar口實(shí)現(xiàn))都不行。如果你確實(shí)需要定義ー個(gè)靜態(tài)或者全局類變量,考慮從主函數(shù)或者pthread_onece〇函數(shù)初始化一個(gè)指針(永遠(yuǎn)不會(huì)被銷毀)。注意,這個(gè)指針一定是ー個(gè)普通指針而不是智能指針,因?yàn)橹悄苤羔樀奈鰳?gòu)將面臨析構(gòu)次序的問題。類(Classes)類是C++代碼的基本單位。自然,其使用也是廣泛的。這一部分將告訴你在使用類時(shí)應(yīng)該和不應(yīng)該做的。1在構(gòu)造函數(shù)中完成工作(DoingWorkinConstructors)通常認(rèn)為構(gòu)造函數(shù)僅僅完成成員變量的初始化。其他復(fù)雜的初始化工作則交給InitO函數(shù)。定義:可以在構(gòu)造函數(shù)中實(shí)現(xiàn)類的初始化。利:形式簡單,在使用類時(shí)不必?fù)?dān)心類是否被初始化。弊:在構(gòu)造函數(shù)中完成初始化工作面臨如下問題:.由于缺少異常處理(在構(gòu)造函數(shù)中不允許使用),構(gòu)造函數(shù)很難發(fā)現(xiàn)錯(cuò)誤:.如果初始化失敗,繼續(xù)使用類將進(jìn)入不可預(yù)知狀態(tài);.如果構(gòu)造函數(shù)是虛函數(shù),則其調(diào)用不會(huì)傳至子類的實(shí)現(xiàn)。未來對類的修改可能悄悄地引入此問題,甚至類不是其子類時(shí)也會(huì)引起混亂。.如果創(chuàng)建全局類變量(雖然違反此指南,但仍有人這么做),構(gòu)造函數(shù)將在main()執(zhí)行之前被調(diào)用,這很可能打破在構(gòu)造函數(shù)中的假設(shè)。譬如,gflags還未被初始化。結(jié)論:如果初始化工作對于類很重要,考慮使用lnit()方法。特別地,構(gòu)造函數(shù)不應(yīng)該調(diào)用虛函數(shù),這很可能引起錯(cuò)誤、訪問未初始化的全局變量等問題。2默認(rèn)構(gòu)造函數(shù)(Defau11Constructor)如果類定義了成員變量且沒有其他構(gòu)造函數(shù),應(yīng)定義默認(rèn)構(gòu)造函數(shù),這樣可以確保新建對象的內(nèi)部狀態(tài)一致和有效。否則,編譯器將會(huì)不安全地初始化類。定義:當(dāng)創(chuàng)建一個(gè)類而不傳入?yún)?shù)時(shí),編譯器便會(huì)調(diào)用默認(rèn)構(gòu)造函數(shù)來完成初始化。比如使用new。運(yùn)算符時(shí)總是調(diào)用這個(gè)構(gòu)造器。利:按默認(rèn)方式初始化結(jié)構(gòu)體,能處理非法值,簡化調(diào)試工作。彈:增加代碼寫書量。結(jié)論:即使你沒有定義默認(rèn)構(gòu)造函數(shù),編譯器也會(huì)自動(dòng)產(chǎn)生一個(gè)來初始化新對象,這種構(gòu)造函數(shù)通常不能正確地完成初始化工作。繼承自其他類且未增加成員變量的子類不需耍再定義默認(rèn)構(gòu)造函數(shù)。3顯式構(gòu)造函數(shù)(ExpIicitConstructors)關(guān)鍵字explicit用于僅有一個(gè)參數(shù)的構(gòu)造函數(shù)。定義:通常,只接受一個(gè)參數(shù)的構(gòu)造函數(shù)可用于類型轉(zhuǎn)換。比如,Foo的構(gòu)造函數(shù):Foo::Foo(stringname),當(dāng)向以Fo。類型為參數(shù)的函數(shù)傳遞string參數(shù)時(shí),將調(diào)用這個(gè)構(gòu)造函數(shù)完成string到F。。的轉(zhuǎn)換。有時(shí)這很方便,但有時(shí)會(huì)帶來麻煩,比如這種機(jī)制在違背你本意的情況下完成類型轉(zhuǎn)換并創(chuàng)建新的對象。聲明一個(gè)構(gòu)造函數(shù)為顯式(explicit)可以避免這種轉(zhuǎn)換。利:避免不希望的類型轉(zhuǎn)換。弊:無。結(jié)論:最好在每ー個(gè)只有一個(gè)參數(shù)的構(gòu)造函數(shù)前用explicit進(jìn)行限制。但復(fù)制構(gòu)造函數(shù)例外,在ー些極罕見的情況下允許轉(zhuǎn)換。還有一種例外情況是,那些打算作為透明封裝的類。這兩種情況應(yīng)該以注釋注明。4復(fù)制構(gòu)造函數(shù)(CopyConstructos)在必要時(shí)オ提供復(fù)制構(gòu)造函數(shù)和賦值運(yùn)算符。否則,使用DISALLOW_COPY_AND_ASSIGN來禁用它們。定義:復(fù)制構(gòu)造函數(shù)和賦值運(yùn)算符用來創(chuàng)建一個(gè)對象的副本。復(fù)制構(gòu)造函數(shù)在需要時(shí)被自動(dòng)調(diào)用,比如以傳值方式傳遞ー個(gè)對象時(shí)。利:復(fù)制構(gòu)造函數(shù)方便對象的復(fù)制。C++標(biāo)準(zhǔn)模板庫(STL)中的容器內(nèi)容必須是可復(fù)制和可賦值的。復(fù)制構(gòu)造函數(shù)比CopyFrom()這種替代方案更高效,因?yàn)樗鼘?gòu)造和復(fù)制進(jìn)行了結(jié)合,某些情況下,編譯器會(huì)略去它,它也避免了堆分配的開銷。弊:C++對象的顯式復(fù)制常會(huì)導(dǎo)致缺陷和引起性能問題。它也會(huì)降低代碼的可讀性,與引用相比,傳值將使找出到底是哪個(gè)對象被來回傳遞變得困難,因此,找出對象在何處被修改也變得不可映射。結(jié)論:只有少量的類具有可復(fù)制性。大多數(shù)要么有一個(gè)復(fù)制構(gòu)造函數(shù),要么支持賦值運(yùn)算符。通常,指針和引用起到復(fù)制的功能,且性能更好。比如,你可以向函數(shù)傳遞對象的引用或者指針而不是對象本身,你也可以在C++STL標(biāo)準(zhǔn)容器中保存對象的指針。如果類需要復(fù)制性,最好提供復(fù)制方法,比如CopyFrom()或者Clone。而不要使用不能被顯式調(diào)用的復(fù)制構(gòu)造函數(shù)。如果復(fù)制方法不滿足要求(比如出去性能的要求或者類需要被保存在STL標(biāo)準(zhǔn)容器中),可以提供復(fù)制構(gòu)造函數(shù)和賦值運(yùn)算符。如果你的類不需要復(fù)制構(gòu)造函數(shù)或者賦值運(yùn)算符,必須顯式地禁用它們。將它們在類的private部分聲明,且不提供任何相關(guān)定義(這樣,任何試圖調(diào)用都將導(dǎo)致連接錯(cuò)誤)。方便起見,可以使用DISALLOW_COPY_AND_ASSIGN宏://定義一個(gè)宏來禁用復(fù)制構(gòu)造函數(shù)和賦值運(yùn)算符/Z這兩個(gè)方法的聲明應(yīng)該位于類聲明的私有部分#defineDISALLOW_COPY_AND_ASSIGN(TypeName)\TypeName(constTypeName&); \voidoperator=(constTypeNameS)Foof;}在Foo類中這樣聲明:classFoo{
public:Foo(intf);?Foo();private:DISALLOW_COPY_AND_ASSIGN(Foo);3.5結(jié)構(gòu)體與類(StructsvsCIasses)當(dāng)對象只是用來保存數(shù)據(jù)時(shí),則使用結(jié)構(gòu)體,其他情況使用類。在C++中,struct和class幾乎是同義詞。我們將給它們加上自己的語議,以便于正確地使用它們進(jìn)行數(shù)據(jù)定義。結(jié)構(gòu)體應(yīng)該被用來承載數(shù)據(jù),也可包含相關(guān)的常量,除了數(shù)據(jù)成員的訪問/修改方法外不提供其他方法。而且數(shù)據(jù)成員的訪問/修改也是直接對其數(shù)據(jù)的訪問而不通過方法調(diào)用。即使它有其他方法,這些方法也只完成數(shù)據(jù)成員的修改,比如構(gòu)造器、析構(gòu)函數(shù),Initialize()Reset()、Validate()〇如果還需要其他方法,則使用類。為保持與STL的一致性,函數(shù)對象(functor)和類型獲取器(traits)可以使用結(jié)構(gòu)體。注意:在結(jié)構(gòu)體和類中,成員變量的命名方式是不同的。3.6繼承(Inheritance)組合通常比繼承更合適。使用繼承時(shí),一般為公有繼承。定義:一個(gè)子類將繼承基類的全部數(shù)據(jù)和操作。特別地,C++中,繼承有兩種主要方式:實(shí)現(xiàn)繼承:實(shí)質(zhì)性代碼都被子類繼承和接口繼承:子類只繼承接口名稱。利:實(shí)現(xiàn)繼承通過重用基類的代碼來減小程序規(guī)模。由于繼承是ー個(gè)編譯時(shí)的聲明,程序員和編譯器可以理解這些操作并檢測錯(cuò)誤。接口繼承則通過編程使ー個(gè)類對外暴露特定的API?同樣,當(dāng)ー個(gè)類沒有定義必要的API時(shí),編譯器可以檢測出錯(cuò)誤。弊:對于實(shí)現(xiàn)繼承,由于子類的實(shí)現(xiàn)代碼需要在基類和其自身展開,理解這些實(shí)現(xiàn)將變得困難。子類不可以覆蓋非虛方法,所以它不能改變(基類的)實(shí)現(xiàn)。基類也可以定義ー些數(shù)據(jù)成員來規(guī)定其物理布局。結(jié)論:所有繼承應(yīng)該是公有繼承。如果使用私有繼承,更好的方法是使用一個(gè)基類的實(shí)例作為成員。不要濫用實(shí)現(xiàn)繼承,類組合常常更合適。只有當(dāng)Bar有充分的理由說明其isaFoo時(shí),才能說Bar是Fo。的子類必要時(shí)使析構(gòu)函數(shù)虛化。任何定義了虛函數(shù)的類,析構(gòu)函數(shù)都應(yīng)該被虛化。子類需要使用的基類方法最好用protected加以限制。注意,基本的數(shù)據(jù)成員必要是private〇重寫繼承的虛函數(shù)時(shí),在子類中顯式地聲明其為virtual。一旦漏掉了virtual,讀者必須檢測其所有基類來確保它是不是虛函數(shù)。7多重繼承(MultipleInheritance)多重實(shí)現(xiàn)繼承通常罕有其用。只有當(dāng)僅一個(gè)基類被實(shí)現(xiàn)繼承,而其他類都是純接口且以Interface作為后綴聲明時(shí)オ允許多重:繼承。定義:多重繼承允許ー個(gè)類繼承多個(gè)基類。請注意區(qū)別基類、純接口和實(shí)現(xiàn)接口。利:多重繼承允許你更大限度地重用代碼。弊:多重繼承只有一種情況オ被允許:除了第一基類,其他類都是以|nterface作為后綴結(jié)束的純接口。注意:在Windows中有一個(gè)例外。8接口(Interface)作為接口的類可以但不必以Interface后綴結(jié)束。定義:滿足以下條件的類被稱為純接口:只有公共的純虛函數(shù)('、0")和靜態(tài)方法(見下文,析構(gòu)函數(shù)例外);只能有靜態(tài)數(shù)據(jù)成員;沒有構(gòu)造函數(shù)定義。即使有構(gòu)造函數(shù),也僅是默認(rèn)構(gòu)造函數(shù)且被聲明為protected;如果是子類,只能繼承自滿足以上條件且以Interface后綴結(jié)束的類;由于內(nèi)部都是純虛函數(shù),接口類不能被直接實(shí)例化。為使所有接口的實(shí)現(xiàn)都可以被正確地銷毀,所有接口類必須定義ー個(gè)虛析構(gòu)函數(shù)(這與第一條沖突)。詳細(xì)請參見Stroustup的行heC++ProgrammingLanguage》第3版的12.4章節(jié)。利:最好給純接口類加上Interface后綴以讓其他程序員知道此類不能添加任何方法實(shí)現(xiàn)和非靜態(tài)數(shù)據(jù)成員,這對于多重繼承來說很重要。Java程序員可能更了解接口。弊:Interface后綴使類名變得冗長而難以閱讀和理解。而且,接口的特征可能被誤解為其具體實(shí)現(xiàn)不能暴露給調(diào)用者。結(jié)論:滿足以上條件的類最好以Interface后綴結(jié)束。然而,這不是必須的。9運(yùn)算符重載(OperatorOverIoading)只有在很罕見的情況下オ會(huì)用到運(yùn)算符重我。定義:類可以重載諸如+/-的運(yùn)算符以使其能像內(nèi)建類型ー樣操作。利:這些類可以像內(nèi)建類型ー樣操作(比如int),代碼看上去更直觀。相比于那些呆板的命名函數(shù)(比如Equals。、Add()),重載的運(yùn)算符是ー種操作性的命名。為確保某些模板函數(shù)的正確性,有時(shí)必須重載運(yùn)算符。弊:運(yùn)算符有很多弊端:它可能使我們誤以為大開銷的操作(運(yùn)算符重載實(shí)際上是函數(shù)調(diào)用)是小開銷的內(nèi)建操作;找到重載運(yùn)算符的調(diào)用點(diǎn)常常很困難。比如找至リ函數(shù)Equals。的調(diào)用處ヒ匕找至リ:的簡單的多。一些運(yùn)算符也適用于指針,這很容易造成程序缺陷。舉個(gè)例子:&FOO+4和Foo+4實(shí)現(xiàn)的操作完全不同,但編譯器不會(huì)報(bào)錯(cuò)。運(yùn)算符重載也有可能造成歧義。比如,如果一個(gè)類重載了單目運(yùn)算符&,它不可以被安全地前置聲明。結(jié)論:一般情況下不要重載運(yùn)算符。尤其賦值運(yùn)算符,通常很隱蔽。如果需要,可以定義Equals()和CopyFrom。等函數(shù)。如果ー個(gè)類需要被前置聲明,一定避免危險(xiǎn)的單目運(yùn)算符&的重載。然而,在很罕見的情況下,你可能需要重載運(yùn)算符來與模板和C++標(biāo)準(zhǔn)類(如?(ostream&,constT&))互操作。合理的情況下,可以使用運(yùn)算符重載,但如果可能,還是應(yīng)該避免。尤其注意,不要僅僅為了類能在標(biāo)準(zhǔn)容器中作為鍵而重載==和〈,相反,你應(yīng)該創(chuàng)建相等和比較函數(shù)對象。ー些標(biāo)準(zhǔn)模板庫算法可能需要你重載=,但必須說明原因。參見復(fù)制構(gòu)造函數(shù)和函數(shù)重寫。3.10訪問控制(AccessControI)數(shù)據(jù)成員應(yīng)該被定義成私有(private)(靜態(tài)常數(shù)據(jù)成員除外),需要時(shí)提供訪問器(accessor)(出于技術(shù)考慮,使用GoogleTest時(shí),承載測試功能的類可以將其數(shù)據(jù)成員聲明成protected)。通常,名稱為foo_的變量其訪問函數(shù)為f。。(),而其修改器(mutator)則為set_foo()〇訪問器常在頭文件中定義為內(nèi)聯(lián)函數(shù)。參見繼承和函數(shù)命名。11聲明次序(DeclarationOrder)請按下面的規(guī)則次序來定義類:公共成員位于私有成員前;方法位于數(shù)據(jù)成員前(變量)公共部分位于保護(hù)部分前,保護(hù)部分位于私有部分前,如果某個(gè)部分空,則忽略它。在每個(gè)部分,請按照下面的次序來聲明成員:類型定義(Typedefs)和枚舉(Enums);常量(staticconst數(shù)據(jù)成員);構(gòu)造函數(shù);析構(gòu)函數(shù);類方法,包括靜態(tài)方法數(shù)據(jù)成員(靜態(tài)常量除外);友元聲明和DISALLOW_COPY_AND_ASSIGN宏調(diào)用應(yīng)該在私有部分。私有部分應(yīng)該在類定義的最后部分。參見復(fù)制構(gòu)造函數(shù)。在相關(guān)源文件中,方法的實(shí)現(xiàn)次序也應(yīng)盡量與類聲明中一致。不要將把大函數(shù)內(nèi)聯(lián)定義在類定義中。通常,只有很短且性能要求高的情況下オ將一個(gè)函數(shù)定義成內(nèi)聯(lián)。參見內(nèi)聯(lián)函數(shù)。12定義簡短函數(shù)(WriteShortFundions)函數(shù)應(yīng)該盡量簡短并功能單ー。不得不承認(rèn),某些場合長函數(shù)很合適,所以不太容易去限制其長度。如果ー個(gè)函數(shù)超過40行,考慮可否在不改變程序結(jié)構(gòu)的情況下將其拆分。盡管長函數(shù)目前工作良好,也許其他程序員日后會(huì)修改并給它添加新功能。這會(huì)導(dǎo)致難以發(fā)現(xiàn)的缺陷。保持你的函數(shù)簡短可以方便其他程序員閱讀和修改你的代碼。閱讀ー些代碼時(shí),你可能發(fā)現(xiàn)長函數(shù)。不過,不要害怕修改它們。如果發(fā)現(xiàn)使用它們很困難或者調(diào)試有難度,或者你想在多處使用部分函數(shù)代碼,考慮把它拆分成更易管理的片段。谷歌經(jīng)驗(yàn)技巧(Google-SpecificMagic)谷歌采用很多技巧和工具來確保C++代碼的健壯性,而且,谷歌使用C++的方式和其他地方很不同。智能指針(SmartPointers)如果使用指針,最好使用受限指針(soped_ptr)。而std::trl::shared_prt只有在很少的情況下都會(huì)用得到,比如對象需要被標(biāo)準(zhǔn)模板庫容器包含。任何情況下不要使用自動(dòng)指針(auto_ptr)。智能指針是指行為類似于指針但增加額外功能的對象。當(dāng)一個(gè)scoped_ptr被銷毀時(shí),它也將刪除其指向的對象刪除。shared_prt也具有類似功能,但它會(huì)實(shí)現(xiàn)引用計(jì)數(shù)直到它指向的最后ー個(gè)對象刪除它。通俗點(diǎn)說,我們希望定義清楚每個(gè)對象的歸屬。但最清楚的對象歸屬是此對象被域或者局部變量擁有,而不是使用指針。另ー個(gè)極端是,在它們被定義時(shí),引用計(jì)數(shù)指針不被任何對象擁有。這種定義的問題是,這樣將很容易循環(huán)引用或者對象無法被銷毀的奇怪現(xiàn)象。而每個(gè)原子操作都進(jìn)行復(fù)制或賦值將影響性能。即使不提倡,引用計(jì)數(shù)指針有時(shí)卻是最簡單和幽雅的問題解決方式。2CPPIint使用cpplint.py來檢測風(fēng)格錯(cuò)誤。Cpplintpy是ー個(gè)能讀取源文件并識(shí)別風(fēng)格錯(cuò)誤的工具。盡管不很完美,有很多優(yōu)點(diǎn)和缺點(diǎn),但它仍是ー個(gè)有用的工具。主動(dòng)錯(cuò)誤信息可以將〃NOLINT放在行后來忽略。有些項(xiàng)目帶有如何從項(xiàng)目工具運(yùn)行cpplint.py的說明。如果沒有,你可以單獨(dú)ド載它。5.其他C++特性(OtherC++Fetures)引用參數(shù)(ReferenceArguments)所有用引用傳值的變量應(yīng)該被const修飾。定義:在C語言中,如果函數(shù)需要修改ー個(gè)變量,必須使用指針作為其參數(shù)。比如intf〇〇(int*pval)〇但在C++中,有了另ー"種方式,即引用:intf〇〇(int&val)〇利:把ー個(gè)參數(shù)定義為引用可以避免丑陋的代碼(比如?pval++)。有些程序需要,比如復(fù)制構(gòu)造函數(shù)。使程序更明確,不像指針能取得NULL值。弊:由于引用兼具值表達(dá)式和指針的主義,會(huì)引起迷惑。結(jié)論:所有函數(shù)引用參數(shù)都應(yīng)該定義為const引用。voidFoo(conststring&in,string*out)實(shí)際上,將值或者常引用作為輸入?yún)?shù)而將指針作為輸出參數(shù)是谷歌的ー個(gè)慣例。輸入?yún)?shù)也可以是常指針,但不允許非const(non-const)引用。將常指針作為輸入?yún)?shù)的ー種情況是,你想強(qiáng)調(diào)這個(gè)參數(shù)將不被復(fù)制,它在對象的整個(gè)生命周期內(nèi)必須存在,但最好在注釋中說明。標(biāo)準(zhǔn)模板庫的適配器(比如bind2nd和mem_fun)不允許引用參數(shù),這時(shí)只有用指針了。2函數(shù)重載(FunctionOverIoading)看到ー個(gè)函數(shù)的調(diào)用立即能知道其操作而不是需要首先找出是哪個(gè)重教版本被調(diào)用了時(shí),オ使用重載函數(shù)。定義:利用重載,你可以定義接收不同參數(shù)的同名函數(shù),比如接收conststring&和constchar?的同名函數(shù)。classMyClass{Public:voidAnalyze(conststring&text);voidAnalyze(constchar*text,size_ttextlen);};利:重載可以使代碼更直觀。對于模板化的代碼,重載可能是必須的;對于訪問控制器(Visitor)的實(shí)現(xiàn),重載也是很方便的。陣:如果函數(shù)僅以參數(shù)類型不同來重載,讀者可能需要深入理解C++復(fù)雜的參數(shù)匹配規(guī)則才能知道是怎么回事。在繼承中,子類只重寫基類函數(shù)的某些版本也會(huì)引起迷惑。結(jié)論:要想重載函數(shù),考慮根據(jù)其參數(shù)來命名函數(shù)。比如AppendString()、Appendlnt()就比Append()好。3默認(rèn)參數(shù)(Defau11Arguments)除非在以下情況下,不允許使用函數(shù)默認(rèn)參數(shù)。利:你經(jīng)常會(huì)寫帶有很多默認(rèn)值的函數(shù),但有時(shí)又不得不重載這些默認(rèn)值。默認(rèn)參數(shù)提供了實(shí)現(xiàn)它的簡單方法,且不用為了少量例外而定義大量函數(shù)。弊:程序員常常通過查看己有代碼來找出調(diào)用ー個(gè)API的方法。默認(rèn)參數(shù)將變得更難維護(hù),因?yàn)閺钠渌胤綇?fù)制一粘貼代碼,默認(rèn)參數(shù)可能未被顯示。當(dāng)默認(rèn)參數(shù)不適用于新代碼時(shí),復(fù)制ー粘貼部分將引起問題。結(jié)論:除了以下情況,函數(shù)必須明確定義每個(gè)參數(shù)來強(qiáng)制程序員在調(diào)用API時(shí)考慮傳入?yún)?shù)值,而不是簡單地接受默認(rèn)參數(shù)。ー種特殊的例外是當(dāng)默認(rèn)參數(shù)是用來模擬可變長參數(shù)時(shí):/Z通過默認(rèn)參數(shù),最多支持4個(gè)參數(shù)stringStrCat(constAlphaNum&a,constAlphaNum&b=gEmptyAlphaNum,constAlphaNum&c=gEmptyAlphaNum,constAlphaNum&d=gEmptyAlphaNum);5.4可變長度數(shù)組和內(nèi)存申請(Variable-LengthArraysandaIIoca())不允許變長數(shù)組和內(nèi)存申請。利:可變長度數(shù)組語句自然,且與alloca()ー樣,很高效。弊:可變長度數(shù)組和alloc()不是C++標(biāo)準(zhǔn)的一部分。重要的是,它們根據(jù)程序棧容量來申請空間,這可能引起內(nèi)存覆蓋缺陷。“在我的機(jī)器上一切正常,但做成產(chǎn)品后卻神秘死機(jī)。”結(jié)論:使用更安全的內(nèi)存申請函數(shù),比如scoped_ptr/scoped_array。5.5友元(Friends)適度使用友元類和友元函數(shù)是允許的。友元應(yīng)該和其友類定義在同一個(gè)文件中,這樣,讀者不必再去另ー個(gè)文件中查看友元使用了該類中的哪些私有成員。友元的ー個(gè)常見作用是在不暴露ー個(gè)類的內(nèi)部細(xì)節(jié)時(shí)利用友元類來正確地構(gòu)造其內(nèi)部狀態(tài)。比如FooBuilder和F。。。有時(shí),將一個(gè)單元測試類定義為其測試類的友元會(huì)很有用。友元僅僅是擴(kuò)展而不是打破類的封裝性。當(dāng)一個(gè)類需要訪問另ー個(gè)類的私有成員時(shí),友元比將這個(gè)成員公有化更好。然而,類與類的協(xié)作只能通過公共成員。6異常處理(Excpetions)通常不使用C++的異常處理。利:異常處理使在程序的更高層次來處理多層函數(shù)嵌套調(diào)用的’‘不可發(fā)生”錯(cuò)誤成為可能,而且不需要使用隱蔽且容易出錯(cuò)的錯(cuò)誤代碼簿記;異常處理被大多數(shù)其他現(xiàn)代程序設(shè)計(jì)語言采用。在C++中使用異常處理將使其與其他語言如Python,Java保持一致性。ー些第三方庫使用異常處理,如果關(guān)閉,可能導(dǎo)致很驗(yàn)證使用這些API。異常是唯一能導(dǎo)致構(gòu)造失敗的方式。盡管可以使用工廠函數(shù)或者InitO方法來實(shí)現(xiàn)構(gòu)造,但它們分別需要堆申請和“無效”狀態(tài)。異常處理常被用于架構(gòu)測試。弊:當(dāng)給?個(gè)函數(shù)加上拋出(throw)語句時(shí),必須檢查其調(diào)用鏈。它們要么進(jìn)行基本的異常處理,要么忽略異常且無視程庁:由此而終止運(yùn)行。舉個(gè)例子,如果f()調(diào)用g(),g()又調(diào)用f(),h拋出ー個(gè)異常,f捕捉到了這個(gè)異常,那么g必須注意在異常發(fā)生時(shí)的清理工作。更一般地,異常處理使程序很難從其代碼中看出其控制流:程序可能從意想不到的地方返回。這將使維護(hù)和調(diào)試變得困難重重。你可以通過使用ー些異常處理規(guī)則來減小這些開銷,但ー定比開發(fā)人員需要了解和理解的多。異常安全性需要資源獲取即初始化(RAID和不同的編碼實(shí)踐的支持。為簡化正確開發(fā)異常安全代碼的工作,也需要很多支持機(jī)制。進(jìn)ー步,為避免陷入尋找完整函數(shù)調(diào)用鏈的麻煩,異常安全代碼必須把用于將狀態(tài)持久化為“提交”階段的邏輯進(jìn)行隔離。這既有好處又有開銷(也許在你被迫隱藏代碼來隔離提交的地方)。允許異常處理,在不值得的情況下也得付出這些代價(jià)。使用異常處理將增加目標(biāo)代碼量,增加編譯時(shí)間(通常不明顯)并可能增大地址空間的壓カ。異常的可用性可能使開發(fā)者在不合時(shí)宜時(shí)拋出異常或者從異常狀態(tài)恢復(fù)并不安全時(shí)使用。比如,無效的用戶輸入不應(yīng)該導(dǎo)致異常拋出。結(jié)論:表面上,使用異常處理的好處比開銷多,尤其是新項(xiàng)目。然而,如果為已有代碼引入異常處理,將會(huì)引起所有相關(guān)代碼的變動(dòng)。如果異常在新項(xiàng)目外可被拋出,則其與未進(jìn)行異常處理的舊項(xiàng)目的互操作將變得困難重重。由于大多數(shù)谷歌的C++項(xiàng)目都不準(zhǔn)備采用異常處理機(jī)制,因而將很難使用產(chǎn)生異常的新代碼。考慮到谷歌已有代碼未進(jìn)行異常處理,異常處理的機(jī)制引入的花銷將遠(yuǎn)大于新項(xiàng)目。而且,轉(zhuǎn)換過程也將緩慢而且容易出錯(cuò)。再者,異常處理的替代方式,比如錯(cuò)誤處理和斷言也不會(huì)增加很多編程負(fù)擔(dān)。我們并不是站在哲學(xué)或者道德的立場反對使用異常處理機(jī)制,而是站在實(shí)用的立場上。因?yàn)槲覀冃枰褂霉雀栝_源項(xiàng)目,而且對這些項(xiàng)目引入異常處理很困難,我們不得不在谷歌開源項(xiàng)目中建議不要采用異常處理。如果從頭開發(fā)這些項(xiàng)目將困難重重。對于Windows代碼來說例外。フ運(yùn)行時(shí)類型信息(Run-TimeTypeInformation,RTTI)不建議使用RTTIo定義:RTTI允許程序員在運(yùn)行時(shí)查看ー個(gè)對象的類類型。利:在單元測試時(shí)會(huì)有用,比如進(jìn)行エ廠類測試時(shí),必須證實(shí)ー個(gè)新創(chuàng)建對象是否是應(yīng)有動(dòng)態(tài)類型。測試之外罕見其用。彈:運(yùn)行時(shí)檢查類型通常意味著類設(shè)計(jì)有問題。結(jié)論:除了單元測試,不要使用RTTI。確實(shí)需要基于對象類型來完成不同的功能時(shí),考慮替代方案。虛方法是使子類執(zhí)行不同代碼的首選方案。這將使對象自己來完成自己特定的工作。如果這些工作位于項(xiàng)目的ー些處理代碼中,考慮雙分派(Double-Dispatch),比如訪問器設(shè)計(jì)模式(VisitorDesignPattern)?這允許對象外的設(shè)施可以利用系統(tǒng)內(nèi)建類型來決定一個(gè)類的類型。如果你不贊成這些觀點(diǎn),你可以使用RTTI。但請三思⑥再三思。不要手動(dòng)實(shí)現(xiàn)類似RTTI的變通方案。反對RTTI及其變通方案應(yīng)用的觀點(diǎn)就和反對用類型標(biāo)簽對類分層ー樣多。8類型轉(zhuǎn)換(Casting)在C++中,需要類型轉(zhuǎn)換時(shí)請使用static_cast<>(),不要使用諸如inty=(int)x和inty=int(x)的其他形式。定義:C++提供了一一種與C不同的因類型而異的類型轉(zhuǎn)換操作。利:C語言的問題是轉(zhuǎn)換操作的二義性:有時(shí)是轉(zhuǎn)換(比如(int)3.5),而有時(shí)卻是cast(比如(int)"hello"))。C++的類型轉(zhuǎn)換則不存在這個(gè)問題,而且,C++的類型轉(zhuǎn)換是顯式的和可追蹤的。弊:語句繁瑣。結(jié)論:使用static-cast進(jìn)行C風(fēng)格的值轉(zhuǎn)換或者將子類指針提升為基本指針;使用const_cast去掉const修飾(參見const):使用reinterpret_cast進(jìn)行不安全的指針類型轉(zhuǎn)換(比如轉(zhuǎn)換到或者從整型指針和其他類型指針)。當(dāng)你確定你的做法及其引用的問題時(shí)オ進(jìn)行這種操作;除了在測試中,不要用dynamic_cast.如果在單元測試之外需要使用這種方法來測試ー個(gè)類的類型,通常意味著你的設(shè)計(jì)有問題。5.9流(Streams)流只用于日志記錄。定義:流是C中printf()和scanf()的替代實(shí)現(xiàn)。利:使用流,不需要關(guān)心輸出對象的類型,也不需要像C那樣定義一大串格式符了。流的構(gòu)造和析構(gòu)函數(shù)會(huì)自動(dòng)打開和關(guān)閉相關(guān)文件。弊:流不利于隨機(jī)讀取。ー些格式(尤其字符串格式習(xí)慣用法:%.*s)也不像使用類似printf技巧那樣方便。流也不支持有利于國際化的運(yùn)算符重定序(比如%1s指令)。結(jié)論:除非需要日志接口,否則不要使用流。使用類似printf的例程。關(guān)于流的爭論很多,但正如我們一致強(qiáng)調(diào)的,不要使用流。擴(kuò)展討論:關(guān)于這些問題的爭論此起彼伏,所以這里我們深入討論。重申一下唯?指南原則:我們希望確保每次對于特定類型的I/O的代碼都是ー樣的。為此,我們不允許使用者在使用流還是printf(加上讀/寫/等等)之間進(jìn)行選擇,我們的做法是確定一個(gè)(即printf)?之所以日志例外,是出于歷史原因考慮的,日志是ー個(gè)很特殊的程序。流的支持者認(rèn)為流是明智的選擇,但并不是這樣。他們指出的每ー個(gè)優(yōu)點(diǎn),都有相應(yīng)的缺點(diǎn)。最大的優(yōu)點(diǎn)是,你根本不需要知道輸出對象的類型。但仔細(xì)想想,你可能使用了錯(cuò)誤類型,而編譯器不會(huì)警報(bào)。在使用流時(shí),很容易犯這種錯(cuò)誤:cout<<this; //打印地址cout?*this; 〃打印指針內(nèi)容由于運(yùn)算符<<被重載,編譯器將不會(huì)報(bào)錯(cuò)。這也是我們不提倡重載的原因。ー些人說printf格式繁瑣不易閱讀,但流也好不到哪里。看看卜.面的兩段代碼,處理同一類型的數(shù)據(jù)。哪ー個(gè)更簡單呢?cerrくく’'Errorconnectingto'"<<foo->bar()->hostname.first<<":"<<foo->bar->hostname.second<<":"<<strerror(errno);Fprintf(stderrz"Errorconnectingto'%s:%u:%s"zfoo->bar()->hostname.first,foo->bar()->hostname.second,strerror(errno));等等你可能發(fā)現(xiàn)的其他問題。(你也許會(huì)爭論,正確地封裝更好。但如果它對ー個(gè)模式正確,對另ー個(gè)呢?再者,我們的目標(biāo)是盡量簡化ー種語言,而不是增加更多需要學(xué)習(xí)的機(jī)制。)關(guān)于兩者優(yōu)缺點(diǎn)的爭論還在繼續(xù),也找不到ー種更合理的解決方案。盡管我們簡單地規(guī)定了選擇它們中的ー種,但還是使用printf+read/write的居多。5.10前置自增和前置自減(PreincrementandPredecrement)在操作迭代器和模板對象的時(shí)候使用前置自增和前置自減。定義:當(dāng)ー個(gè)變量自增(++i或i-)(自減(-i或i-)且不需要使用其值時(shí),必須考慮是前置還是后置++/ー。利:不考慮返回值時(shí),前置性能總是優(yōu)于后置。這是因?yàn)楹笾眯枰獜?fù)制i的值。如果i是ー個(gè)迭代器或者非標(biāo)量類型,復(fù)制i將是很大的開銷。既然兩者的作用相同,為何不選擇前置運(yùn)算呢。弊:在C傳統(tǒng)開發(fā)中,常使用后置方式,尤其在for循環(huán)中。有些人發(fā)現(xiàn)后置運(yùn)算更易讀,與英語語法一樣,后置運(yùn)算的對象(i)在動(dòng)詞(++)之前。11cons!修飾符的使用(Useofconst)當(dāng)需要的時(shí)候盡量使用const修飾。定義:使用const來限制變量或者參數(shù)不被修改(比如constintfoo)o類方法也可用const修飾以說明其不會(huì)改變類的狀態(tài)。(比如classFoo{(intBar(charc)const;))利:有利于程序員理解變量的使用方式。編譯器能進(jìn)行更嚴(yán)格的類型檢查,而且常常能產(chǎn)生更高效的代碼。說服程序員相信其程序的正確性,畢竟他們知道其調(diào)用的函數(shù)對這些變量的修改是有限制的。這可以幫助程序員知道在多線程編程中哪些函數(shù)不需要鎖也能正確地運(yùn)行。弊:const是傳遞的,如果你給函數(shù)傳遞const變量,那么這個(gè)函數(shù)在聲明時(shí)必須加上const聲明(否則變量需要const_cast)。在使用庫函數(shù)時(shí),這將是一個(gè)問題。結(jié)論:const變量、數(shù)據(jù)成員、方法和參數(shù)使編譯器進(jìn)行編譯時(shí)類型檢查,因此能盡早發(fā)現(xiàn)程序錯(cuò)誤。以下情況強(qiáng)烈建議使用const修飾:函數(shù)不修改通過引用或者指針傳遞的參數(shù)值,應(yīng)該使用const修飾;類方法應(yīng)該盡量定義成const。訪問器(Accessor)應(yīng)該永遠(yuǎn)是const的。其他不修改類數(shù)據(jù)成員、不調(diào)用非const方法不返回非const引用或指針的函數(shù)也應(yīng)該是const的;把初始化后不需要修改的數(shù)據(jù)成員定義成const=但不要濫用const。像constint*const*constx就是濫用,即使你能準(zhǔn)確地解釋清楚constx是什么意思。注意什么是真正有用的,這種情況下,constint**x就足夠了。當(dāng)進(jìn)行多線程編程時(shí),mutable關(guān)鍵字可能不安全,所以在使用時(shí)應(yīng)該首先考慮線程安全。應(yīng)該將const放在什么位置某些人更喜歡將const放在類型后(intconst*foo而不是constint*foo)。他們認(rèn)為這樣更易讀和一致,const修飾的對象總是緊跟在它后面。然而,這種一致性在這里卻不適用,因?yàn)椤安灰^分”宣言排除大多數(shù)你認(rèn)為的一致性用法。將const放在前面更具可讀性,因?yàn)樗c英語語法一樣,把形容詞(const)放在名詞(int)前面。這并不是說我們鼓勵(lì)你將const放在最前面你就一定要這么做,注意與你的代碼保持一致性。5.12整型類型(IntegerTypes)所有C++整型中,唯一可能用到就是int。當(dāng)需要其他長度的整型時(shí),使用stdint.h頭文件中的精確整型,比如intl6一t。定義:C++未明確定義整型的長度。人們通常的假設(shè)是:short:16位,int:32位,long:32位,longlong:64位。利:一致的聲明:弊
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年下城區(qū)青梅收購合同
- 《廉潔自律教育》課件
- 2025有關(guān)餐廳轉(zhuǎn)讓合同的范本
- 2025共創(chuàng)連鎖加盟合同
- 《金融機(jī)構(gòu)行政許可》課件
- 中國第二十冶金建設(shè)公司綜合學(xué)校高中分校高中歷史四導(dǎo)學(xué)案:毛澤東
- 2025年河北省張家口部分學(xué)校中考一模道德與法治試題(含答案)
- 貓砂冰淇淋采購合同協(xié)議
- 白酒禮品采購合同協(xié)議
- 甲方裝修工程合同協(xié)議
- 排土場災(zāi)害防治技術(shù)
- 世界港口代碼表
- 消防設(shè)施安全操作規(guī)程
- 植物的光合作用生理生態(tài)
- 項(xiàng)目三任務(wù)1知識(shí)點(diǎn)1課程思政√
- 08K507-1 管道與設(shè)備絕熱-保溫(有水印)
- 公差疊加計(jì)算表
- 中國政法知識(shí)產(chǎn)權(quán)訴訟專題講座:知識(shí)產(chǎn)權(quán)訴訟攻防策略與技巧
- 血栓彈力圖在ICU應(yīng)用
- MT 491-1995煤礦防爆蓄電池電機(jī)車通用技術(shù)條件
- GB 38900-2020機(jī)動(dòng)車安全技術(shù)檢驗(yàn)項(xiàng)目和方法
評(píng)論
0/150
提交評(píng)論