谷歌C++風(fēng)格文檔的使用介紹
??
谷歌C++風(fēng)格文檔真是一個(gè)好東西,為C++開發(fā)提供了一個(gè)便捷高效又有無數(shù)人在實(shí)踐和驗(yàn)證的白皮書,雖然其中并不是所有說法都是客觀的,但是既然是經(jīng)過谷歌這樣的公司投入實(shí)際應(yīng)用的,那總不會(huì)有很大的壞處,至少不會(huì)給你帶來麻煩,所以我個(gè)人一直都比較堅(jiān)持使用這套風(fēng)格。
但是注意對(duì)于Windows程序員,對(duì)于谷歌這套風(fēng)格來說,確實(shí)是有點(diǎn)不適合,比如異常,比如多重繼承,比如縮進(jìn)、換行符等,因?yàn)閃indows自成一體系以及經(jīng)歷了歷史的沉淀,當(dāng)你接手一個(gè)項(xiàng)目時(shí)發(fā)現(xiàn)縮進(jìn)都是Tab換行符都是Win樣式,那你也不可能就改成2個(gè)空格,這樣會(huì)讓code base變得無比混亂,所以在實(shí)際項(xiàng)目中使用谷歌C++風(fēng)格的時(shí)候,視目前情況來確定哪些風(fēng)格可用,哪些應(yīng)該不用就是了。
- 頭文件原則 -
1、如果要使用一個(gè)聲明在某個(gè)頭文件中的函數(shù),應(yīng)該總是include那個(gè)頭文件,也就是如果你實(shí)現(xiàn)一個(gè)函數(shù),應(yīng)該將其實(shí)現(xiàn)在一個(gè).c文件中,然后把函數(shù)聲明暴露在.h文件,而外部使用者應(yīng)該include這個(gè).h文件。
2、使用類模板的時(shí)候,應(yīng)該include這個(gè)類的頭文件。
3、如果使用的是一個(gè)普通類,那向前聲明而不include類頭文件是可以的,但是你要確定向前聲明來使用不會(huì)出現(xiàn)其他問題,如果你不確定,那遵循一律include頭文件原則。
頭文件的包含順序:
1、C語言庫
2、C++的庫
3、項(xiàng)目中的第三方庫的頭文件
4、當(dāng)前項(xiàng)目的頭文件
而include組織原則應(yīng)該按照當(dāng)前項(xiàng)目中的目錄樹結(jié)構(gòu)來組織,比如項(xiàng)目代碼“src/core/log.h”,在include時(shí)則應(yīng)該是“core/log.h”。
- 命名空間 -
可以在源碼文件(CPPCC)中使用【未命名空間】來避免運(yùn)行時(shí)的名稱沖突,注意命名空間格式不縮進(jìn)。
- 類的一些注意 -
原則:應(yīng)避免在類的構(gòu)造函數(shù)中進(jìn)行復(fù)雜的初始化,尤其是是調(diào)用可能初始化失敗的函數(shù)或者是調(diào)用虛函數(shù)。
構(gòu)造函數(shù)失敗的情況除了拋出異常沒有其他辦法知道是否初始化失敗,而谷歌推薦不使用C++異常處理;
而如果構(gòu)造函數(shù)初始化失敗,我們得到的這個(gè)新的類的內(nèi)部狀態(tài)是“不清不楚”的,我們不知道是否該繼續(xù)調(diào)用此類的其他方法,還是干脆銷毀類;
如果在構(gòu)造函數(shù)中調(diào)用虛函數(shù),那就會(huì)依賴于子類的虛函數(shù)實(shí)現(xiàn),出現(xiàn)不可控制的情況,但是如果有這種奇葩的需求的話可以例外。
原則:寫一個(gè)默認(rèn)的構(gòu)造初始化函數(shù)。
如果類有成員變量,你應(yīng)該寫一個(gè)默認(rèn)的構(gòu)造函數(shù)用于將這些變量進(jìn)行初始化,否則編譯器會(huì)生成類似malloc這樣的挫B構(gòu)造函數(shù)。
在創(chuàng)建新的對(duì)象的時(shí)候?qū)⑵淠J(rèn)初始化為一個(gè)“無效”的狀態(tài),以方便錯(cuò)誤判斷,但是對(duì)寫這個(gè)類的人來說,這個(gè)工作顯得有多余。
而如果你的類定義了成員變量但是沒有初始化構(gòu)造函數(shù),則編譯器提供的默認(rèn)初始化函數(shù)可能把你這些變量初始化為一個(gè)未知的值狀態(tài)。
原則:使用顯示構(gòu)造函數(shù)。
為了避免隱式的類構(gòu)造,應(yīng)使用explicit關(guān)鍵字,具體大家可以百度此關(guān)鍵字。
原則:盡量不要使用復(fù)制構(gòu)造和賦值運(yùn)算符來復(fù)制對(duì)象。
賦值構(gòu)造函數(shù)在編譯器處理時(shí)可以帶來一點(diǎn)好處,現(xiàn)代編譯器會(huì)有一些特定的優(yōu)化,當(dāng)然主要是很多時(shí)候?qū)懫饋砗芩?br />但是只有小部分類是可復(fù)制創(chuàng)建新的對(duì)象的,大部分類都不需要這樣的功能,很多情況下指針和引用可以部分的替代這樣的復(fù)制構(gòu)造而且可以帶來更高效率的性能。
如果你的類是可以復(fù)制創(chuàng)建的,則最后應(yīng)該提供一個(gè)CopyFrom或者CopyTo方法,而不是使用復(fù)制構(gòu)造函數(shù),因?yàn)檫@些方法不能被隱式調(diào)用,在調(diào)試的時(shí)候也能更快的定位處理點(diǎn),但是為了某些情況,比如STL兼容的問題,你可以提供復(fù)制構(gòu)造函數(shù)來包裝這些方法。
這里還有一個(gè)建議,如果你的類不需要復(fù)制構(gòu)造函數(shù),那你應(yīng)該顯示禁止它,這樣就可以在某些不可預(yù)料的隱式使用的時(shí)候讓編譯時(shí)出現(xiàn)錯(cuò)誤。
原則:盡量使用class,而不是struct。
在C++中,class和struct的區(qū)別僅僅是默認(rèn)訪問權(quán)限上的區(qū)別,他們幾乎就是一樣的。
但是struct應(yīng)該盡量使用在數(shù)據(jù)對(duì)象的存改上,如果struct的功能提示到了“方法”級(jí)別,則應(yīng)該使用class來替換struct,struct應(yīng)該保持它作為數(shù)據(jù)體存儲(chǔ)空間的純潔性。
比如當(dāng)有構(gòu)造函數(shù)、析構(gòu)函數(shù)、初始化方法的時(shí)候,則不應(yīng)該使用struct。
如果不知道該用class還是struct,那就用class吧。
(不過,為了和STL保持一致性,應(yīng)該使用struct而不是class來實(shí)現(xiàn)仿函數(shù)特性)
- 繼承 -
原則:不要過渡使用繼承,用組合會(huì)更好。
原則:如果有必要的話,將你類的析構(gòu)函數(shù)寫成虛的,如果你的類中有虛方法的話,那這個(gè)類的析構(gòu)函數(shù)必需應(yīng)該是虛的。
原則:對(duì)于可能被子類使用的函數(shù)聲明為protected方法,要限制對(duì)它們的使用,而要注意每個(gè)類的數(shù)據(jù)成員應(yīng)該是private的。
原則:在子類重定義一個(gè)虛函數(shù)來實(shí)現(xiàn)繼承時(shí),應(yīng)該顯示寫virtual關(guān)鍵字標(biāo)識(shí)這個(gè)是虛繼承來的函數(shù),原因是,如果virtual關(guān)鍵字沒了,則閱讀者就要去確定這個(gè)方法是不是虛繼承來的。
- 多重繼承 -
原則:應(yīng)該盡量避免多重繼承,而換為使用組合接口實(shí)現(xiàn)。
原則:我們只允許有實(shí)現(xiàn)的基類不超過一個(gè)的多重繼承(2重繼承),所以其他的應(yīng)該都使用接口來實(shí)現(xiàn)。
我們將基類分為有實(shí)現(xiàn)和無實(shí)現(xiàn)(純接口)二種。
如果一個(gè)類滿足下面的情況,它就是一個(gè)純接口類:
1、它只有公開的純虛方法(= 0),和靜態(tài)方法;
2、它沒有非靜態(tài)的數(shù)據(jù)成員;
3、它不需要定義任何構(gòu)造函數(shù);
一個(gè)接口類不能生成一個(gè)新的實(shí)例對(duì)象,為了保證這個(gè)接口所有的實(shí)現(xiàn)都可以被直接銷毀,這個(gè)接口必需有一個(gè)虛的析構(gòu)函數(shù)。
原則:用interface這樣的關(guān)鍵字來表明一個(gè)類是一個(gè)接口類,同時(shí)禁止向其中添加方法實(shí)現(xiàn)或者封靜態(tài)的數(shù)據(jù)成員。
- 重載操作符 -
原則:除非有特殊情況,不然不要重載操作符。
1、重載操作符會(huì)誤導(dǎo)閱讀者的感覺特別是新手,他會(huì)認(rèn)為這個(gè)操作十分廉價(jià);
2、重裝操作符在尋找位置的時(shí)候不好找,因?yàn)榇a都是==、++;
3、重載操作符在某些情況下可能沖突或者導(dǎo)致bug;
一般來說不要重載操作符,而應(yīng)該使用像Equals()這樣的方法來進(jìn)行類似操作符的比對(duì),如果這個(gè)類有向前聲明,就不要重載危險(xiǎn)的一元操作符&。
但是也有一些特殊情況,比如需要跟STL交互,則需要實(shí)現(xiàn)==這樣的操作符或者仿函數(shù)的時(shí)候,那可以允許。
- 類的訪問控制 -
原則:類的數(shù)據(jù)成員應(yīng)該為私有的,要暴露應(yīng)該提供SetGet方法,類似C#中的屬性系統(tǒng),對(duì)C++來說,SetGet方法應(yīng)該盡量保證inline。
- 類的聲明順序 -
原則:在類中的聲明順序應(yīng)該為,public高于private,methods高于members。
你定義的類應(yīng)該從public成員開始,接著是protected,然后是private。
在每個(gè)部分中,應(yīng)該按照下面的順序,比如:
private
?- 自定義的typedef或者enums。
?- 常量(static const)。
?- 構(gòu)造函數(shù)
?- 析構(gòu)函數(shù)
?- 方法,以及靜態(tài)方法
?- 數(shù)據(jù)成員(除了靜態(tài)常量成員)
友元聲明應(yīng)該總是在private部分。
- 所有按引用傳遞的參數(shù)都應(yīng)該是const的 -
原則:我們堅(jiān)持引用傳遞使用const,而如果要修改,則應(yīng)該使用指針。
例如:void foo(const String& in, String* out)
- 函數(shù)重載 -
原則:只有當(dāng)閱讀者一看就知道這個(gè)函數(shù)執(zhí)行了什么,而不需要去看哪個(gè)版本的函數(shù)被調(diào)用了,不然不要使用函數(shù)重載。
重載能令代碼更直接美觀,對(duì)于模板化的代碼重裝也是必需的。但是如果一個(gè)函數(shù)只通過參數(shù)來區(qū)別各個(gè)重載的版本,則需要去理解C++復(fù)雜的匹配優(yōu)先原則才能正確的使用和閱讀。而如果一個(gè)派生類只重寫了一部分函數(shù),許多人也會(huì)被繼承的語義所混亂視覺。
如果你想重載一個(gè)函數(shù),考慮一下將函數(shù)的名字寫成帶有一定的參數(shù)信息,比如AppendString和AddInt,而不是用一個(gè)Append或者Add去處理各個(gè)類型。
- 函數(shù)的默認(rèn)參數(shù) -
原則:盡量不使用函數(shù)的默認(rèn)參數(shù),這可能會(huì)導(dǎo)致和重載函數(shù)混亂。
- 不允許使用變長(zhǎng)數(shù)組和alloca函數(shù) -
原則:變長(zhǎng)數(shù)組并不是C++標(biāo)準(zhǔn)的一部分,部分編譯比如MSVC并不對(duì)其進(jìn)行支持。
- 友元 -
原則:如果可能,盡可能的使用public方法來進(jìn)行訪問控制的溝通。
- 異常 -
(這里爭(zhēng)議比較大,C++異常我實(shí)在不好評(píng)價(jià)什么,看你自己了。)
原則:我們不使用異常。
- RTTI -
原則:盡量不使用RTTI,除非在某些特殊的情況比如單元測(cè)試的時(shí)候需要便捷也不考慮性能效率的測(cè)試父類型或者判斷類型。
因?yàn)镽TTI會(huì)時(shí)代碼難以維護(hù),它會(huì)使代碼遍地都是if和case,而且如果要頻繁的檢查對(duì)象類型就證明目前的架構(gòu)設(shè)計(jì)其實(shí)是有問題的。
- 類型轉(zhuǎn)換 -
原則:使用C++風(fēng)格的類型轉(zhuǎn)換,而不是使用C語言風(fēng)格的。
這樣可以使在批量搜索代碼的時(shí)候快速定位類型轉(zhuǎn)換關(guān)鍵字,也更加鮮明,主要是符合C++的特性。
1、使用static_cast進(jìn)行數(shù)值的轉(zhuǎn)換,比如int64轉(zhuǎn)換int32,或是顯示將一個(gè)類的指針轉(zhuǎn)換為它的子類的指針;
2、使用const_cast去掉const特性;
3、使用reinterpret_cast做類似C語言的不安全強(qiáng)制轉(zhuǎn)換;
- IO和Stream -
原則:如果成員有IO和Stream操作,則它應(yīng)該提供一套IO的ReadWrite接口。
- ++i和i++ -
原則:忽略返回值的時(shí)候,前綴形式(++i)至少不會(huì)比后綴形式效率低,甚至更高,因?yàn)楹缶Y形式自加或者自減,需要保存i的原始值作為表達(dá)式的值,如果i是迭代器或者其他被標(biāo)準(zhǔn)類型,復(fù)制操作的代價(jià)可能更大。
所以我們應(yīng)該盡量使用前綴++形式,特別是使用STL迭代器的情況。
- 數(shù)和類型 -
原則:0用于整數(shù),0.0(f)用于浮點(diǎn)數(shù),nullptr用于指針,'