《Google的C++編碼規(guī)范》閱讀隨筆 —— 第四章 智能指針和其他C++特性

1.智能指針
如果確實需要使用智能只恨的話,scoped_ptr完全可以勝任。早費城特殊的情況下,例如對STL容器中對象,你應(yīng)該只使用std::tr1::shared_ptr,任何情況下都不要使用auto_ptr。

只能指針看上去是指針,其實是附加了語義的對象,以scoped_ptr為例,scoped_ptr被銷毀時,刪除了他所指向的對象。shared_ptr也是如此,而且,shared_ptr使用了引用計數(shù)。從而只有當(dāng)它所指向的最后一個對象被銷毀時,指針才會被刪除。

一般來說,我們傾向于設(shè)計對象隸屬明確的代碼,最明確的對象隸屬是根本不使用指針,直接將獨享作為一個域或局部變量使用。另一種極端是引用計數(shù)指針不屬于任何對象,這樣設(shè)計的問題容易導(dǎo)致循環(huán)引用或其他導(dǎo)致對象無法刪除的詭異條件,而且在每一次拷貝或賦值時連原子操作都會很慢。

雖然不推薦這么做,但有些時候,引用計數(shù)指針是最簡單有效的解決方案。

其他C++特性
1.引用參數(shù)
所有按引用傳遞的參數(shù)必須加上const

定義:在C語言中,如果函數(shù)需要修改變量的值,形參必須為指針,如int foo(int *pval).
在C++中,函數(shù)還可以聲明引用參數(shù):int foo(int &val).

優(yōu)點:定義形參為引用避免了想(*pval)++這樣丑陋的代碼,像拷貝構(gòu)造函數(shù)這樣的應(yīng)用也是必須的,而且不像指針那樣不接受空指針NULL;

缺點:容易引起誤解,因為引用在語法上是值卻擁有指針的語義。

結(jié)論:函數(shù)形參表中,所有引用必須是const:
void Foo(const string &in, string *out);

事實上這是一個硬性約定,輸入?yún)?shù)為值或者常數(shù)引用,輸出參數(shù)為指針;輸入?yún)?shù)可以是常數(shù)指針,但不可能使用非常數(shù)引用形參

在強調(diào)參數(shù)不是拷貝而來,在對象生命周期內(nèi)必須一直存在隨時可以使用的常數(shù)指針,最好將這些在注釋中詳細說明。bind2nd和mem_fun等STL適配器不接受引用形參,這種情況下也必須以指針形參聲明函數(shù)

2.函數(shù)重載
盡在輸入?yún)?shù)類型不同、功能相同時使用用重載函數(shù)(含構(gòu)造函數(shù)),不要使用函數(shù)重載模仿缺省函數(shù)參數(shù)

定義:可以定義一個函數(shù)參數(shù)類型為const string&,并定義其重載函數(shù)類型為 const char*。
class MyClass{
public:
void Analyze(const string &text);
void Analyze(const char *text, size_t textlen);
};

優(yōu)點:通過重載不同參數(shù)的同名函數(shù),令代碼更加直觀,模板化代碼需要重載,同時為訪問者帶來便利。

缺點:限制使用重載的一個原因是在特定的調(diào)用處很難確定到底調(diào)用的是哪一個函數(shù),另一個原因是當(dāng)派生類只重載函數(shù)的部分變量會令很多人對繼承語義產(chǎn)生困惑。此外在閱讀庫的客戶端代碼是,因缺省函數(shù)參數(shù)造成不必要的費解。

結(jié)論:如果你想重載一個函數(shù),考慮讓函數(shù)名包含參數(shù)信息,例如,使用AppendString(),AppendInt(),而不是Append()。

3.缺省參數(shù)
禁止使用缺省函數(shù)參數(shù)

優(yōu)點:經(jīng)常用到一個函數(shù)帶有大量缺省值,偶爾重寫一下這些值,缺省參數(shù)為很少涉及的例外情況提供了少定義一些函數(shù)的方便。

缺點:大家經(jīng)常會通過查看現(xiàn)有代碼確定如何使用API,缺省參數(shù)會使得復(fù)制粘貼以前的代碼難以呈現(xiàn)所有參數(shù),當(dāng)缺省參數(shù)不適用于新代碼時可能導(dǎo)致重大問題

結(jié)論:所有參數(shù)必須明確指定,強制程序員考慮API和傳入的各參數(shù)值,避免使用可能不為程序員所知的缺省參數(shù)。

4.變長數(shù)組和alloca
禁止使用變長數(shù)組和alloca()。

優(yōu)點:變長數(shù)組具有渾然天成的語法,變長數(shù)組和alloca()也都很高效

