www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁(yè) > 芯聞號(hào) > 充電吧
[導(dǎo)讀]·背景Google的開(kāi)源項(xiàng)目大多使用C++開(kāi)發(fā)。每一個(gè)C++程序員也都知道,C++具有很多強(qiáng)大的語(yǔ)言特性,但這種強(qiáng)大不可避免的導(dǎo)致它的復(fù)雜,這種復(fù)雜會(huì)使得代碼更易于出現(xiàn)bug、難于閱讀和維護(hù)。本指南的

·背景

Google的開(kāi)源項(xiàng)目大多使用C++開(kāi)發(fā)。每一個(gè)C++程序員也都知道,C++具有很多強(qiáng)大的語(yǔ)言特性,但這種強(qiáng)大不可避免的導(dǎo)致它的復(fù)雜,這種復(fù)雜會(huì)使得代碼更易于出現(xiàn)bug、難于閱讀和維護(hù)。

本指南的目的是通過(guò)詳細(xì)闡述在C++編碼時(shí)要怎樣寫(xiě)、不要怎樣寫(xiě)來(lái)規(guī)避其復(fù)雜性。這些規(guī)則可在允許代碼有效使用C++語(yǔ)言特性的同時(shí)使其易于管理。

風(fēng)格,也被視為可讀性,主要指稱管理C++代碼的習(xí)慣。使用術(shù)語(yǔ)風(fēng)格有點(diǎn)用詞不當(dāng),因?yàn)檫@些習(xí)慣遠(yuǎn)不止源代碼文件格式這么簡(jiǎn)單。

使代碼易于管理的方法之一是增強(qiáng)代碼一致性,讓別人可以讀懂你的代碼是很重要的,保持統(tǒng)一編程風(fēng)格意味著可以輕松根據(jù)“模式匹配”規(guī)則推斷各種符號(hào)的含義。創(chuàng)建通用的、必需的習(xí)慣用語(yǔ)和模式可以使代碼更加容易理解,在某些情況下改變一些編程風(fēng)格可能會(huì)是好的選擇,但我們還是應(yīng)該遵循一致性原則,盡量不這樣去做。

本指南的另一個(gè)觀點(diǎn)是C++特性的臃腫。C++是一門包含大量高級(jí)特性的巨型語(yǔ)言,某些情況下,我們會(huì)限制甚至禁止使用某些特性使代碼簡(jiǎn)化,避免可能導(dǎo)致的各種問(wèn)題,指南中列舉了這類特性,并解釋說(shuō)為什么這些特性是被限制使用的。

由Google開(kāi)發(fā)的開(kāi)源項(xiàng)目將遵照本指南約定。

注意:本指南并非C++教程,我們假定讀者已經(jīng)對(duì)C++非常熟悉。

·頭文件

通常,每一個(gè).cc文件(C++的源文件)都有一個(gè)對(duì)應(yīng)的.h文件(頭文件),也有一些例外,如單元測(cè)試代碼和只包含main()的.cc文件。

正確使用頭文件可令代碼在可讀性、文件大小和性能上大為改觀。

下面的規(guī)則將引導(dǎo)你規(guī)避使用頭文件時(shí)的各種麻煩。

1.#define的保護(hù)

所有頭文件都應(yīng)該使用#define防止頭文件被多重包含(multipleinclusion),命名格式當(dāng)是:

為保證唯一性,頭文件的命名應(yīng)基于其所在項(xiàng)目源代碼樹(shù)的全路徑。例如,項(xiàng)目foo中的頭文件foo/src/bar/baz.h按如下方式保護(hù):

#ifndefFOO_BAR_BAZ_H_
#defineFOO_BAR_BAZ_H_
...
#endif//FOO_BAR_BAZ_H_

2.頭文件依賴

使用前置聲明(forwarddeclarations)盡量減少.h文件中#include的數(shù)量。

當(dāng)一個(gè)頭文件被包含的同時(shí)也引入了一項(xiàng)新的依賴(dependency),只要該頭文件被修改,代碼就要重新編譯。如果你的頭文件包含了其他頭文件,這些頭文件的任何改變也將導(dǎo)致那些包含了你的頭文件的代碼重新編譯。因此,我們寧可盡量少包含頭文件,尤其是那些包含在其他頭文件中的。

使用前置聲明可以顯著減少需要包含的頭文件數(shù)量。舉例說(shuō)明:頭文件中用到類File,但不需要訪問(wèn)File的聲明,則頭文件中只需前置聲明classFile;無(wú)需#include"file/base/file.h"。

在頭文件如何做到使用類Foo而無(wú)需訪問(wèn)類的定義?

1)將數(shù)據(jù)成員類型聲明為Foo*或Foo&;

2)參數(shù)、返回值類型為Foo的函數(shù)只是聲明(但不定義實(shí)現(xiàn));

3)靜態(tài)數(shù)據(jù)成員的類型可以被聲明為Foo,因?yàn)殪o態(tài)數(shù)據(jù)成員的定義在類定義之外。

另一方面,如果你的類是Foo的子類,或者含有類型為Foo的非靜態(tài)數(shù)據(jù)成員,則必須為之包含頭文件。

有時(shí),使用指針成員(pointermembers,如果是scoped_ptr更好)替代對(duì)象成員(objectmembers)的確更有意義。然而,這樣的做法會(huì)降低代碼可讀性及執(zhí)行效率。如果僅僅為了少包含頭文件,還是不要這樣替代的好。

當(dāng)然,.cc文件無(wú)論如何都需要所使用類的定義部分,自然也就會(huì)包含若干頭文件。

譯者注:能依賴聲明的就不要依賴定義。

3.內(nèi)聯(lián)函數(shù)

只有當(dāng)函數(shù)只有10行甚至更少時(shí)才會(huì)將其定義為內(nèi)聯(lián)函數(shù)(inlinefunction)。

定義(Definition):當(dāng)函數(shù)被聲明為內(nèi)聯(lián)函數(shù)之后,編譯器可能會(huì)將其內(nèi)聯(lián)展開(kāi),無(wú)需按通常的函數(shù)調(diào)用機(jī)制調(diào)用內(nèi)聯(lián)函數(shù)。

優(yōu)點(diǎn):當(dāng)函數(shù)體比較小的時(shí)候,內(nèi)聯(lián)該函數(shù)可以令目標(biāo)代碼更加高效。對(duì)于存取函數(shù)(accessor、mutator)以及其他一些比較短的關(guān)鍵執(zhí)行函數(shù)。