缺點:變長數(shù)組和alloca()不是標(biāo)準(zhǔn)C++的組成部分,更重要的是,它們在堆棧(stack)上根據(jù)數(shù)據(jù)分配大小可能導(dǎo)致難以發(fā)現(xiàn)的內(nèi)存泄漏:在我的機器上運行的好好地,到了產(chǎn)品中卻莫名其妙的掛掉了

結(jié)論:使用安全的分配器(allocator),如scoped_ptr/scoped_array。

5.友元
允許合理使用友元類及友元函數(shù)。

通常將友元定義在同一文件下,避免讀者跑到其他文件中查找其對某個類私有成員的使用。經(jīng)常用到友元的一個地方是將FooBuilder聲明為Foo的友元,F(xiàn)ooBuilder易邊可以正確構(gòu)造Foo的內(nèi)部狀態(tài),而無需將該狀態(tài)暴露出來。某些情況下,講一個單元測試用類聲明為待測類的友元會很方便

友元延伸了(但沒有打破)類的封裝界限,當(dāng)你希望只允許一個類訪問某個成員時,使用友元通常比將其聲明為public要好得多。當(dāng)然,大多數(shù)類應(yīng)該只提供公共成員與其交互。

6.異常
不要使用C++異常

優(yōu)點:
1)異常允許上層應(yīng)用決定如何處理在底層嵌套函數(shù)中發(fā)生的“不可能發(fā)生“的失敗,不想出錯代碼的記錄那么模糊費解。
2)應(yīng)用于其他很多現(xiàn)代語言中,引入異常使得C++與Python、java以及其他與C++相近的語言更加兼容
3)組多C++第三方庫使用異常,關(guān)閉異常將導(dǎo)致難以與之結(jié)合
4)異常是解決構(gòu)造函數(shù)失敗的唯一方案,雖然可以通過工廠函數(shù)或Init()方法模擬異常,但他們分別需要堆分配或新的“非法”狀態(tài)
5)則測試框架找那個,異常確實很好用

缺點:
1)在現(xiàn)有函數(shù)中添加throw語句時,必須檢查所有調(diào)用處,即使他們致至少具有基本的異常安全維護,或者程序正常結(jié)束,永遠不可能捕獲該異常。例如:如果f()依次調(diào)用了g()和h(),h跑出被f捕獲的異常,g就要當(dāng)心了,避免沒有完全清理
2)通俗一點說,異常會導(dǎo)致程序控制流通過查看代碼無法確定:函數(shù)有可能在不確定的地方返回,從而導(dǎo)致代碼管理和調(diào)試困難,當(dāng)然,你可以通過規(guī)定何時何地如何使用異常來最小化的降低開銷,卻給開發(fā)人員帶來掌握這些規(guī)定的負擔(dān)
3)異常安全需要RAII和不同編碼實踐。輕松、正確編寫異常安全代碼需要大量支撐,允許使用異常
4)加入異常使二進制執(zhí)行代碼體積變大,增加了編譯時長,還可能增加地址空間壓力
5)異常的實用性可能會刺激開發(fā)人員在不恰當(dāng)?shù)臅r候拋出異常,或者在不安全的地方從異常中恢復(fù),例如:非法用戶輸入可能導(dǎo)致拋出異常。如果允許使用異常會使得這樣一篇編程風(fēng)格指南長出很多。

結(jié)論:表面上看,使用利大于弊,尤其在新項目中,然而,對于現(xiàn)有代碼,引入異常會牽連到所有依賴代碼。如果允許異常在新項目中使用,在跟以前沒有使用的代碼整合時也是一個麻煩,因為google現(xiàn)有的大多數(shù)C++代碼都沒有異常處理

實際使用中還是自己決定是否使用異常

7.運行時識別(Run-time Type Information, RTTI)
我們禁止使用RTTI

定義:RTTI允許程序員在運行時識別C++對象的類型

優(yōu)點:RTTI在某些單元測試中非常有用,如在進行工廠類測試時用于檢驗一個新建對象是否為期望的動態(tài)類型。除了測試外,極少用到

缺點:運行時識別類型意味著設(shè)計本身有問題,如果你需要在運行期間確定一個對象的類型,這通常說明你需要重新考慮你的類的設(shè)計

結(jié)論:出單元測試外,不要使用RTTI,如果你發(fā)現(xiàn)需要所寫代碼因?qū)ο箢愋筒煌鴦幼鞲鳟惖脑?,考慮換一種方式識別對象類型。

虛函數(shù)可以實現(xiàn)隨子類類型不同而執(zhí)行不同代碼,工作都是交給對象本身去完成

如果工作在對象之外的代碼中完成,考慮雙重分發(fā)方案,如Visitor模式,可以方便的在對象本身之外確定類的類型

如果你認為上面的方法你掌握不了,可以使用RTTI,但務(wù)必請三思,不要去手工實現(xiàn)一個貌似RTTI的方案。我們反對使用RTTI,同樣反對貼上類型標(biāo)簽的貌似類繼承的替代方案(譯者注:使用就使用吧,不使用也不要造輪子)