缺點(diǎn):濫用內(nèi)聯(lián)將導(dǎo)致程序變慢,內(nèi)聯(lián)有可能是目標(biāo)代碼量或增或減,這取決于被內(nèi)聯(lián)的函數(shù)的大小。內(nèi)聯(lián)較短小的存取函數(shù)通常會(huì)減少代碼量,但內(nèi)聯(lián)一個(gè)很大的函數(shù)(譯者注:如果編譯器允許的話)將戲劇性的增加代碼量。在現(xiàn)代處理器上,由于更好的利用指令緩存(instructioncache),小巧的代碼往往執(zhí)行更快。

結(jié)論:一個(gè)比較得當(dāng)?shù)奶幚硪?guī)則是,不要內(nèi)聯(lián)超過(guò)10行的函數(shù)。對(duì)于析構(gòu)函數(shù)應(yīng)慎重對(duì)待,析構(gòu)函數(shù)往往比其表面看起來(lái)要長(zhǎng),因?yàn)橛幸恍╇[式成員和基類析構(gòu)函數(shù)(如果有的話)被調(diào)用!

另一有用的處理規(guī)則:內(nèi)聯(lián)那些包含循環(huán)或switch語(yǔ)句的函數(shù)是得不償失的,除非在大多數(shù)情況下,這些循環(huán)或switch語(yǔ)句從不執(zhí)行。

重要的是,虛函數(shù)和遞歸函數(shù)即使被聲明為內(nèi)聯(lián)的也不一定就是內(nèi)聯(lián)函數(shù)。通常,遞歸函數(shù)不應(yīng)該被聲明為內(nèi)聯(lián)的(譯者注:遞歸調(diào)用堆棧的展開(kāi)并不像循環(huán)那么簡(jiǎn)單,比如遞歸層數(shù)在編譯時(shí)可能是未知的,大多數(shù)編譯器都不支持內(nèi)聯(lián)遞歸函數(shù))。析構(gòu)函數(shù)內(nèi)聯(lián)的主要原因是其定義在類的定義中,為了方便抑或是對(duì)其行為給出文檔。

4.-inl.h文件

復(fù)雜的內(nèi)聯(lián)函數(shù)的定義,應(yīng)放在后綴名為-inl.h的頭文件中。

在頭文件中給出內(nèi)聯(lián)函數(shù)的定義,可令編譯器將其在調(diào)用處內(nèi)聯(lián)展開(kāi)。然而,實(shí)現(xiàn)代碼應(yīng)完全放到.cc文件中,我們不希望.h文件中出現(xiàn)太多實(shí)現(xiàn)代碼,除非這樣做在可讀性和效率上有明顯優(yōu)勢(shì)。

如果內(nèi)聯(lián)函數(shù)的定義比較短小、邏輯比較簡(jiǎn)單,其實(shí)現(xiàn)代碼可以放在.h文件中。例如,存取函數(shù)的實(shí)現(xiàn)理所當(dāng)然都放在類定義中。出于實(shí)現(xiàn)和調(diào)用的方便,較復(fù)雜的內(nèi)聯(lián)函數(shù)也可以放到.h文件中,如果你覺(jué)得這樣會(huì)使頭文件顯得笨重,還可以將其分離到單獨(dú)的-inl.h中。這樣即把實(shí)現(xiàn)和類定義分離開(kāi)來(lái),當(dāng)需要時(shí)包含實(shí)現(xiàn)所在的-inl.h即可。

-inl.h文件還可用于函數(shù)模板的定義,從而使得模板定義可讀性增強(qiáng)。

要提醒的一點(diǎn)是,-inl.h和其他頭文件一樣,也需要#define保護(hù)。

5.函數(shù)參數(shù)順序(FunctionParameterOrdering)

定義函數(shù)時(shí),參數(shù)順序?yàn)椋狠斎雲(yún)?shù)在前,輸出參數(shù)在后。

C/C++函數(shù)參數(shù)分為輸入?yún)?shù)和輸出參數(shù)兩種,有時(shí)輸入?yún)?shù)也會(huì)輸出(譯者注:值被修改時(shí))。輸入?yún)?shù)一般傳值或常數(shù)引用(constreferences),輸出參數(shù)或輸入/輸出參數(shù)為非常數(shù)指針(non-constpointers)。對(duì)參數(shù)排序時(shí),將所有輸入?yún)?shù)置于輸出參數(shù)之前。不要僅僅因?yàn)槭切绿砑拥膮?shù),就將其置于最后,而應(yīng)該依然置于輸出參數(shù)之前。

這一點(diǎn)并不是必須遵循的規(guī)則,輸入/輸出兩用參數(shù)(通常是類/結(jié)構(gòu)體變量)混在其中,會(huì)使得規(guī)則難以遵循。

6.包含文件的名稱及次序

將包含次序標(biāo)準(zhǔn)化可增強(qiáng)可讀性、避免隱藏依賴(hiddendependencies,譯者注:隱藏依賴主要是指包含的文件中編譯時(shí)),次序如下:C庫(kù)、C++庫(kù)、其他庫(kù)的.h、項(xiàng)目?jī)?nèi)的.h。

項(xiàng)目?jī)?nèi)頭文件應(yīng)按照項(xiàng)目源代碼目錄樹(shù)結(jié)構(gòu)排列,并且避免使用UNIX文件路徑.(當(dāng)前目錄)和..(父目錄)。例如,google-awesome-project/src/base/logging.h應(yīng)像這樣被包含:

#include"base/logging.h"

dir/foo.cc的主要作用是執(zhí)行或測(cè)試dir2/foo2.h的功能,foo.cc中包含頭文件的次序如下:

dir2/foo2.h(優(yōu)先位置,詳情如下)
C系統(tǒng)文件
C++系統(tǒng)文件
其他庫(kù)頭文件
本項(xiàng)目?jī)?nèi)頭文件

這種排序方式可有效減少隱藏依賴,我們希望每一個(gè)頭文件獨(dú)立編譯。最簡(jiǎn)單的實(shí)現(xiàn)方式是將其作為第一個(gè).h文件包含在對(duì)應(yīng)的.cc中。

dir/foo.cc和dir2/foo2.h通常位于相同目錄下(像base/basictypes_unittest.cc和base/basictypes.h),但也可在不同目錄下。

相同目錄下頭文件按字母序是不錯(cuò)的選擇。

舉例來(lái)說(shuō),google-awesome-project/src/foo/internal/fooserver.cc的包含次序如下:

#include"foo/public/fooserver.h"http://優(yōu)先位置

#include

#include

#include"base/basictypes.h"
#include"base/commandlineflags.h"
#include"foo/public/bar.h"

______________________________________

譯者:英語(yǔ)不太好,翻譯的也就不太好。這一篇主要提到的是頭文件的一些規(guī)則,總結(jié)一下:

1.避免多重包含是學(xué)編程時(shí)最基本的要求;

2.前置聲明是為了降低編譯依賴,防止修改一個(gè)頭文件引發(fā)多米諾效應(yīng);

3.內(nèi)聯(lián)函數(shù)的合理使用可提高代碼執(zhí)行效率;

4.-inl.h可提高代碼可讀性(一般用不到吧:D);

5.標(biāo)準(zhǔn)化函數(shù)參數(shù)順序可以提高可讀性和易維護(hù)性(對(duì)函數(shù)參數(shù)的堆棧空間有輕微影響,我以前大多是相同類型放在一起);

6.包含文件的名稱使用.和..雖然方便卻易混亂,使用比較完整的項(xiàng)目路徑看上去很清晰、很條理,包含文件的次序除了美觀之外,最重要的是可以減少隱藏依賴,使每個(gè)頭文件在“最需要編譯”(對(duì)應(yīng)源文件處:D)的地方編譯,有人提出庫(kù)文件放在最后,這樣出錯(cuò)先是項(xiàng)目?jī)?nèi)的文件,頭文件都放在對(duì)應(yīng)源文件的最前面,這一點(diǎn)足以保證內(nèi)部錯(cuò)誤的及時(shí)發(fā)現(xiàn)了。

·作用域

1.命名空間(Namespaces)

在.cc文件中,提倡使用不具名的命名空間(unnamednamespaces,譯者注:不具名的命名空間就像不具名的類一樣,似乎被介紹的很少:-()。使用具名命名空間時(shí),其名稱可基于項(xiàng)目或路徑名稱,不要使用using指示符。

定義:命名空間將全局作用域細(xì)分為不同的、具名的作用域,可有效防止全局作用域的命名沖突。

優(yōu)點(diǎn):命名空間提供了(可嵌套)命名軸線(nameaxis,譯者注:將命名分割在不同命名空間內(nèi)),當(dāng)然,類也提供了(可嵌套)的命名軸線(譯者注:將命名分割在不同類的作用域內(nèi))。

舉例來(lái)說(shuō),兩個(gè)不同項(xiàng)目的全局作用域都有一個(gè)類Foo,這樣在編譯或運(yùn)行時(shí)造成沖突。如果每個(gè)項(xiàng)目將代碼置于不同命名空間中,project1::Foo和project2::Foo作為不同符號(hào)自然不會(huì)沖突。

缺點(diǎn):命名空間具有迷惑性,因?yàn)樗鼈兒皖愐粯犹峁┝祟~外的(可嵌套的)命名軸線。在頭文件中使用不具名的空間容易違背C++的唯一定義原則(OneDefinitionRule(ODR))。

結(jié)論:根據(jù)下文將要提到的策略合理使用命名空間。

1)不具名命名空間(UnnamedNamespaces)

在.cc文件中,允許甚至提倡使用不具名命名空間,以避免運(yùn)行時(shí)的命名沖突:

namespace{//.cc文件中

//命名空間的內(nèi)容無(wú)需縮進(jìn)
enum{UNUSED,EOF,ERROR};//經(jīng)常使用的符號(hào)
boolAtEof(){returnpos_==EOF;}//使用本命名空間內(nèi)的符號(hào)EOF

}//namespace

然而,與特定類關(guān)聯(lián)的文件作用域聲明在該類中被聲明為類型、靜態(tài)數(shù)據(jù)成員或靜態(tài)成員函數(shù),而不是不具名命名空間的成員。像上文展示的那樣,不具名命名空間結(jié)束時(shí)用注釋//namespace標(biāo)識(shí)。

不能在.h文件中使用不具名命名空間。

2)具名命名空間(NamedNamespaces)

具名命名空間使用方式如下:

命名空間將除文件包含、全局標(biāo)識(shí)的聲明/定義以及類的前置聲明外的整個(gè)源文件封裝起來(lái),以同其他命名空間相區(qū)分。

//.h文件
namespacemynamespace{

//所有聲明都置于命名空間中
//注意不要使用縮進(jìn)
classMyClass{
public:
...
voidFoo();
};

}//namespacemynamespace

//.cc文件
namespacemynamespace{

//函數(shù)定義都置于命名空間中
voidMyClass::Foo(){
...
}

}//namespacemynamespace

通常的.cc文件會(huì)包含更多、更復(fù)雜的細(xì)節(jié),包括對(duì)其他命名空間中類的引用等。

#include"a.h"

DEFINE_bool(someflag,false,"dummyflag");

classC;//全局命名空間中類C的前置聲明
namespacea{classA;}//命名空間a中的類a::A的前置聲明

namespaceb{

...codeforb...//b中的代碼

}//namespaceb

不要聲明命名空間std下的任何內(nèi)容,包括標(biāo)準(zhǔn)庫(kù)類的前置聲明。聲明std下的實(shí)體會(huì)導(dǎo)致不明確的行為,如,不可移植。聲明標(biāo)準(zhǔn)庫(kù)下的實(shí)體,需要包含對(duì)應(yīng)的頭文件。

最好不要使用using指示符,以保證命名空間下的所有名稱都可以正常使用。

//禁止——污染命名空間
usingnamespacefoo;

在.cc文件、.h文件的函數(shù)、方法或類中,可以使用using。

//允許:.cc文件中
//.h文件中,必須在函數(shù)、方法或類的內(nèi)部使用
using::foo::bar;

在.cc文件、.h文件的函數(shù)、方法或類中,還可以使用命名空間別名。

//允許:.cc文件中
//.h文件中,必須在函數(shù)、方法或類的內(nèi)部使用

namespacefbz=::foo::bar::baz;

2.嵌套類(NestedClass)

當(dāng)公開(kāi)嵌套類作為接口的一部分時(shí),雖然可以直接將他們保持在全局作用域中,但將嵌套類的聲明置于命名空間中是更好的選擇。

定義:可以在一個(gè)類中定義另一個(gè)類,嵌套類也稱成員類(memberclass)。

classFoo{

private:
//Bar是嵌套在Foo中的成員類
classBar{
...
};

};

優(yōu)點(diǎn):當(dāng)嵌套(成員)類只在被嵌套類(enclosingclass)中使用時(shí)很有用,將其置于被嵌套類作用域作為被嵌套類的成員不會(huì)污染其他作用域同名類。可在被嵌套類中前置聲明嵌套類,在.cc文件中定義嵌套類,避免在被嵌套類中包含嵌套類的定義,因?yàn)榍短最惖亩x通常只與實(shí)現(xiàn)相關(guān)。

缺點(diǎn):只能在被嵌套類的定義中才能前置聲明嵌套類。因此,任何使用Foo::Bar*指針的頭文件必須包含整個(gè)Foo的聲明。