8.類型轉(zhuǎn)換
使用static_cast<>()等C++的類型轉(zhuǎn)換,不要使用int y = (int)x; or int y = int(x);

定義:C++引入了有別于C的不同類型的類型轉(zhuǎn)換操作

優(yōu)點:C語言的類型轉(zhuǎn)化問題在于操作比較含糊:有時是在做強制轉(zhuǎn)換((int)3.5),有時是在做類型轉(zhuǎn)換((int)"hello").另外,C++的類型轉(zhuǎn)換查找更加容易,更醒目

缺點:語法比較惡心

結(jié)論:使用C++風(fēng)格而不要使用C風(fēng)格類型轉(zhuǎn)換
1)static_cast:和C風(fēng)格轉(zhuǎn)換相似可做值的強制轉(zhuǎn)換,或指針的父類到子類的明確向上轉(zhuǎn)換
2)const_cast:移除const屬性
3)reinterpret_cast:指針類型和整型或其他指針間不安全的相互轉(zhuǎn)換,僅在你對所做的一切了然于心時使用
4)dynamic_cast:除測試外不要使用,除單元測試外,如果你需要在運行時確定類型信息,說明設(shè)計有缺陷

9.流
只在記錄日志時使用流。

定義:流是printf和scanf的替代

優(yōu)點:有了流,在輸出時不需要關(guān)心對象的類型,不用擔(dān)心格式化字符串與參數(shù)列表不匹配(雖然在gcc中使用printf也不存在這個問題),打開。關(guān)閉對應(yīng)文件時,流可以自動構(gòu)造、析構(gòu)。

缺點:流使得pread()等功能函數(shù)很難執(zhí)行,如果不使用printf之類的函數(shù)而是使用流很那對格式進行操作(尤其是常用的格式字符串%.*s),流不支持字符串操作符重新定序(%1s),而這一點對國際化很有用。

結(jié)論:不要使用流,除非是日志接口需要,使用printf之類的代替
使用流還有很多利弊,代碼一致性勝過一切,不要再代碼中使用流

拓展:
我們希望在任何時候只使用一種確定IO類型,使代碼在所有IO處保持一致。
目前多數(shù)還是決定printf+read/write

10.前置自增和自減
對于迭代器和其他模板對象使用前綴形式(++i)的自增、自減運算符

優(yōu)點:不考慮返回值的話,前置自增通常比后置自增效率更高,因為后置自增需要對表達式的值i進行一次拷貝。如果i是迭代器或者非數(shù)值類型,拷貝的代價比較大。

11.const的使用
我們強烈建議在任何可以使用的情況下都要使用const

定義:在聲明的變量或參數(shù)前加上關(guān)鍵字const用于指明變量值不可修改,為類中的函數(shù)加上const(const放在形參的括號之后)限定表明該函數(shù)不會修改類成員變量的狀態(tài)。

優(yōu)點:人們更容易理解變量是如何使用的,編輯器可以更好地進行類型檢查、更好的生成代碼。即使在無鎖的多線程編程中,人們也知道什么樣的函數(shù)是安全的

缺點:如果你向一個函數(shù)傳入const變量,函數(shù)原型中也必須是const的(否則需要const_cast類型轉(zhuǎn)換),在調(diào)用庫函數(shù)時這尤其是個麻煩

結(jié)論:const變量、數(shù)據(jù)成員、函數(shù)和參數(shù)為編譯時類型檢查增加了一層保障,更好的盡早發(fā)現(xiàn)錯誤。
1)如果函數(shù)不會修改傳入的引用或指針類型的參數(shù),這樣的參數(shù)應(yīng)該為const
2)盡可能將函數(shù)聲明為const,訪問函數(shù)應(yīng)該總是const,其他函數(shù)如果不會修改任何數(shù)據(jù)成員也應(yīng)該是const,不要返回對數(shù)據(jù)成員的非const引用或指針
3)如果數(shù)據(jù)成員在對象構(gòu)造之后不再改變,可將其定義為const

然而,也不要對const過度使用,想const int* const* const x;就有些過了,其實寫為const int** x;就可以了。

關(guān)鍵字mutable可以使用,但是在多線程中是不安全的,使用時首先要考慮線程安全

const位置:
有人喜歡int const foo形勢不喜歡const int foo;
這里我們提倡const在前,因為const修飾的是int。更符合自然語言

12.整型
C++內(nèi)建整型中,唯一用到的是int,如果程序中需要不同大小的變量,可以使用<stdint.h>中的精確寬度的整型,如int16_t;

定義:C++沒有指定整型的大小,通常人們?nèi)藶閟hort是16位,int是32位,龍是32wei,long long是64位。

優(yōu)點:保持聲明統(tǒng)一。