結(jié)論:不要將嵌套類定義為public,除非它們是接口的一部分,比如,某個(gè)方法使用了這個(gè)類的一系列選項(xiàng)。

3.非成員函數(shù)(Nonmember)、靜態(tài)成員函數(shù)(StaticMember)和全局函數(shù)(GlobalFunctions)

使用命名空間中的非成員函數(shù)或靜態(tài)成員函數(shù),盡量不要使用全局函數(shù)。

優(yōu)點(diǎn):某些情況下,非成員函數(shù)和靜態(tài)成員函數(shù)是非常有用的,將非成員函數(shù)置于命名空間中可避免對(duì)全局作用域的污染。

缺點(diǎn):將非成員函數(shù)和靜態(tài)成員函數(shù)作為新類的成員或許更有意義,當(dāng)它們需要訪問(wèn)外部資源或具有重要依賴時(shí)更是如此。

結(jié)論:

有時(shí),不把函數(shù)限定在類的實(shí)體中是有益的,甚至需要這么做,要么作為靜態(tài)成員,要么作為非成員函數(shù)。非成員函數(shù)不應(yīng)依賴于外部變量,并盡量置于某個(gè)命名空間中。相比單純?yōu)榱朔庋b若干不共享任何靜態(tài)數(shù)據(jù)的靜態(tài)成員函數(shù)而創(chuàng)建類,不如使用命名空間。

定義于同一編譯單元的函數(shù),被其他編譯單元直接調(diào)用可能會(huì)引入不必要的耦合和連接依賴;靜態(tài)成員函數(shù)對(duì)此尤其敏感。可以考慮提取到新類中,或者將函數(shù)置于獨(dú)立庫(kù)的命名空間中。

如果你確實(shí)需要定義非成員函數(shù),又只是在.cc文件中使用它,可使用不具名命名空間或static關(guān)聯(lián)(如staticintFoo(){...})限定其作用域。

4.局部變量(LocalVariables)

將函數(shù)變量盡可能置于最小作用域內(nèi),在聲明變量時(shí)將其初始化。

C++允許在函數(shù)的任何位置聲明變量。我們提倡在盡可能小的作用域中聲明變量,離第一次使用越近越好。這使得代碼易于閱讀,易于定位變量的聲明位置、變量類型和初始值。特別是,應(yīng)使用初始化代替聲明+賦值的方式。

inti;
i=f();//壞——初始化和聲明分離
ntj=g();//好——初始化時(shí)聲明

注意:gcc可正確執(zhí)行for(inti=0;i<10;++i)(i的作用域僅限for循環(huán)),因此其他for循環(huán)中可重用i。if和while等語(yǔ)句中,作用域聲明(scopedeclaration)同樣是正確的。

while(constchar*p=strchr(str,'/'))str=p+1;

注意:如果變量是一個(gè)對(duì)象,每次進(jìn)入作用域都要調(diào)用其構(gòu)造函數(shù),每次退出作用域都要調(diào)用其析構(gòu)函數(shù)。

//低效的實(shí)現(xiàn)
for(inti=0;i<1000000;++i){
Foof;//構(gòu)造函數(shù)和析構(gòu)函數(shù)分別調(diào)用1000000次!
f.DoSomething(i);
}

類似變量放到循環(huán)作用域外面聲明要高效的多:

Foof;//構(gòu)造函數(shù)和析構(gòu)函數(shù)只調(diào)用1次
for(inti=0;i<1000000;++i){
f.DoSomething(i);
}

5.全局變量(GlobalVariables)

class類型的全局變量是被禁止的,內(nèi)建類型的全局變量是允許的,當(dāng)然多線程代碼中非常數(shù)全局變量也是被禁止的。永遠(yuǎn)不要使用函數(shù)返回值初始化全局變量。

不幸的是,全局變量的構(gòu)造函數(shù)、析構(gòu)函數(shù)以及初始化操作的調(diào)用順序只是被部分規(guī)定,每次生成有可能會(huì)有變化,從而導(dǎo)致難以發(fā)現(xiàn)的bugs。

因此,禁止使用class類型的全局變量(包括STL的string,vector等等),因?yàn)樗鼈兊某跏蓟樞蛴锌赡軐?dǎo)致構(gòu)造出現(xiàn)問(wèn)題。內(nèi)建類型和由內(nèi)建類型構(gòu)成的沒(méi)有構(gòu)造函數(shù)的結(jié)構(gòu)體可以使用,如果你一定要使用class類型的全局變量,請(qǐng)使用單件模式(singletonpattern)。

對(duì)于全局的字符串常量,使用C風(fēng)格的字符串,而不要使用STL的字符串:

constcharkFrogSays[]="ribbet";

雖然允許在全局作用域中使用全局變量,使用時(shí)務(wù)必三思。大多數(shù)全局變量應(yīng)該是類的靜態(tài)數(shù)據(jù)成員,或者當(dāng)其只在.cc文件中使用時(shí),將其定義到不具名命名空間中,或者使用靜態(tài)關(guān)聯(lián)以限制變量的作用域。

記住,靜態(tài)成員變量視作全局變量,所以,也不能是class類型!

______________________________________

譯者:這一篇主要提到的是作用域的一些規(guī)則,總結(jié)一下:

1..cc中的不具名命名空間可避免命名沖突、限定作用域,避免直接使用using提示符污染命名空間;

2.嵌套類符合局部使用原則,只是不能在其他頭文件中前置聲明,盡量不要public;

3.盡量不用全局函數(shù)和全局變量,考慮作用域和命名空間限制,盡量單獨(dú)形成編譯單元;

4.多線程中的全局變量(含靜態(tài)成員變量)不要使用class類型(含STL容器),避免不明確行為導(dǎo)致的bugs。

作用域的使用,除了考慮名稱污染、可讀性之外,主要是為降低耦合度,提高編譯、執(zhí)行效率。

·類

類是C++中基本的代碼單元,自然被廣泛使用。本節(jié)列舉了在寫(xiě)一個(gè)類時(shí)要做什么、不要做什么。

1.構(gòu)造函數(shù)(Constructor)的職責(zé)

構(gòu)造函數(shù)中只進(jìn)行那些沒(méi)有實(shí)際意義的(trivial,譯者注:簡(jiǎn)單初始化對(duì)于程序執(zhí)行沒(méi)有實(shí)際的邏輯意義,因?yàn)槌蓡T變量的“有意義”的值大多不在構(gòu)造函數(shù)中確定)初始化,可能的話,使用Init()方法集中初始化為有意義的(non-trivial)數(shù)據(jù)。

定義:在構(gòu)造函數(shù)中執(zhí)行初始化操作。

優(yōu)點(diǎn):排版方便,無(wú)需擔(dān)心類是否初始化。

缺點(diǎn):在構(gòu)造函數(shù)中執(zhí)行操作引起的問(wèn)題有:

1)構(gòu)造函數(shù)中不易報(bào)告錯(cuò)誤,不能使用異常。

2)操作失敗會(huì)造成對(duì)象初始化失敗,引起不確定狀態(tài)。

3)構(gòu)造函數(shù)內(nèi)調(diào)用虛函數(shù),調(diào)用不會(huì)派發(fā)到子類實(shí)現(xiàn)中,即使當(dāng)前沒(méi)有子類化實(shí)現(xiàn),將來(lái)仍是隱患。

4)如果有人創(chuàng)建該類型的全局變量(雖然違背了上節(jié)提到的規(guī)則),構(gòu)造函數(shù)將在main()之前被調(diào)用,有可能破壞構(gòu)造函數(shù)中暗含的假設(shè)條件。例如,gflags尚未初始化。

結(jié)論:如果對(duì)象需要有意義的(non-trivial)初始化,考慮使用另外的Init()方法并(或)增加一個(gè)成員標(biāo)記用于指示對(duì)象是否已經(jīng)初始化成功。

2.默認(rèn)構(gòu)造函數(shù)(DefaultConstructors)

如果一個(gè)類定義了若干成員變量又沒(méi)有其他構(gòu)造函數(shù),需要定義一個(gè)默認(rèn)構(gòu)造函數(shù),否則編譯器將自動(dòng)生產(chǎn)默認(rèn)構(gòu)造函數(shù)。

定義:新建一個(gè)沒(méi)有參數(shù)的對(duì)象時(shí),默認(rèn)構(gòu)造函數(shù)被調(diào)用,當(dāng)調(diào)用new[](為數(shù)組)時(shí),默認(rèn)構(gòu)造函數(shù)總是被調(diào)用。

優(yōu)點(diǎn):默認(rèn)將結(jié)構(gòu)體初始化為“不可能的”值,使調(diào)試更加容易。

缺點(diǎn):對(duì)代碼編寫(xiě)者來(lái)說(shuō),這是多余的工作。

結(jié)論:

如果類中定義了成員變量,沒(méi)有提供其他構(gòu)造函數(shù),你需要定義一個(gè)默認(rèn)構(gòu)造函數(shù)(沒(méi)有參數(shù))。默認(rèn)構(gòu)造函數(shù)更適合于初始化對(duì)象,使對(duì)象內(nèi)部狀態(tài)(internalstate)一致、有效。

提供默認(rèn)構(gòu)造函數(shù)的原因是:如果你沒(méi)有提供其他構(gòu)造函數(shù),又沒(méi)有定義默認(rèn)構(gòu)造函數(shù),編譯器將為你自動(dòng)生成一個(gè),編譯器生成的構(gòu)造函數(shù)并不會(huì)對(duì)對(duì)象進(jìn)行初始化。

如果你定義的類繼承現(xiàn)有類,而你又沒(méi)有增加新的成員變量,則不需要為新類定義默認(rèn)構(gòu)造函數(shù)。

3.明確的構(gòu)造函數(shù)(ExplicitConstructors)

對(duì)單參數(shù)構(gòu)造函數(shù)使用C++關(guān)鍵字explicit。

定義:通常,只有一個(gè)參數(shù)的構(gòu)造函數(shù)可被用于轉(zhuǎn)換(conversion,譯者注:主要指隱式轉(zhuǎn)換,下文可見(jiàn)),例如,定義了Foo::Foo(stringname),當(dāng)向需要傳入一個(gè)Foo對(duì)象的函數(shù)傳入一個(gè)字符串時(shí),構(gòu)造函數(shù)Foo::Foo(stringname)被調(diào)用并將該字符串轉(zhuǎn)換為一個(gè)Foo臨時(shí)對(duì)象傳給調(diào)用函數(shù)。看上去很方便,但如果你并不希望如此通過(guò)轉(zhuǎn)換生成一個(gè)新對(duì)象的話,麻煩也隨之而來(lái)。為避免構(gòu)造函數(shù)被調(diào)用造成隱式轉(zhuǎn)換,可以將其聲明為explicit。

優(yōu)點(diǎn):避免不合時(shí)宜的變換。

缺點(diǎn):無(wú)。

結(jié)論:

所有單參數(shù)構(gòu)造函數(shù)必須是明確的。在類定義中,將關(guān)鍵字explicit加到單參數(shù)構(gòu)造函數(shù)前:explicitFoo(stringname);

例外:在少數(shù)情況下,拷貝構(gòu)造函數(shù)可以不聲明為explicit;特意作為其他類的透明包裝器的類。類似例外情況應(yīng)在注釋中明確說(shuō)明。

4.拷貝構(gòu)造函數(shù)(CopyConstructors)

僅在代碼中需要拷貝一個(gè)類對(duì)象的時(shí)候使用拷貝構(gòu)造函數(shù);不需要拷貝時(shí)應(yīng)使用DISALLOW_COPY_AND_ASSIGN。

定義:通過(guò)拷貝新建對(duì)象時(shí)可使用拷貝構(gòu)造函數(shù)(特別是對(duì)象的傳值時(shí))。

優(yōu)點(diǎn):拷貝構(gòu)造函數(shù)使得拷貝對(duì)象更加容易,STL容器要求所有內(nèi)容可拷貝、可賦值。

缺點(diǎn):C++中對(duì)象的隱式拷貝是導(dǎo)致很多性能問(wèn)題和bugs的根源??截悩?gòu)造函數(shù)降低了代碼可讀性,相比按引用傳遞,跟蹤按值傳遞的對(duì)象更加困難,對(duì)象修改的地方變得難以捉摸。

結(jié)論:

大量的類并不需要可拷貝,也不需要一個(gè)拷貝構(gòu)造函數(shù)或賦值操作(assignmentoperator)。不幸的是,如果你不主動(dòng)聲明它們,編譯器會(huì)為你自動(dòng)生成,而且是public的。

可以考慮在類的private中添加空的(dummy)拷貝構(gòu)造函數(shù)和賦值操作,只有聲明,沒(méi)有定義。由于這些空程序聲明為private,當(dāng)其他代碼試圖使用它們的時(shí)候,編譯器將報(bào)錯(cuò)。為了方便,可以使用宏DISALLOW_COPY_AND_ASSIGN:

//禁止使用拷貝構(gòu)造函數(shù)和賦值操作的宏
//應(yīng)在類的private:中使用
#defineDISALLOW_COPY_AND_ASSIGN(TypeName)
TypeName(constTypeName&);
voidoperator=(constTypeName&)

classFoo{
public:
Foo(intf);
~Foo();

private:
DISALLOW_COPY_AND_ASSIGN(Foo);
};

如上所述,絕大多數(shù)情況下都應(yīng)使用DISALLOW_COPY_AND_ASSIGN,如果類確實(shí)需要可拷貝,應(yīng)在該類的頭文件中說(shuō)明原由,并適當(dāng)定義拷貝構(gòu)造函數(shù)和賦值操作,注意在operator=中檢測(cè)自賦值(self-assignment)情況。

在將類作為STL容器值得時(shí)候,你可能有使類可拷貝的沖動(dòng)。類似情況下,真正該做的是使用指針指向STL容器中的對(duì)象,可以考慮使用std::tr1::shared_ptr。

5.結(jié)構(gòu)體和類(Structsvs.Classes)

僅當(dāng)只有數(shù)據(jù)時(shí)使用struct,其它一概使用class。

在C++中,關(guān)鍵字struct和class幾乎含義等同,我們?yōu)槠淙藶樘砑诱Z(yǔ)義,以便為定義的數(shù)據(jù)類型合理選擇使用哪個(gè)關(guān)鍵字。

struct被用在僅包含數(shù)據(jù)的消極對(duì)象(passiveobjects)上,可能包括有關(guān)聯(lián)的常量,但沒(méi)有存取數(shù)據(jù)成員之外的函數(shù)功能,而存取功能通過(guò)直接訪問(wèn)實(shí)現(xiàn)而無(wú)需方法調(diào)用,這兒提到的方法是指只用于處理數(shù)據(jù)成員的,如構(gòu)造函數(shù)、析構(gòu)函數(shù)、Initialize()、Reset()、Validate()。

如果需要更多的函數(shù)功能,class更適合,如果不確定的話,直接使用class。

如果與STL結(jié)合,對(duì)于仿函數(shù)(functors)和特性(traits)可以不用class而是使用struct。

注意:類和結(jié)構(gòu)體的成員變量使用不同的命名規(guī)則。

6.繼承(Inheritance)

使用組合(composition,譯者注,這一點(diǎn)也是GoF在《DesignPatterns》里反復(fù)強(qiáng)調(diào)的)通常比使用繼承更適宜,如果使用繼承的話,只使用公共繼承。

定義:當(dāng)子類繼承基類時(shí),子類包含了父基類所有數(shù)據(jù)及操作的定義。C++實(shí)踐中,繼承主要用于兩種場(chǎng)合:實(shí)現(xiàn)繼承(implementationinheritance),子類繼承父類的實(shí)現(xiàn)代碼;接口繼承(interfaceinheritance),子類僅繼承父類的方法名稱。

優(yōu)點(diǎn):實(shí)現(xiàn)繼承通過(guò)原封不動(dòng)的重用基類代碼減少了代碼量。由于繼承是編譯時(shí)聲明(compile-timedeclaration),編碼者和編譯器都可以理解相應(yīng)操作并發(fā)現(xiàn)錯(cuò)誤。接口繼承可用于程序上增強(qiáng)類的特定API的功能,在類沒(méi)有定義API的必要實(shí)現(xiàn)時(shí),編譯器同樣可以偵錯(cuò)。

缺點(diǎn):對(duì)于實(shí)現(xiàn)繼承,由于實(shí)現(xiàn)子類的代碼在父類和子類間延展,要理解其實(shí)現(xiàn)變得更加困難。子類不能重寫(xiě)父類的非虛函數(shù),當(dāng)然也就不能修改其實(shí)現(xiàn)?;愐部赡芏x了一些數(shù)據(jù)成員,還要區(qū)分基類的物理輪廓(physicallayout)。

結(jié)論:

所有繼承必須是public的,如果想私有繼承的話,應(yīng)該采取包含基類實(shí)例作為成員的方式作為替代。

不要過(guò)多使用實(shí)現(xiàn)繼承,組合通常更合適一些。努力做到只在“是一個(gè)”("is-a",譯者注,其他"has-a"情況下請(qǐng)使用組合)的情況下使用繼承:如果Bar的確“是一種”Foo,才令Bar是Foo的子類。

必要的話,令析構(gòu)函數(shù)為virtual,必要是指,如果該類具有虛函數(shù),其析構(gòu)函數(shù)應(yīng)該為虛函數(shù)。

譯者注:至于子類沒(méi)有額外數(shù)據(jù)成員,甚至父類也沒(méi)有任何數(shù)據(jù)成員的特殊情況下,析構(gòu)函數(shù)的調(diào)用是否必要是語(yǔ)義爭(zhēng)論,從編程設(shè)計(jì)規(guī)范的角度看,在含有虛函數(shù)的父類中,定義虛析構(gòu)函數(shù)絕對(duì)必要。

限定僅在子類訪問(wèn)的成員函數(shù)為protected,需要注意的是數(shù)據(jù)成員應(yīng)始終為私有。

當(dāng)重定義派生的虛函數(shù)時(shí),在派生類中明確聲明其為virtual。根本原因:如果遺漏virtual,閱讀者需要檢索類的所有祖先以確定該函數(shù)是否為虛函數(shù)(譯者注,雖然不影響其為虛函數(shù)的本質(zhì))。

7.多重繼承(MultipleInheritance)

真正需要用到多重實(shí)現(xiàn)繼承(multipleimplementationinheritance)的時(shí)候非常少,只有當(dāng)最多一個(gè)基類中含有實(shí)現(xiàn),其他基類都是以Interface為后綴的純接口類時(shí)才會(huì)使用多重繼承。

定義:多重繼承允許子類擁有多個(gè)基類,要將作為純接口的基類和具有實(shí)現(xiàn)的基類區(qū)別開(kāi)來(lái)。

優(yōu)點(diǎn):相比單繼承,多重實(shí)現(xiàn)繼承可令你重用更多代碼。

缺點(diǎn):真正需要用到多重實(shí)現(xiàn)繼承的時(shí)候非常少,多重實(shí)現(xiàn)繼承看上去是不錯(cuò)的解決方案,通??梢哉业礁用鞔_、清晰的、不同的解決方案。

結(jié)論:只有當(dāng)所有超類(superclass)除第一個(gè)外都是純接口時(shí)才能使用多重繼承。為確保它們是純接口,這些類必須以Interface為后綴。

注意:關(guān)于此規(guī)則,Windows下有種例外情況(譯者注,將在本譯文最后一篇的規(guī)則例外中闡述)。

8.接口(Interface)