缺點:C++中整型大小因編譯器和體系結(jié)構(gòu)的不同而不同。

結(jié)論:<stdint.h>定義了int16_t,uint32_t,int64_t等整型,在需要確定大小的整形師可以使用它們代替short,unsigned long long 等,在C整型中,只使用int。適當(dāng)情況下,推薦使用標(biāo)準(zhǔn)類型如size_t和ptrdiff_t。

最常使用的證書通常不會太大,需要64位整型的話,可以使用int64_t or uint64_t。

無符號整型:
推薦使用無符號整型表示非負整數(shù)。類型表明了數(shù)值取值形式。但是,在C語言中,這一優(yōu)點被bugs所淹沒,如:
for(unsigned int i = foo.Length() - 1; i >= 0; --i)
這個循環(huán)永不終止。

因此,使用斷言聲明變量為非負數(shù),不要使用無符號整型。

13.64位下的可移植性
代碼在64位和32位的系統(tǒng)中,原則上應(yīng)該都比較友好,尤其對輸出、比較、結(jié)構(gòu)對齊來說:
1)printf()指定的一些類型在32位和64位系統(tǒng)上可移植性不是很好,C99標(biāo)準(zhǔn)定義了一些可移植的格式。不幸的是,MSVC7.1并非全部支持。而且標(biāo)準(zhǔn)中也有所遺漏
2)記住sizeof(void*) != sizeof(int),如果需要一個指針大小的證書要使用intptr_t。
3)需要對結(jié)構(gòu)對齊加以留心,尤其是對于存儲在磁盤上的結(jié)構(gòu)體。在64位系統(tǒng)中,任何擁有int64_t/uint64_t成員的類或結(jié)構(gòu)體將默認被處理為8字節(jié)對齊。如果32位和64位代碼共用磁盤上的結(jié)構(gòu)體,需要確保兩種體系結(jié)構(gòu)下的結(jié)構(gòu)體的對齊一致。大多數(shù)編譯器提供了調(diào)整結(jié)構(gòu)體對齊的方案。gcc中可使用attribute((packed)),MSVC提供了#pragma pack()和_declspec(align())(注,解決方案的項目屬性里也可以直接設(shè)置)
4)創(chuàng)建64位常量時使用LL或ULL作為后綴,如:
int64_t my_value = 0x123456789ll;
uint64_t my_mask = 3ULL << 48;
5)如果你確實需要32位和64位系統(tǒng)具有不同代碼,可以在代碼變量前使用(盡量不要這么做,使用時盡量使修改局部化)

14.預(yù)處理宏
使用宏時要謹慎,盡量以內(nèi)聯(lián)函數(shù)、枚舉和常量代替之。
下面給出的用法模式可以避免一些使用宏的問題,供使用宏時參考:
1)不要在h文件中定義宏
2)使用前正確#define,使用后正確#undef
3)不要只是對已經(jīng)存在的宏使用#undef,選擇一個不會沖突的名稱;
4)不使用會導(dǎo)致不穩(wěn)定的C++構(gòu)造的宏,至少文檔說明其行為

15.0和NULL
整數(shù)用0,實數(shù)0.0,指針NULL字符串'\0'

16.sizeof
盡可能用sizeof(varname)代替sizeof(type)
因為使用sizeof(varname)可以再代碼改變是自動同步變量類型

17.Boost庫
只使用Boost中被認可的庫。
目前比較成熟的Boost庫的子集有:
1)Compressed Pair : boost/compressed_pair.hpp

  1. Pointer Container: boost/ptr_container 不包括ptr_array.hpp和序列化(serialization)
    當(dāng)然,不必拘泥于該規(guī)則

總結(jié):
1)對于智能指針,安全第一、方便第二,盡可能局部化(scoped_ptr)
2)引用形參加上const,否則使用指針形參
3)函數(shù)重載要清晰,易讀
4)鑒于容易誤用,禁止使用缺省函數(shù)參數(shù)(值得商榷)
5)禁止使用變長數(shù)組
6)合理使用友元
7)為了方便代碼管理,禁止使用異常(值得商榷)
8)禁止使用RTTI,否則重新設(shè)計代碼吧
9)使用C++風(fēng)格的類型轉(zhuǎn)化,除單元測試外,不要使用dynamic_cast
10)使用流還是printf+read/write, it is a problem
11)能用前置自增,自減就不用后置自增,自減
12)const能用則用,提倡const在前
13)使用確定大小的整型,除位組外不要使用無符號型
14)格式化輸出及結(jié)構(gòu)對齊時,注意32位和64位的系統(tǒng)差異
15)除字符串化,鏈接外盡量避免使用宏
16)字符串'\0',整數(shù)0,實數(shù)0.0,指針NULL
17)用sizeof(varname)代替sizeof(type)
18)只使用Boost中被認可的庫

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內(nèi)容