接口是指滿足特定條件的類,這些類以Interface為后綴(非必需)。

定義:當(dāng)一個(gè)類滿足以下要求時(shí),稱之為純接口:

1)只有純虛函數(shù)("=0")和靜態(tài)函數(shù)(下文提到的析構(gòu)函數(shù)除外);

2)沒(méi)有非靜態(tài)數(shù)據(jù)成員;

3)沒(méi)有定義任何構(gòu)造函數(shù)。如果有,也不含參數(shù),并且為protected;

4)如果是子類,也只能繼承滿足上述條件并以Interface為后綴的類。

接口類不能被直接實(shí)例化,因?yàn)樗暶髁思兲摵瘮?shù)。為確保接口類的所有實(shí)現(xiàn)可被正確銷毀,必須為之聲明虛析構(gòu)函數(shù)(作為第1條規(guī)則的例外,析構(gòu)函數(shù)不能是純虛函數(shù))。具體細(xì)節(jié)可參考Stroustrup的《TheC++ProgrammingLanguage,3rdedition》第12.4節(jié)。

優(yōu)點(diǎn):以Interface為后綴可令他人知道不能為該接口類增加實(shí)現(xiàn)函數(shù)或非靜態(tài)數(shù)據(jù)成員,這一點(diǎn)對(duì)于多重繼承尤其重要。另外,對(duì)于Java程序員來(lái)說(shuō),接口的概念已經(jīng)深入人心。

缺點(diǎn):Interface后綴增加了類名長(zhǎng)度,為閱讀和理解帶來(lái)不便,同時(shí),接口特性作為實(shí)現(xiàn)細(xì)節(jié)不應(yīng)暴露給客戶。

結(jié)論:。只有在滿足上述需要時(shí),類才以Interface結(jié)尾,但反過(guò)來(lái),滿足上述需要的類未必一定以Interface結(jié)尾。

9.操作符重載(OperatorOverloading)

除少數(shù)特定環(huán)境外,不要重載操作符。

定義:一個(gè)類可以定義諸如+、/等操作符,使其可以像內(nèi)建類型一樣直接使用。

優(yōu)點(diǎn):使代碼看上去更加直觀,就像內(nèi)建類型(如int)那樣,重載操作符使那些Equals()、Add()等黯淡無(wú)光的函數(shù)名好玩多了。為了使一些模板函數(shù)正確工作,你可能需要定義操作符。

缺點(diǎn):雖然操作符重載令代碼更加直觀,但也有一些不足

1)混淆直覺(jué),讓你誤以為一些耗時(shí)的操作像內(nèi)建操作那樣輕巧;

2)查找重載操作符的調(diào)用處更加困難,查找Equals()顯然比同等調(diào)用==容易的多;

3)有的操作符可以對(duì)指針進(jìn)行操作,容易導(dǎo)致bugs,F(xiàn)oo+4做的是一件事,而&Foo+4可能做的是完全不同的另一件事,對(duì)于二者,編譯器都不會(huì)報(bào)錯(cuò),使其很難調(diào)試;

4)重載還有令你吃驚的副作用,比如,重載操作符&的類不能被前置聲明。

結(jié)論:

一般不要重載操作符,尤其是賦值操作(operator=)比較陰險(xiǎn),應(yīng)避免重載。如果需要的話,可以定義類似Equals()、CopyFrom()等函數(shù)。

然而,極少數(shù)情況下需要重載操作符以便與模板或“標(biāo)準(zhǔn)”C++類銜接(如operator<<(ostream&,constT&)),如果被證明是正當(dāng)?shù)纳锌山邮?,但你要盡可能避免這樣做。尤其是不要僅僅為了在STL容器中作為key使用就重載operator==或operator<,取而代之,你應(yīng)該在聲明容器的時(shí)候,創(chuàng)建相等判斷和大小比較的仿函數(shù)類型。

有些STL算法確實(shí)需要重載operator==時(shí)可以這么做,不要忘了提供文檔說(shuō)明原因。

參考拷貝構(gòu)造函數(shù)和函數(shù)重載。

10.存取控制(AccessControl)

將數(shù)據(jù)成員私有化,并提供相關(guān)存取函數(shù),如定義變量foo_及取值函數(shù)foo()、賦值函數(shù)set_foo()。

存取函數(shù)的定義一般內(nèi)聯(lián)在頭文件中。

參考繼承和函數(shù)命名。

11.聲明次序(DeclarationOrder)

在類中使用特定的聲明次序:public:在private:之前,成員函數(shù)在數(shù)據(jù)成員(變量)前。

定義次序如下:public:、protected:、private:,如果那一塊沒(méi)有,直接忽略即可。

每一塊中,聲明次序一般如下:

1)typedefs和enums;

2)常量;

3)構(gòu)造函數(shù);

4)析構(gòu)函數(shù);

5)成員函數(shù),含靜態(tài)成員函數(shù);

6)數(shù)據(jù)成員,含靜態(tài)數(shù)據(jù)成員。

宏DISALLOW_COPY_AND_ASSIGN置于private:塊之后,作為類的最后部分。參考拷貝構(gòu)造函數(shù)。

.cc文件中函數(shù)的定義應(yīng)盡可能和聲明次序一致。

不要將大型函數(shù)內(nèi)聯(lián)到類的定義中,通常,只有那些沒(méi)有特別意義的或者性能要求高的,并且是比較短小的函數(shù)才被定義為內(nèi)聯(lián)函數(shù)。更多細(xì)節(jié)參考譯文第一篇的內(nèi)聯(lián)函數(shù)。

12.編寫(xiě)短小函數(shù)(WriteShortFunctions)

傾向于選擇短小、凝練的函數(shù)。

長(zhǎng)函數(shù)有時(shí)是恰當(dāng)?shù)?,因此?duì)于函數(shù)長(zhǎng)度并沒(méi)有嚴(yán)格限制。如果函數(shù)超過(guò)40行,可以考慮在不影響程序結(jié)構(gòu)的情況下將其分割一下。

即使一個(gè)長(zhǎng)函數(shù)現(xiàn)在工作的非常好,一旦有人對(duì)其修改,有可能出現(xiàn)新的問(wèn)題,甚至導(dǎo)致難以發(fā)現(xiàn)的bugs。使函數(shù)盡量短小、簡(jiǎn)單,便于他人閱讀和修改代碼。

在處理代碼時(shí),你可能會(huì)發(fā)現(xiàn)復(fù)雜的長(zhǎng)函數(shù),不要害怕修改現(xiàn)有代碼:如果證實(shí)這些代碼使用、調(diào)試?yán)щy,或者你需要使用其中的一小塊,考慮將其分割為更加短小、易于管理的若干函數(shù)。

______________________________________

譯者:關(guān)于類的注意事項(xiàng),總結(jié)一下:

1.不在構(gòu)造函數(shù)中做太多邏輯相關(guān)的初始化;

2.編譯器提供的默認(rèn)構(gòu)造函數(shù)不會(huì)對(duì)變量進(jìn)行初始化,如果定義了其他構(gòu)造函數(shù),編譯器不再提供,需要編碼者自行提供默認(rèn)構(gòu)造函數(shù);

3.為避免隱式轉(zhuǎn)換,需將單參數(shù)構(gòu)造函數(shù)聲明為explicit;

4.為避免拷貝構(gòu)造函數(shù)、賦值操作的濫用和編譯器自動(dòng)生成,可目前聲明其為private且無(wú)需實(shí)現(xiàn);

5.僅在作為數(shù)據(jù)集合時(shí)使用struct;

6.組合>實(shí)現(xiàn)繼承>接口繼承>私有繼承,子類重載的虛函數(shù)也要聲明virtual關(guān)鍵字,雖然編譯器允許不這樣做;

7.避免使用多重繼承,使用時(shí),除一個(gè)基類含有實(shí)現(xiàn)外,其他基類均為純接口;

8.接口類類名以Interface為后綴,除提供帶實(shí)現(xiàn)的虛析構(gòu)函數(shù)、靜態(tài)成員函數(shù)外,其他均為純虛函數(shù),不定義非靜態(tài)數(shù)據(jù)成員,不提供構(gòu)造函數(shù),提供的話,聲明為protected;

9.為降低復(fù)雜性,盡量不重載操作符,模板、標(biāo)準(zhǔn)類中使用時(shí)提供文檔說(shuō)明;

10.存取函數(shù)一般內(nèi)聯(lián)在頭文件中;

11.聲明次序:public->protected->private;

12.函數(shù)體盡量短小、緊湊,功能單一。

·Google特有的風(fēng)情

Google有很多自己實(shí)現(xiàn)的使C++代碼更加健壯的技巧、功能,以及有異于別處的C++的使用方式。

1.智能指針(SmartPointers)

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

“智能”指針看上去是指針,其實(shí)是附加了語(yǔ)義的對(duì)象。以scoped_ptr為例,scoped_ptr被銷毀時(shí),刪除了它所指向的對(duì)象。shared_ptr也是如此,而且,shared_ptr實(shí)現(xiàn)了引用計(jì)數(shù)(reference-counting),從而只有當(dāng)它所指向的最后一個(gè)對(duì)象被銷毀時(shí),指針才會(huì)被刪除。

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

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

譯者注:看來(lái),Google所謂的不同之處,在于盡量避免使用智能指針:D,使用時(shí)也盡量局部化,并且,安全第一。

·其他C++特性

1.引用參數(shù)(ReferenceArguments)

所以按引用傳遞的參數(shù)必須加上const。

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

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

缺點(diǎn):容易引起誤解,因?yàn)橐迷谡Z(yǔ)法上是值卻擁有指針的語(yǔ)義。

結(jié)論:

函數(shù)形參表中,所有引用必須是const:

voidFoo(conststring&in,string*out);

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

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

2.函數(shù)重載(FunctionOverloading)

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

定義:可以定義一個(gè)函數(shù)參數(shù)類型為conststring&,并定義其重載函數(shù)類型為constchar*。

classMyClass{
public:
voidAnalyze(conststring&text);
voidAnalyze(constchar*text,size_ttextlen);
};

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

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

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

3.缺省參數(shù)(DefaultArguments)

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

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

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

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

4.變長(zhǎng)數(shù)組和alloca(Variable-LengthArraysandalloca())

禁止使用變長(zhǎng)數(shù)組和alloca()。

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

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

結(jié)論:

使用安全的分配器(allocator),如scoped_ptr/scoped_array。

5.友元(Friends)

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

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

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

6.異常(Exceptions)

不要使用C++異常。

優(yōu)點(diǎn):

1)異常允許上層應(yīng)用決定如何處理在底層嵌套函數(shù)中發(fā)生的“不可能發(fā)生”的失敗,不像出錯(cuò)代碼的記錄那么模糊費(fèi)解;

2)應(yīng)用于其他很多現(xiàn)代語(yǔ)言中,引入異常使得C++與Python、Java及其他與C++相近的語(yǔ)言更加兼容;

3)許多C++第三方庫(kù)使用異常,關(guān)閉異常將導(dǎo)致難以與之結(jié)合;

4)異常是解決構(gòu)造函數(shù)失敗的唯一方案,雖然可以通過(guò)工廠函數(shù)(factoryfunction)或Init()方法模擬異常,但他們分別需要堆分配或新的“非法”狀態(tài);

5)在測(cè)試框架(testingframework)中,異常確實(shí)很好用。

缺點(diǎn):

1)在現(xiàn)有函數(shù)中添加throw語(yǔ)句時(shí),必須檢查所有調(diào)用處,即使它們至少具有基本的異常安全保護(hù),或者程序正常結(jié)束,永遠(yuǎn)不可能捕獲該異常。例如:iff()callsg()callsh(),h拋出被f捕獲的異常,g就要當(dāng)心了,避免沒(méi)有完全清理;

2)通俗一點(diǎn)說(shuō),異常會(huì)導(dǎo)致程序控制流(controlflow)通過(guò)查看代碼無(wú)法確定:函數(shù)有可能在不確定的地方返回,從而導(dǎo)致代碼管理和調(diào)試?yán)щy,當(dāng)然,你可以通過(guò)規(guī)定何時(shí)何地如何使用異常來(lái)最小化的降低開(kāi)銷,卻給開(kāi)發(fā)人員帶來(lái)掌握這些規(guī)定的負(fù)擔(dān);

3)異常安全需要RAII和不同編碼實(shí)踐。輕松、正確編寫(xiě)異常安全代碼需要大量支撐。允許使用異常;

4)加入異常使二進(jìn)制執(zhí)行代碼體積變大,增加了編譯時(shí)長(zhǎng)(或許影響不大),還可能增加地址空間壓力;

5)異常的實(shí)用性可能會(huì)刺激開(kāi)發(fā)人員在不恰當(dāng)?shù)臅r(shí)候拋出異常,或者在不安全的地方從異常中恢復(fù),例如,非法用戶輸入可能導(dǎo)致拋出異常。如果允許使用異常會(huì)使得這樣一篇編程風(fēng)格指南長(zhǎng)出很多(譯者注,這個(gè)理由有點(diǎn)牽強(qiáng):-()!

結(jié)論:

<p style="background:rgb

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