google開源 C / C ++項目代碼規範
1.頭文件
每一般一個 .cc 文件都有一個對應的 .h 文件。也有一些常見例外,如單元測試代碼和只包含 main() 函數的 .cc 文件。git
正確使用頭文件可令代碼在可讀性,文件大小和性能上大爲改觀。程序員
下面的規則將引導你規避使用頭文件時的各類陷阱。web
1.1。自包含的頭文件
TIP編程
頭文件應該可以自給自足(自包含的,也就是能夠做爲第一個頭文件被引入),以 .h 結尾。至於用來插入文本的文件,說到底它們並非頭文件,因此以應 .inc 結尾不容許。出分離 -inl.h 頭文件的作法。數組
全部頭文件要可以自給自足。換言之,用戶和重構工具不須要爲特別場合而包含額外的頭文件。詳言之,一個頭文件要有 1.2。#define保護,統計包含它所須要的其它頭文件,也不要求定義任何特別的符號。緩存
不過有一個例外,即一個文件並非自足的,而是做爲文本插入到代碼某處。或者,文件內容其實是其它頭文件的特定平臺(特定於平臺)擴展部分,這些文件就要用 .inc 文件擴展名。多線程
若是 .h 文件聲明瞭一個模板或內聯函數,同時也在該文件加以定義。有用凡是到這些的 .cc 文件,就得通通包含該頭文件,不然程序可能會在構建中連接失敗。不要把這些定義放到分離的 -inl.h 文件裏(譯者注:過去該規範曾提倡把定義放到-inl.h裏過)。app
有個例外:若是某函數模板爲全部相關模板參數顯式實例化,或自己就是某類的一個私有成員,它就那麼定義只能在實例化該模板的 .cc 文件裏。編輯器
1.2。#define保護
TIPide
全部頭文件都應該使用 #define 來防止頭文件被多重包含,命名格式當是: <PROJECT>_<PATH>_<FILE>_H_ 。
爲保證惟一性,頭文件的命名應該基於全部項目源代碼樹的全路徑。例如,項目 foo 中的頭文件 foo/src/bar/baz.h 可按以下方式保護:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
1.3。前置聲明
TIP
儘量地避免使用前置聲明。使用 #include 全部遊戲須要的頭文件便可。
定義:
所謂「前置聲明」(forward declaration)是類,函數和模板的純粹聲明,沒伴隨着其定義。
優勢:
前置聲明可以節省編譯時間,的多餘 #include 會迫使compile-器展開更多的文件,處理更多的輸入。
前置聲明可以節省沒必要要的從新編譯的時間。 #include 使代碼由於頭文件中無關的改動而被從新編譯屢次。
缺點:
前置聲明隱藏了依賴關係,頭文件改動時,用戶的代碼會跳過必要的從新編譯過程。
前置聲明可能會被庫的後續更改所破壞。前置聲明函數或模板有時會妨礙頭文件開發者變更其API。例如擴大形參類型,加個自帶默認參數的模板形參等等。
前置聲明來自命名空間 std:: 的符號時,其行爲未定義。
很難判斷何時該用前置聲明,時候什麼用該 #include 極端狀況下,用前置聲明代替。 includes 甚至都會暗暗地改變代碼的含義:
// bh:
struct B {};
struct D : B {}
// good_user.cc:
#包括 「BH」
void f (B * );
void f (void * );
void test (D * x ) { f (x ); } //調用f(B *)
若是 #include 被 B 狀語從句: D 的前置聲明替代, test() 就會調用 f(void*) 。
前置聲明瞭include 很多來自頭文件的符號時,就會比單單一行的 冗長。
僅僅爲了能前置聲明而重構代碼(好比用指針成員代替對象成員)會使代碼變得更慢更復雜。
結論:
儘可能避免前置聲明那些定義在其餘項目中的實體。
函數:老是使用 #include。
類模板:優先使用 #include。
至於何時包含頭文件,參見 1.5。#include的路徑及順序 。
1.4。內聯函數
TIP
只有當函數只有10行甚至更少時纔將其定義爲內聯函數。
定義:
當函數被聲明爲內聯函數以後,編譯器會將其內聯展開,而不是按一般的函數調用機制進行調用。
優勢:
只要內聯的函數體小小,內聯該函數能夠令目標代碼更加高效。對於存取函數以及其它函數體比較短,性能關鍵的函數,鼓勵使用內聯。
缺點:
濫用內聯將致使程序變得更慢。內聯可能使目標代碼量或增或減,這取決於內聯函數的大小。內聯很是短小的存取函數一般會減小代碼大小,但內聯一個至關大的函數將戲劇性的增長代碼大小。現代處理器因爲更好的利用了指令緩存,小巧的代碼每每執行更快。
結論:
一個較爲合理的經驗準則是,不要內聯超過10行的函數。謹謹對待析構函數,析構函數每每比其表面看起來要更長,由於有隱含的成員和基類析構函數被調用!
另外一個實用的經驗準則:內聯那些包含循環或 switch 語句的函數經常是得不償失(除非在大多數狀況下,這些循環或 switch 語句從不被執行)。
有些函數即便聲明爲內聯的也不必定會被編譯器內聯,這點很重要; 好比虛函數和遞歸函數就不會被正常內聯。一般,遞歸函數不該該聲明成內聯函數。(YuleFox注:遞歸調用堆棧的展開並不像循環那麼簡單,好比遞進層數在編譯時多是未知的,大多數編譯器都不支持內聯遞歸函數)。虛函數內聯的主要緣由是想把它的函數體放在類定義內,爲了圖個方便,抑或是看成文件描述其行爲,好比精短的存取函數。
1.5。 #include 的路徑及順序
TIP
使用標準的頭文件包含順序可加強可讀性,避免隱藏依賴:相關頭文件,C庫,C ++庫,其餘庫的 .h,本項目內的 .h。
項目內部文件應按照項目源代碼目錄樹結構排列,避免使用UNIX特殊的快捷目錄 .(當前目錄)或 .. (上級目錄)。例如, google-awesome-project/src/base/logging.h 應該按以下方式包含:
#include 「base / logging.h」
又如, dir/foo.cc 或 dir/foo_test.cc 的主要做用英文的英文實現或測試 dir2/foo2.h 的功能, foo.cc 中包含頭文件的次序以下:
dir2/foo2.h (優先位置,詳情以下)
C系統文件
C ++系統文件
庫其餘的 .h 文件
項目本。內 .h 文件
優先這種順序的排序保證當 dir2/foo2.h 遺漏某些必要的庫時, dir/foo.cc 或 dir/foo_test.cc 的構建會馬上停止。所以這一條規則保證維護這些文件的人們首先看到構建停止的消息而不是維護其餘包的人們。
dir/foo.cc 和 dir2/foo2.h 一般位於同一目錄下(如 base/basictypes_unittest.cc 和 base/basictypes.h),但也可放在不一樣目錄下。
按字母順序分別對每種類型的頭文件進行二次排序是不錯的主意。注意較老的代碼可不符合這條規則,要在方便的時候改正它們。
您所依賴的符號(符號)被哪些頭文件所定義,您就應該包含(包括)哪些頭文件,前置聲明 (向前聲明)狀況除外。您好比要用到 bar.h 中的某個符號,哪怕您所包含的 foo.h 已經包含了 bar.h,也照樣得包含 bar.h,除非foo.h 有明確 說明它會自動向您提供 bar.h 中符號。不過,凡是cc文件所對應的「相關頭文件」已經包含的,就不用再重複包含進其cc文件裏面了,就像 foo.cc 只包含 foo.h就夠了,不用再管後者所包含的其它內容。
舉#include 「foo / public / fooserver.h」//優先位置
#include 「foo / public / bar.h」例如, google-awesome-project/src/foo/internal/fooserver.cc 包含次序以下:
4.函數
4.1。參數順序
總述
函數的參數順序爲:輸入參數在先,後跟輸出參數。
說明
C / C ++中的函數參數或者是函數的輸入,或者是函數的輸出,或兼而有之。輸入參數一般是值參或 const 引用,輸出參數或輸入/輸出參數則通常爲非 const 指針。在排列參數順序時,將全部的輸入參數置於輸出參數以前。特別要注意,在加入新參數時不要由於它們是新參數就置於參數列表最後,而是仍然要按照前述的規則,即將新的輸入參數也置於輸出參數以前。
這並不是一個硬性規定。輸入/輸出參數(一般是類或結構體)讓這個問題變得複雜。而且,有時候爲了其餘函數保持一致,你可能不得不不全部變通。
4.2。編寫簡短函數
總述
咱們傾向於編寫簡短,凝練的函數。
說明
咱們認可長函數有時是合理的,所以並不硬限制函數的長度。若是函數超過40行,能夠思索一下能不能在不影響程序結構的前提下對其進行分割。
即便一個長函數如今工做的很是好,一旦有人對其修改,有可能出現新的問題,甚至致使難以發現的錯誤。使函數儘可能簡短,以便於他在他人閱讀和修改代碼。
在處理代碼時,你可能會發現複雜的長函數。不要懼怕修改現有代碼:若是證明這些代碼使用/調試起來很困難,或者你只需要使用其中的一小段代碼,考慮將其分割爲更加簡短並易於管理的若干函數。
4.3。引用參數
總述
全部按引用傳遞的參數必須加上 const。
定義
在C語言中,若是函數須要修改變量的值,參數必須爲指針,如 。在C ++中,函數還能夠聲明爲引用參數: 。int foo(int *pval)int foo(int &val)
優勢
引用定義參數能夠防止出現 (*pval)++ 這樣醜陋的代碼。引用參數對於拷貝構造函數這樣的應用也是必需的。同時也更明確地不接受空指針。
缺點
容易引發誤解,由於引用在語法上是值變量卻擁有指針的語義。
結論
函數參數列表中,全部引用參數都必須是 const:
void Foo (const string &in , string * out );
事實上這在Google Code是一個硬性約定:輸入參數是值參或 const 引用,輸出參數爲指針。輸入參數能夠是 const 指針,但決不能是非 const 引用參數,除非特殊要求,好比 swap()。
有時候,在輸入形參中用針指 比 更明智。好比:const T*const T&
可能會傳遞空指針。
函數要把指針或對地址的引用賦值給輸入形參。
總而言之,大多時候輸入形參每每是 。用若 則說明輸入側另有處理。因此若要使用 ,則應給出相應的理由,不然會使讀者感到迷惑。const T&const T*const T*
4.4。函數重載
總述
若要使用函數重載,則必須能讓讀者一看調用點就成竹在胸,而不用花心思猜想調用的重載函數究竟是哪種。這一規則也適用於構造函數。
定義
你能夠編寫一個參數類型爲 的函數,而後用另外一個參數類型爲 的函數對其進行重載:const string&const char*
class MyClass {
public :
void Analyze (const string &text );
void 分析(const char * text , size_t textlen );
};
優勢
經過重載參數不一樣的同名函數,能夠令代碼更直觀。模板化代碼須要重載,這同時也能爲使用者帶來便利。
缺點
若是函數單靠不一樣的參數類型而重載(acgtyrant注:這意味着參數數量不變),讀者就得十分熟悉C ++五花八門的匹配規則,以瞭解匹配過程具體到底如何。另外,若是派生類只重載了某個函數的部分變體,繼承語義就容易使人困惑。
結論
若是打算重載一個函數,能夠試試改在函數名里加參數信息。例如,用 AppendString()和 AppendInt() 等,而不是一口氣重載多個 Append()。若是重載函數的目的是爲了支持不一樣數量的同一類型參數,則優先考慮使用 std::vector 以便使用者能夠用 列表初始化指定參數。
4.5。缺省參數
總述
只容許在非虛函數中使用缺省參數,且必須保證缺省參數的值始終一致。參數缺省與 函數重載 遵循一樣的規則。通常狀況下建議使用函數重載,尤爲是在缺省函數帶來的可讀性提高不能彌補下文中所提到的缺點的狀況下。
優勢
有些函數通常狀況下使用默認參數,但有時須要又使用非默認的參數。缺省參數爲這樣的情形提供了便利,使程序員不須要爲了極少的例外狀況編寫大量的函數。和函數重載相比,缺省參數的語法更簡潔明瞭,減小了大量的樣板代碼,也更好地區別了「必要參數」和「可選參數」。
缺點
缺省參數其實是函數重載語義的另外一種實現方式,所以全部 不該當使用函數重載的理由 也都適用於缺省參數。
虛函數調用的缺省參數取決於目標對象的靜態類型,此時沒法保證給定函數的全部重載聲明的都是一樣的缺省參數。
缺省參數是在每一個調用點都要進行從新求值的,這會形成生成的代碼迅速膨脹。做爲讀者,通常來講也更但願缺省的參數在聲明時就已經被固定了,而不是在每次調用時均可能會有不一樣的取值。
缺省參數會干擾函數指針,致使函數簽名與調用點的簽名不一致。而函數重載不會致使這樣的問題。
結論
對於虛函數,不容許使用缺省參數,由於在虛函數中缺省參數不必定能正常工做。若是在每一個調用點缺省參數的值都有可能不一樣,在這種狀況下缺省函數也不容許使用。(例如,不要寫像 這樣的代碼。)void f(int n = counter++);
在其餘狀況下,若是缺省參數對可讀性的提高遠遠超過了以上說起的缺點的話,能夠使用缺省參數。若是仍有疑惑,就使用函數重載。
4.6。函數返回類型後置語法
總述
只有在常規寫法(返回類型前置)不便於書寫或不便於閱讀時使用返回類型後置語法。
定義
C ++如今容許兩種不一樣的函數聲明方式。以往的寫法是將返回類型置於函數名以前。例如:
int foo (int x );
C ++ 11引入了這一新的形式。如今能夠在函數名前使用 auto 關鍵字,在參數列表以後後置返回類型。例如:
auto foo (int x ) - > int ;
後置返回類型爲函數做用域。對於像 int 這樣簡單的類型,兩種寫法沒有區別。但對於複雜的狀況,例如類域中的類型聲明或者以函數參數的形式書寫的類型,寫法的不一樣會形成區別。
優勢
後置返回類型是顯式地指定 Lambda表達式 的返回值的惟一方式。某些狀況下,編譯器能夠自動推導出Lambda表達式的返回類型,但並非在全部的狀況下都能實現。即便編譯器可以自動推導,顯式地指定返回類型也能讓讀者更明瞭。
有時在已經出現了的函數參數列表以後指定返回類型,可以讓書寫更簡單,也更易讀,尤爲是在返回類型依賴於模板參數時。例如:
template < class T , class U > auto add (T t , U u ) - > decltype (t + u );
對比下面的例子:
template < class T , class U > decltype (declval < T &> () + declval < U &gt ;) add (T t , U u );
缺點
後置返回類型相對來講是很是新的語法,並且在C和Java中都沒有類似的寫法,所以可能對讀者來講比較陌生。
在已有的代碼中有大量的函數聲明,你不可能把它們都用新的語法重寫一遍。所以實際的作法只能是使用舊的語法或者新舊混用。在這種狀況下,只使用一種版本是相對來講更規整的形式。
結論
在大部分狀況下,應當繼續使用以往的函數聲明寫法,即將返回類型置於函數名前。只有在必要的時候(如Lambda表達式)或者使用後置語法可以簡化書寫而且提升易讀性的時候才使用新的返回類型後置語法。可是後一種狀況通常來講是不多見的,大部分時候都出如今至關複雜的模板代碼中,而多數狀況下不鼓勵寫這樣 複雜的模板代碼。
7.命名約定
最重要的一致性規則是命名管理。命名的風格能讓咱們在不須要去查找類型聲明的條件下快速地瞭解某個名字表明的含義:類型,變量,函數,常量,宏,等等,甚至。咱們大腦中的模式匹配引擎很是依賴這些命名規則。
命名規則具備必定隨意性,但相比按我的喜愛命名,一致性更重要,因此不管你認爲它們是否重要,規則總歸是規則。
7.1。通用命名規則
總述
函數命名,變量命名,文件命名要有描述性; 少用縮寫。
說明
儘量使用描述性的命名,別心疼空間,畢竟相比之下讓代碼易於新讀者理解更重要。不要用只有項目開發者能理解的縮寫,也不要經過砍掉幾個字母來縮寫單詞。
int price_count_reader ; //無縮寫
int num_errors ; //「num」是一個常見的寫法
int num_dns_connections ; //人人都知道「DNS」是什麼
int n ; //毫無心義。
int nerr ; //含糊不清的縮寫。
int n_comp_conns ; //含糊不清的縮寫。
int wgc_connections ; //只有貴團隊知道是什麼意思
int pc_reader ; //「pc」有太多可能的解釋了。
int cstmr_id ; //刪減了若干字母。
注意,一些特定的廣爲人知的縮寫是容許的,例如用 i 表示迭代變量和用 T 表示模板參數。
模板參數的命名應當遵循對應的分類:類型模板參數應當遵循 類型命名 的規則,而非類型模板應當 遵循變量命名 的規則。
7.2。文件命名
總述
文件名要所有小寫,能夠包含下劃線(_)或連-字符(),依照項目的約定。若是沒有約定,那麼「 _」 更好。
說明
可接受的文件命名示例:
my_useful_class.cc
my-useful-class.cc
myusefulclass.cc
myusefulclass_test.cc // _unittest 狀語從句: _regtest 已棄用。
C ++文件要以 .cc 結尾,頭文件以 .h 結尾。專門插入文本的文件則以 .inc 結尾,參見 頭文件自足。
不要使用已經存在於 /usr/include 下的文件名(Yang.Y注:即編譯器搜索系統頭文件的路徑),如 db.h。
一般應儘可能讓文件名更加明確。 http_server_logs.h 就比 logs.h 要好。定義類時文件名通常成對出現,如 foo_bar.h 和 foo_bar.cc,對應於類 FooBar。
聯內必須函數放在 .h 文件中。若是內聯函數比較短,就直接放在 .h 中。
7.3。類型命名
總述
類型名稱的每一個單詞首字母均大寫,不包含下劃線: MyExcitingClass, MyExcitingEnum。
說明
全部類型命名 - 類,結構體,類型定義(typedef),枚舉,類型模板參數 - 均使用相同約定,即以大寫字母開始,每一個單詞首字母均大寫,不包含下劃線。例如:
//類和結構體
類 UrlTable { ...
class UrlTableTester { ...
struct UrlTableProperties { ...
//類型定義
typedef hash_map < UrlTableProperties * , string > PropertiesMap ;
//使用別名
使用 PropertiesMap = hash_map < UrlTableProperties * , string > ;
//枚舉
enum UrlTableErrors { ...
7.4。變量命名
總述
變量(包括函數參數)和數據成員名一概小寫,單詞之間用下劃線鏈接。類的成員變量如下劃線結尾,但結構體的就不用,如: a_local_variable, a_struct_data_member, a_class_data_member_。
說明
普通變量命名
舉例:
字符串 table_name ; //好 - 用下劃線。
字符串 表名; //好 - 全小寫。
字符串 tableName ; //差 - 混合大小寫
類數據成員
無論是靜態的仍是非靜態的,類數據成員均可以和普通變量同樣,但要接下劃線。
類 TableInfo {
...
private :
string table_name_ ; //好 - 後加下劃線。
字符串 tablename_ ; //好。
靜態 池< TableInfo > * pool_ ; //好。
};
結構體變量
無論是靜態的仍是非靜態的,結構體數據成員均可以和普通變量同樣,不用像類那樣接下劃線:
struct UrlTableProperties {
string name ;
int num_entries ;
靜態 池< UrlTableProperties > * 池;
};
結構體與類的使用討論,參考 結構體與類。
7.5。常量命名
總述
聲明爲 constexpr 或 const 的變量,或在程序運行期間其值始始保持不變的,命名時以「k」開頭,大小寫混合。例如:
const int kDaysInAWeek = 7 ;
說明
全部具備靜態存儲類型的變量(例如靜態變量或全局變量,參見 存儲類型)都應當以此方式命名。對於其餘存儲類型的變量,如自動變量等,這條規則是可選的。若是不採用這條規則,就按照通常的變量命名規則。
7.7。函數命名
總述
常規函數使用大小寫混合,取值和設值函數則要求與變量名匹配: MyExcitingFunction(), MyExcitingMethod(), my_exciting_member_variable(), set_my_exciting_member_variable()。
說明
通常來講,函數名的每一個單詞首字母大寫(即「駝峯變量名」或「帕斯卡變量名」),沒有下劃線。對於首字母縮寫的單詞,更傾向於將它們視做一個單詞進行首字母大寫(例如,寫做 StartRpc() 而非 StartRPC())。
AddTableEntry ()
DeleteUrl ()
OpenFileOrDie ()
(一樣的命名規則同時適用於類做用域和命名空間做用域的常量,由於它們是做爲API的一部分暴露對外的,所以應當讓它們看起來像是一個函數,由於在這時,它們其實是一個對象而非函數的這一事實對外不過是一個可有可無的實現細節。)
取值和設值函數的命名與變量一致。通常來講它們的名稱與實際的成員變量對應,但並不強制要求。例如 與 。int count()void set_count(int count)
7.7。命名空間命名
總述
命名空間以小寫字母命名。最高級命名空間的名字取決於項目名稱。要注意避免嵌套命名空間的名字之間和常見的頂級命名空間的名字之間發生衝突。
頂級命名空間的名稱應當是項目名或者是該命名空間中的代碼所屬的團隊的名字。命名空間中的代碼,應當存放於和命名空間的名字匹配的文件夾或其子文件夾中。
注意 不使用縮寫做爲名稱 的規則一樣適用於命名空間。命名空間中的代碼極少須要涉及命名空間的名稱,所以沒有必要在命名空間中使用縮寫。
要避免嵌套的命名空間與常見的頂級命名空間發生名稱衝突。因爲名稱查找規則的存在,命名空間之間的衝突徹底有可能致使編譯失敗。尤爲是,不要建立嵌套的 std 命名空間。建議使用更獨特的項目標識符(websearch::index, websearch::index_util)而很是見的極易發生衝突的名稱(好比 websearch::util)。
對於 internal 命名空間,要小心加入到同一 internal 命名空間的代碼之間發生衝突(因爲內部維護人員一般來自同一團隊,所以常有可能致使衝突)。在這種狀況下,請使用文件名以使內部名稱獨一無二(例如對於 frobber.h,使用 websearch::index::frobber_internal)。
7.8。枚舉命名
總述
的枚舉命名應當狀語從句: 常量 或 宏 harmony和諧: kEnumName 或是 ENUM_NAME。
說明
的單獨枚舉值應該優先採用 常量 的命名方式。但 宏 方式的命名也。能夠接受。枚舉名 UrlTableErrors (以及 AlternateUrlTableErrors)是類型,因此要用大小寫混合的方式。
enum UrlTableErrors {
kOK = 0 ,
kErrorOutOfMemory ,
kErrorMalformedInput ,
};
枚舉 AlternateUrlTableErrors {
OK = 0 ,
OUT_OF_MEMORY = 1 ,
MALFORMED_INPUT = 2 ,
};
2009年1月以前,咱們一直建議採用 宏 的方式命名枚舉值。因爲枚舉值和宏之間的命名衝突,直接致使了不少問題。由此,這裏改成優先選擇常量風格的命名方式。新代碼應該儘量優先使用常量風格。可是老代碼不必切換到常量風格,除非宏風格確實會產生編譯期問題。
7.9。宏命名
總述
你並不打算 使用宏,對吧?若是你必定要用,像這樣命名: MY_MACRO_THAT_SCARES_SMALL_CHILDREN。
說明
參考 預處理宏 ; 一般 不該該 使用宏。若是不得不使用,其命名像枚舉命名同樣所有大寫,使用下劃線:
#define ROUND(x)...
#define PI_ROUNDED 3.0
7.10。命名規則的特例
總述
若是你命名的實體與已有C / C ++實體類似,可參考現有命名策略。
bigopen():函數名,參照 open() 的形式
uint: typedef
bigpos: struct 或 class,參照 pos 的形式
sparse_hash_map:STL型實體; 參照STL命名約定
LONGLONG_MAX:常量,如同 INT_MAX
8.注意
註釋雖然寫起來很痛苦,但對保證代碼可讀性相當重要。下面的規則描述瞭如何註釋以及在哪兒註釋。固然也要記住:註釋當然很重要,但最好的代碼應當自己就是文檔。有意義的類型名和變量名,要遠賽過要用註釋解釋的含糊不清的名字。
你寫的註釋是給代碼讀者看的,也就是下一個須要理解你的代碼的人。因此慷慨些吧,下一個讀者可能就是你!
8.1。註釋風格
總述
使用 // 或 ,統一就好。/* */
說明
// 或 均可以; 但 更 經常使用。要在如何註釋及註釋風格上確保統一。/* *///
8.2。文件註釋
總述
在每個文件開頭加入版權公告。
文件註釋描述了該文件的內容。若是一個文件只聲明,或實現或測試了一個對象,而且這個對象已經在它的聲明處進行了詳細的註釋,那麼就沒有必要再加上文件註釋。除此以外的其餘文件都須要文件註釋。
說明
法律公告和做者信息
每一個文件都應該包含許可證引用。爲項目選擇合適的許可證版本(好比,Apache 2.0,BSD,LGPL,GPL)
若是你對原始做者的文件作了重大修改,請考慮刪除原做者信息。
文件內容
若是一個 .h 文件聲明瞭多個概念,則文件註釋應當對文件的內容作一個大體的說明,同時說明各個概念之間的聯繫。一個一到兩行的文件註釋就足夠了,對於每一個概念的詳細文檔應當放在各個概念中,而不是文件註釋中。
不要在 .h 和 .cc 之間複製註釋,這樣的註釋偏離了註釋的實際意義。
8.3。類註釋
總述
每一個類的定義都要附帶一份註釋,描述類的功能和用法,除非它的功能至關明顯。
//遍歷GargantuanTable的內容。
//示例:
// GargantuanTableIterator * iter = table-> NewIterator();
// it for(iter-> Seek(「foo」);!iter-> done(); iter-> Next()){
// process(iter-> key(),iter-> value());
//}
//刪除它;
類 GargantuanTableIterator {
...
};
說明
類註釋應當爲讀者理解如何使用與什麼時候使用類提供足夠的信息,同時應當提醒讀者在正確使用此類時應當考慮的因素。若是類有任何同步前提,請用文檔說明。若是該類的實例可被多線程訪問,要特別注意文檔說明多線程環境下相關的規則和常量使用。
若是你想用一小段代碼演示這個類的基本用法或一般用法,放在類註釋裏也很是合適。
若是類的聲明和定義分開了(例如分別放在了 .h 和 .cc 文件中),此時,描述類用法的註釋應當和接口定義放在一塊兒,描述類的操做和實現的註釋應當和實現放在一塊兒。
8.4。函數註釋
總述
函數聲明處的註釋描述函數功能; 定義處的註釋描述函數實現。
說明
函數聲明
基本上每一個函數聲明處前都應當加上註釋,描述函數的功能和用途。只有在函數的功能簡單而明顯時才能省略這些註釋(例如,簡單的取值和設值函數)。註釋使用敘述式(「打開文件」)而非指令式(「打開文件」); 註釋只是爲了描述函數,而不是命令函數作什麼。一般,註釋不會描述函數如何工做。那是函數定義部分的事情。
函數聲明處註釋的內容:
函數的輸入輸出。
對類成員函數而言:函數調用期間對象是否須要保持引用參數,是否會釋放這些參數。
函數是否分配了必須由調用者釋放的空間。
參數是否能夠爲空指針。
是否存在函數使用上的性能隱患。
若是函數是可重入的,其同步提提是什麼?
舉例以下:
//返回此表的迭代器。
當迭代器完成時,它是
客戶端的責任//而且一旦
建立
迭代器的GargantuanTable對象被刪除,它就不能使用迭代器。//
//迭代器最初位於表的開始位置。
//
//此方法等同於:
// Iterator * iter = table-> NewIterator();
// iter-> Seek(「」);
//返回iter;
//若是您要當即尋找到
返回的迭代器
中的其餘位置,則使用NewIterator()會更快,並避免額外的查找。
Iterator * GetIterator () const;
但也要避免羅羅嗦嗦,或者對顯着易見的內容進行說明。下面的註釋就沒有必要加上「不然返回false」,由於已經暗含其中了:
//若是表不能包含更多條目,則返回true。
bool IsTableFull ();
註釋函數重載時,註釋的重點應該是函數中被重載的部分,而不是簡單的重複被重載的函數的註釋。多數狀況下,函數重載不須要額外的文檔,所以也沒有必要加上註釋。
註釋構造/析構函數,切記讀代碼的人知道構造/析構函數的全部功能,因此「銷燬這一對象」這樣的註釋是沒有意義的。你應該注意的是注意構造函數對參數作了什麼(例如,是否取得指針全部權)以及析構函數清理了什麼。若是都是些可有可無的內容,直接省掉註釋。析構函數前沒有註釋是很正常的。
函數定義
若是函數的實現過程當中用到了很巧妙的方式,那麼在函數定義處應當加上解釋性的註釋。例如,你所使用的編程技巧,實現的大體步驟,或解釋如此實現的理由。舉個例子,你能夠說明爲何函數的前半部分要加鎖然後半部分不須要。
不要 從 .h 文件或其餘地方的函數聲明處直接複製註釋。簡要重述函數功能是能夠的,但註釋重點要放在如何實現上。
8.5。變量註釋
總述
一般變量名自己足以很好說明變量用途。某些狀況下,也須要額外的註釋說明。
說明
類數據成員
每一個類數據成員(也叫實例變量或成員變量)都應該用註釋說明用途。若是有非變量的參數(例如特殊值,數據成員之間的關係,生命週期等)不可以使用類型與變量名明確表達,則應當加上註釋。然而,若是變量類型與變量名已經足夠描述一個變量,那麼就不須要加上註釋。
特別地,若是變量能夠接受 NULL 或 -1 等警惕值,須加以說明。好比:
private :
//用於限制檢查表訪問。-1意味着
//咱們還不知道表中有多少個條目。
int num_total_entries_ ;
全局變量
和數據成員同樣,全部全局變量也要註釋說明含義及用途,以及做爲全局變量的緣由。好比:
//在此迴歸測試中咱們經歷的測試用例的總數。
const int kNumTestCases = 6 ;
8.6。實現註釋
總述
對於代碼中巧妙的,晦澀的,有趣的,重要的地方加以註釋。
說明
代碼前註釋
巧妙或複雜的代碼段前要加註釋。好比:
//將結果除以2,考慮到x
//包含來自add的進位。
for (int i = 0 ; i < result - > size (); i ++ ) {
x = (x << 8 ) + (* result )[ i ];
(* 結果)[ i ] = x >> 1 ;
x &= 1 ;
}
行註釋
比較隱晦的地方要在行尾加入註釋。在行尾空兩格進行註釋。好比:
//若是咱們有足夠的內存,也能夠對數據部分進行mmap。
mmap_budget = max < int64 > (0 , mmap_budget - index_ - > length ());
if (mmap_budget > = data_size_ && !MmapData (mmap_chunk_bytes , mlock ))
return ; //錯誤已經記錄。
注意,這裏用了兩段註釋分別描述這段代碼的做用,並提示函數返回錯誤已經被記入日誌。
若是你須要連續進行多行註釋,能夠使之對齊得到更好的可讀性:
DoSomething (); //在這裏發表評論,以便評論排成一行。
DoSomethingElseThatIsLonger (); //代碼和註釋之間有兩個空格。
{ //容許打開一個新的做用域時,在註釋以前的一個空格
// //所以註釋與下面的註釋和代碼一塊兒排列。
DoSomethingElse (); //一般在行註釋以前有兩個空格。
}
std :: vector < string > list {
//支撐列表中的註釋描述下一個元素...
「First item」 ,
// ..而且應該適當地對齊。
「第二項」 };
作一點事(); / *對於尾部塊註釋,一個空間能夠。* /
函數參數註釋
若是函數參數的意義不明顯,考慮用下面的方式進行彌補:
若是參數是一個字面常量,而且這一常量在多處函數調用中被使用,用以推斷它們一致,你應該用一個常量名讓這個約定變得更明顯,而且保證這一約定不會被打破。
考慮更改函數的簽名,讓某個 bool 類型的參數變爲 enum 類型,這樣可讓這個參數的值表達其意義。
若是某個函數有多個配置選項,你能夠考慮定義一個類或結構體以保存全部的選項,並傳入類或結構體的實例。這樣的方法有許多優勢,例如這樣的選項能夠在調用處用變量名引用,這樣就能清晰地代表其意義。同時也減小了函數參數的數量,使得函數調用更易讀也易寫。除此以外,以這樣的方式,若是你使用其餘的選項,就無需對調用點進行更改。
用具名變量代替大段而複雜的嵌套表達式。
萬不得已時,才考慮在調用點用註釋闡明參數的意義。
好比下面的示例的對比:
//這些論據是什麼?
const DecimalNumber product = CalculateProduct (values , 7 , false , nullptr );
和
ProductOptions 選項;
選項。set_precision_decimals (7 );
選項。set_use_cache (ProductOptions :: kDontUseCache );
const DecimalNumber product =
CalculateProduct (values , options , / * completion_callback = * / nullptr );
哪一個更清晰一目瞭然。
不容許的行爲
不要描述顯而易見的現象, 永遠不要 用天然語言翻譯代碼做爲註釋,除非即便對深刻理解C ++的讀者來講代碼的行爲都是不明顯的。要假設讀代碼的人C ++水平比你高,即使他/她可能不知道你的用意:
你所提供的註釋應當解釋代碼 爲何 要這麼作和代碼的目的,或者最好是讓代碼自文檔化。
比較這樣的註釋:
//在矢量中查找元素。< - 差:這太明顯了!
自動 ITER = STD :: 找到(v 。開始(), v 。端(), 元素);
若是 (ITER =! v 。端()) {
過程(元件);
}
和這樣的註釋:
//處理「元素」,除非它已經被處理。
自動 ITER = STD :: 找到(v 。開始(), v 。端(), 元素);
若是 (ITER =! v 。端()) {
過程(元件);
}
自文檔化的代碼根本就不須要註釋。上面例子中的註釋對下面的代碼來講就是毫無必要的:
if (!IsAlreadyProcessed (element )) {
Process (element );
}
8.8。標點,拼寫和語法
總述
注意標點,拼寫和語法; 寫的好的註釋比差的要易讀的多。
說明
註釋的一般寫法是包含正確大小寫和結尾句號的完整敘述性語句。大多數狀況下,完整的句子比句子片斷可讀性更高。短一點的註釋,好比代碼行尾註釋,能夠隨意點,但依然要注意風格的一致性。
雖然被別人指出該用分號時卻用了逗號多少有些尷尬,但清晰易讀的代碼仍是很重要的。正確的標點,拼寫和語法對此會有很大幫助。
8.8。TODO註釋
總述
對那些臨時的,短時間的解決方案,或已經夠好,但仍不完美的代碼使用 TODO 註釋。
TODO 注意要使用全大寫的字符串 TODO,在隨後的圓括號裏寫上你的名字,郵件地址,bug ID,或其它身份標識和與這一 TODO 相關的問題。主要目的是讓添加註釋的人(也是能夠請求提供更多細節的人)可根據規範的 TODO 格式進行查找。添加 TODO 註釋並不意味着你要本身來修正,所以當你加上帶有姓名的時候 TODO ,通常都是寫上本身的名字。
// TODO(kl@gmail.com):這裏使用「*」做爲鏈接運算符。
// TODO(Zeke)將其改成使用關係。
// TODO(錯誤12345):刪除「最後訪問者」功能
若是加 TODO 是爲了在「未來某一天作某事」,能夠附上一個很是明確的時間「Fix by November 2005」),或者一個明確的事項(「全部客戶端均可以處理XML響應時刪除此代碼。」) 。
8.9。棄用註釋
總述
經過棄用註釋(DEPRECATED 評論)以標記某接口點已棄用。
您能夠寫上包含全大寫的 DEPRECATED 註釋,以標記某接口爲棄用狀態。註釋能夠放在接口聲明前,或者同一行。
在DEPRECATED 一詞後,在 括號中留下您的名字,郵箱地址以及其餘身份標識。
棄用註釋應當包涵簡短而清晰的指引,以幫助其餘人修復其調用點。在C ++中,你能夠將一個棄用函數改形成一個內聯函數,這一函數將調用新的接口。
DEPRECATED 僅僅標記接口爲並 不容許你們不約而同地棄用,您還得親自主動修正調用點(callsites),或是找個幫手。
修正好的代碼應該不會再涉及棄用接口點了,着實改用新接口點。若是您不知從何下手,能夠找標記棄用註釋的當事人一塊兒商量。
9.格式
每一個人均可能有本身的代碼風格和格式,但若是一個項目中的全部人都遵循同一風格的話,這個項目就能更順利地進行。每一個人未必能贊成下述的每一處格式規則,並且其中的很多規則須要必定時間的適應,但整個項目服從統一的編程風格是很重要的,只有這樣才能讓全部人輕鬆地閱讀和理解代碼。
爲了幫助你正確的格式化代碼,咱們寫了一個 emacs配置文件。
9.1。行長度
總述
每一行代碼字符數不超過80。
咱們也認識到這條規則是有爭議的,但不少已有代碼都遵守這一規則,所以咱們感受一致性更重要。
優勢
提倡該原則的人認爲強迫他們調整編輯器窗口大小是很野蠻的行爲。不少人同時並排開幾個代碼窗口,根本沒有多餘的空間拉伸窗口。你們都把窗口最大尺寸加以限定,而且80列寬是傳統標準。那麼爲何要改變呢?
缺點
反對該原則的人則認爲更寬的代碼行更易閱讀。80列的限制是上個世紀60年代的大型機的古板缺陷; 現代設備具備更寬的顯示屏,能夠很輕鬆地顯示更多代碼。
結論
80個字符是最大值。
若是沒法在不傷害易讀性的條件下進行斷行,那麼註釋行能夠超過80個字符,這樣能夠方便複製粘貼。例如,帶有命令示例或URL的行能夠超過80個字符。
長全部遊戲的路徑 #include 語句能夠超出80列。
文件頭保護 能夠無視該原則。
9.2。非ASCII字符
總述
儘可能不使用非ASCII字符,使用時必須使用UTF-8編碼。
說明
即便是英文,也不該將用戶界面的文本硬編碼到源代碼中,所以非ASCII字符應當不多被用到。特殊狀況下能夠適當包含此類字符。例如,代碼分析外部數據文件時,能夠適當硬編碼數據文件中做爲分隔符的非ASCII字符串; 更常見的是(不須要本地化的)單元測試代碼可能包含非ASCII字符串。此類狀況下,應使用UTF-8編碼,由於不少工具均可以理解和處理UTF-8編碼。
十六進制編碼也能夠,能加強可讀性的狀況下尤爲鼓鼓 - 好比 "\xEF\xBB\xBF",或者更簡潔地寫做 u8"\uFEFF",在Unicode中是 零寬度無間斷 的間隔符號,若是不用十六進制直接放在UTF -8格式的源文件中,是看不到的。
(Yang.Y注: "\xEF\xBB\xBF" 一般用做帶編碼標記的UTF-8)
使用 u8 前綴把帶 uXXXX 轉義序列的字符串字面值編碼成UTF-8。不要用在自己就帶UTF-8字符的字符串字面值上,由於若是編譯器不把源代碼識別成UTF-8,輸出就會出錯。
別用C ++ 11的 char16_t 和 char32_t,它們和UTF-8文本沒有關係, wchar_t 同理,除非你寫的代碼要調用Windows API,後者普遍使用了 wchar_t。
9.3。空格仍是製表位
總述
只使用空格,每次縮進2個空格。
說明
咱們使用空格縮進。不要在代碼中使用製表符。你應該設置編輯器將製表符轉爲空格。
9.4。函數聲明與定義
總述
返回類型和函數名在同一行,參數也儘可能放在同一行,若是放不下就對形參分行,分行方式與 函數調用 一致。
說明
函數看上去像這樣:
ReturnType ClassName :: FunctionName (Type par_name1 , Type par_name2 ) {
DoSomething ();
...
}
若是同一行文本太多,放不下全部參數:
ReturnType ClassName :: ReallyLongFunctionName (類型 par_name1 , 類型 par_name2 ,
類型 par_name3 ) {
DoSomething ();
...
}
甚至連第一個參數都放不下:
ReturnType LongClassName :: ReallyReallyReallyLongFunctionName (
Type par_name1 , // 4 space indent
Type par_name2 ,
Type par_name3 ) {
DoSomething (); // 2空格縮進
...
}
注意如下幾點:
使用好的參數名。
只有參數未被使用或者其用途很是明顯時,才能省略參數名。
若是返回類型和函數名在一行放不下,分行。
若是返回類型與函數聲明或定義分行了,不要縮進。
左圓括號老是和函數名在同一行。
函數名和左圓括號間永遠沒有空格。
圓括號與參數間沒有空格。
左大括號總在最後一個參數同一行的末尾處,不另起新行。
右大括號老是單獨位於函數最後一行,或者與左大括號同一行。
右圓括號和左大括號間老是有一個空格。
全部形參應儘量對齊。
缺省縮進爲2個空格。
換行後的參數保持4個空格的縮進。
未被使用的參數,或者根據上下文很容易看出其用途的參數,能夠省略參數名:
類 美孚 {
公共:
美孚(富&& );
Foo (const Foo &);
Foo & operator = (Foo && );
Foo & operator = (const Foo &);
};
未被使用的參數若是其用途不明顯的話,在函數定義處將參數名註釋起來:
class Shape {
public :
virtual void Rotate (double radians ) = 0 ;
};
class Circle : public Shape {
public:
void Rotate(double radians) override;
};
void Circle::Rotate(double /*radians*/) {}
// 差 - 若是未來有人要實現, 很難猜出變量的做用.
void Circle::Rotate(double) {}
屬性, 和展開爲屬性的宏, 寫在函數聲明或定義的最前面, 即返回類型以前:
MUST_USE_RESULT bool IsOK();
9.5. Lambda 表達式
總述
Lambda 表達式對形參和函數體的格式化和其餘函數一致; 捕獲列表同理, 表項用逗號隔開.
說明
若用引用捕獲, 在變量名和 & 之間不留空格.
int x = 0;
auto add_to_x = [&x](int n) { x += n; };
短 lambda 就寫得和內聯函數同樣.
std::set<int> blacklist = {7, 8, 9};
std::vector<int> digits = {3, 9, 1, 8, 4, 7, 1};
digits.erase(std::remove_if(digits.begin(), digits.end(), [&黑名單](詮釋 我) {
返回 黑名單。找到(i ) != 黑名單。end ();
}),
數字。end ());
9.6。函數調用
總述
要麼一行寫完函數調用,要麼在圓括號裏對參數分行,要麼參數另起一行且縮進四格。若是沒有其它顧慮的話,儘量精簡行數,好比把多個參數適當地放在同一行裏。
說明
函數調用遵循以下形式:
bool retval = DoSomething (argument1 , argument2 , argument3 );
若是同一行放不下,可斷爲多行,後面每一行都和第一個實參對齊,左圓括號後和右圓括號前不要留空格:
bool retval = DoSomething (averyveryveryverylongargument1 ,
argument2 , argument3 );
參數也能夠放在次行,縮進四格:
若是 (...) {
...
...
if (...) {
DoSomething (
argument1 , argument2 , // 4空格縮進
argument3 , argument4 );
}
把多個參數放在同一行以減小函數調用所需的行數,除非影響到可讀性。有人認爲把每一個參數都獨立成行,不只更好讀,並且方便編輯參數。不過,比起所謂的參數編輯,咱們更看重可讀性,且後者比較好辦:
若是一些參數自己就是略複雜的表達式,且下降了可讀性,那麼能夠直接建立臨時變量描述該表達式,並傳遞給函數:
int my_heuristic = scores[x] * y + bases[x];
bool retval = DoSomething(my_heuristic, x, y, z);
或者放着無論, 補充上註釋:
bool retval = DoSomething(scores[x] * y + bases[x], // Score heuristic.
x, y, z);
若是某參數獨立成行, 對可讀性更有幫助的話, 那也能夠如此作. 參數的格式處理應當以可讀性而非其餘做爲最重要的原則.
此外, 若是一系列參數自己就有必定的結構, 能夠酌情地按其結構來決定參數格式:
//經過3x3矩陣轉換小部件。
my_widget 。變換(x1 , x2 , x3 ,
y1 , y2 , y3 ,
z1 , z2 , z3 );
9.7。列表初始化格式
總述
您平時怎麼格式化函數調用,就怎麼格式化 列表初始化。
說明
若是列表初始化伴隨着名字,好比類型或變量名,格式化時將將名稱視圖函數調用名, {}視圖函數調用的括號。若是沒有名字,就視做名字長度爲零。
//一行列表初始化示範。
返回 { foo , bar };
functioncall ({ foo , bar });
pair < int , int > p { foo , bar };
//當不得不行時。
SomeFunction (
{ 「在{」 } 以前假定一個零長度的名字,//假設在{前有長度爲零的名字
。some_other_function_parameter );
SomeType 變量{
some , other , values ,
{ 「假設在{」 } 以前有一個零長度的名字,//假設在{前有長度爲零的名字。
SomeOtherType {
「很是長的字符串須要周圍的中斷。」 , //很是長的字符串,先後都須要斷行。
一些, 其它 的值},
SomeOtherType { 「略短字符串」 , //稍短的字符串。
一些, 其它, 值}};
SomeType 變量{
「這太長了,沒法將全部內容放在一行中」 }; //字符串過長,所以沒法放在同一行。
MyType m = { //注意了,您能夠在{前斷行。
superlongvariablename1 ,
superlongvariablename2 ,
{ short , interior , list },
{ interiorwrappinglist ,
interiorwrappinglist2 }};
9.9。條件語句
總述
傾向於不在圓括號內使用空格。關鍵字 if 狀語從句: else 另起一行。
說明
對基本條件語句有兩種能夠接受的格式。一種在圓括號和條件之間有空格,另外一種沒有。
最多見的是沒有空格的格式。哪種均可以,最重要的是 保持一致。若是你是在修改一個文件,參考當前已有格式。若是是寫新的代碼,請參考目錄下或項目中的其它文件。還在猶豫的話,就不要加空格了。
if (condition ) { //圓括號裏沒有空格。
... // 2空格縮進。
} else if (...) { // else與if的右括號同一行。
...
} 其餘 {
...
}
若是你更喜歡在圓括號內部加空格:
if ( condition ) { //圓括號與空格緊鄰 - 不常見
... // 2空格縮進。
} else { // else與if的右括號同一行。
...
}
全部注意下狀況 if 狀語從句:左圓括號間都有個空格。右圓括號和左大括號之間也要有個空格:
if (condition ) //差 - IF後面沒空格。
if (condition ){ //差 - {前面沒空格。
if (condition ){ //變本加厲地差。
if (condition ) { //好 - IF和{都與空格緊鄰。
若是能加強可讀性,簡短的條件語句容許寫在同一行。當只有簡單語句而且沒有使用 else [主語]時使用:
if (x == kFoo ) 返回 新的 Foo ();
if (x == kBar ) 返回 新的 Bar ();
若是有語句 else 分支則不容許:
//不容許 - 當有ELSE分支時如塊塊寫在同一行
if (x ) DoThis ();
其餘 DoThat ();
一般,單行語句不須要使用大括號,若是你喜歡用也沒問題; 複雜的條件或循環語句用大括號可讀性會更好。有也。項目一些要求 if 必須老是使用大括號:
若是 (條件)
DoSomething (); // 2空格縮進。
if (condition ) {
DoSomething (); // 2空格縮進。
}
但若是語句中 if-else 某個分支使用了大括號的話,其它分支也必須使用:
//不能夠這樣子 - 若是有大括號ELSE卻沒有。
if (condition ) {
foo ;
} else
bar ;
//不能夠這樣子 - ELSE有大括號IF卻沒有。
若是 (條件)
foo ;
else {
bar ;
}
//只要其中一個分支用了大括號,兩個分支都要用上大括號。
if (condition ) {
foo ;
} else {
bar ;
}
9.9。循環和開關選擇語句
總述
switch 語句能夠使用大括號分段,以代表cases之間不是連在一塊兒的。在單語句循環裏,括號可用可不用。循環空應行業釋義體育使用 {} 或 continue。
說明
switch 語句中的 case 塊能夠使用大括號也能夠不用,取決於你的我的喜愛。若是用的話,要按照下文所述的方法。
若是有不知足 case 條件的枚舉值, switch 應該default 老是包含一個 匹配(若是有輸入值沒有case去處理,編譯器將給出警告)。若是 default 應該永遠執行不到,簡單的加條 assert:
switch (var ) {
case 0 : { // 2空格縮進
... // 4空格縮進
break ;
}
案例 1 : {
...
break ;
}
default : {
assert (false );
}
}
在單語句循環裏,括號可用可不用:
for (int i = 0 ; i < kSomeNumber ; ++ i )
printf (「我愛你\ n 」 );
for (int i = 0 ; i < kSomeNumber ; ++ i ) {
printf (「我拿回來\ n 」 );
}
空循環體應使用 {} 或 continue,而不是一個簡單的分號。
while (condition ) {
//反覆循環直到條件失效。
}
for (int i = 0 ; i < kSomeNumber ; ++ i ) {} //可 - 空循環體。
同時 (條件) 繼續; //可 - contunue代表沒有邏輯。
while (condition ); //差 - 看起來僅僅只是while / loop的部分之一。
9.10。指針和引用表達式
總述
句點或箭頭先後不要有空格。指針/地址操做符()以後不能有空格。*, &
說明
下面是指針和引用表達式的正確使用範例:
x = * p ;
p = &x ;
x = r 。y ;
x = r - > y ;
注意:
在訪問成員時,句點或箭頭先後沒有空格。
操做指針符 * 或 & 後沒有空格。
在聲明指針變量或參數時,星號與類型或變量名緊挨均可以:
//好,空格前置。
char * c ;
const string &str ;
//好,空格後置。
char * c ;
const string & str ;
int x , * y ; //不容許 - 在多重聲明中不能使用&或*
char * c ; //差 - *兩邊都有空格
const string & str ; //差 - &兩邊都有空格。
在單個文件內要保持風格一致,因此,若是是修改現有文件,要遵守該文件的風格。
9.11。布爾表達式
總述
若是一個布爾表達式超過 標準行寬,斷行方式要統一一下。
說明
下例中,邏輯與(&&)操做符總位於行尾:
if (this_one_thing > this_other_thing &&
a_third_thing == a_fourth_thing &&
yet_another && last_one ) {
...
}
注意,上例的邏輯與(&&)操做符均位於行尾。這個格式在Google裏很常見,雖然把全部操做符放在開頭也能夠。能夠考慮額外插入圓括號,合理使用的話對加強可讀性是頗有幫助的。此外,直接用符號形式的操做符,好比 && 和 ~,不要用詞語形式的 and 和 compl。
9.12。函數返回值
總述
在不要 return 表達式里加上非必須的圓括號。
說明
在只有寫 要加上括號的時候纔在 裏使用括號。x = exprreturn expr;
返回 結果; //返回很簡單,沒有圓括號。
//能夠用圓括號把複雜表達式圈起來,改善可讀性。
return (some_long_condition &&
another_condition );
回報 (價值); //畢竟您從歷來不會寫var =(value);
返回(結果); // return可不是函數!
9.13。變量及數組初始化
總述
用 =, () 狀語從句: {} 都可。
說明
您能夠用 =, () 和 {},如下的例子都是正確的:
int x = 3 ;
int x (3 );
int x { 3 };
字符串 名稱(「Some Name」 );
字符串 名稱 = 「某個名稱」 ;
字符串 名稱{ 「Some Name」 };
請當心務必列表初始化 {...} 用 std::initializer_list 構造函數初始化出的類型。非空列表初始化就會優先調用 std::initializer_list,不過空列表初始化除外,後者原則上會調用默認構造函數。爲了強制禁用 std::initializer_list 構造函數,請改用括號。
矢量< INT > v (100 , 1 ); //內容爲100個1的向量。
矢量< INT > v { 100 , 1 }; //內容爲100和1的向量。
此外,列表初始化不容許整型類型的四捨五入,這能夠用來避免一些類型上的編程失誤。
int pi (3.14 ); //好 - pi == 3.
int pi { 3.14 }; //編譯錯誤:縮略轉換。
9.14。預處理指令
總述
預處理指令不要縮進,從行首開始。
說明
即便預處理指令位於縮進代碼塊中,指令也應從行首開始。
//好 - 指令從行首開始
if (lopsided_score ) {
#if DISASTER_PENDING //正確 - 從行首開始
DropEverything ();
#if NOTIFY //非必要 - #後跟空格
NotifyClient ();
#endif
#endif
BackToNormal ();
}
//差-指令縮進
若是 (lopsided_score ) {
的#if DISASTER_PENDING //差- 「的#if」應該放在行開頭
DropEverything ();
#endif //差 - 「#endif」不要縮進
BackToNormal ();
}
9.15。類格式
總述
訪問控制塊的聲明依次序是 public:, protected:, private:,每一個都縮進1個空格。
說明
類聲明(下面的代碼中缺乏註釋,參考 類註釋)的基本格式以下:
class MyClass : public OtherClass {
public : //注意有一個空格的縮進
MyClass (); //標準的兩空格縮進
顯式 MyClass (int var );
〜MyClass的() {}
void SomeFunction ();
void SomeFunctionThatDoesNothing () {
}
void set_some_var (int var ) { some_var_ = var ; }
int some_var () const { return some_var_ ; }
private :
bool SomeInternalFunction ();
int some_var_ ;
int some_other_var_ ;
};
注意事項:
全部基類名應在80列限制下儘可能與子類名放在同一行。
關鍵詞 public:, protected:, private: 要縮進1個空格。
除第一個關鍵詞(通常是 public)外,其餘關鍵詞前要空一行。若是類比較小的話也能夠不空。
這些關鍵詞後不要保留空行。
public 放在最前面,而後是 protected,最後是 private。
聲明關於順序的規則請參考 聲明順序 一節。
9.16。構造函數初始值列表
總述
構造函數初始化列表放在同一行或按四格縮進並排多行。
說明
下面兩種初始值列表方式均可以接受:
//若是全部變量能放在同一行:
MyClass :: MyClass (int var ) : some_var_ (var ) {
DoSomething ();
}
//若是不能放在同一行,
//必須置於冒號後,並縮進4個空格
MyClass :: MyClass (int var )
: some_var_ (var ), some_other_var_ (var + 1 ) {
DoSomething ();
}
//若是初始化列表須要置於多行,將每一個成員放在單獨的一行
//並逐行對齊
MyClass :: MyClass (int var )
: some_var_ (var ), // 4空格縮進
some_other_var_ (var + 1 ) { //列隊
DoSomething ();
}
//右大括號}能夠和左大括號{放在同一行
//若是這樣作合適的話
MyClass :: MyClass (int var )
: some_var_ (var ) {}
9.17。命名空間格式化
總述
命名空間內容不縮進。
說明
命名空間 不要增長額外的縮進層次,例如:
命名空間 {
void foo () { //正確。命名空間內沒有額外的縮進。
...
}
} //命名空間
不要在命名空間內縮進:
命名空間 {
//錯,縮進多餘了。
void foo () {
...
}
} //命名空間
聲明嵌套命名空間時,每一個命名空間都獨立成行。
namespace foo {
namespace bar {
9.19。水平留白
總述
水平留白的使用根據在代碼中的位置決定。永遠不要在行尾添加沒意義的留白。
說明
通用
void f (bool b ) { //左大括號前老是有空格。
...
int i = 0 ; //分號前不加空格。
//列表初始化中大括號內的空格是可選的。
//若是加了空格,那麼兩邊都要加。
int x [] = { 0 };
int x [] = { 0 };
//繼承與初始列表中的冒號先後恆有空格。
class Foo : public Bar {
public :
//對於單行函數的實現,在大括號內加上空格
//而後是函數實現
Foo (int b ) : Bar (), baz_ (b ) {} //大括號裏面是空的話,不加空格。
void Reset () { baz_ = 0 ; } //用括號把大括號與實現分開。
...
添加冗餘的留白會給其餘人編輯時形成額外負擔。所以,行尾不要留空格。若是肯定一行代碼已經修改完畢,將多餘的空格去掉; 或者在專門清理空格時去掉(尤爲是在沒有其餘人在處理這件事的時候)。(Yang.Y注:如今大部分代碼編輯器稍加設置後,都支持自動刪除行首/行尾空格,若是不支持,考慮換一款編輯器或IDE)
循環和條件語句
if (b ) { // if條件語句和循環語句關鍵字後均有空格。
} else { // else先後有空格。
}
while (test ) {} //圓括號內部不緊鄰空格。
switch (i ) {
for (int i = 0 ; i < 5 ; ++ i ) {
switch ( i ) { //循環和條件語句的圓括號裏能夠與空格緊鄰。
若是 ( 測試 ) { //圓括號,但這不多見。總之要一致。
for ( int i = 0 ; 我 < 5 ; ++ i ) {
for ( ; i < 5 ; ++ i ) { //循環裏內; 後恆有空格,; 前能夠加個空格。
switch (i ) {
case 1 : // switch case的冒號前無空格。
...
案例 2 : 打破; //若是冒號有代碼,加個空格。
操做符
//賦值運算符先後老是有空格。
x = 0 ;
//其它二元操做符也先後恆有空格,不過對於表達式的子式能夠不加空格。
//圓括號內部沒有緊鄰空格。
v = w * x + y / z ;
v = w * x + y / z ;
v = w * (x + z );
//在參數和一元操做符之間不加空格。
X = - 5 ;
++ x ;
若是 (x && !y )
...
模板和轉換
//尖括號(<和>)不與空格緊鄰,<前沒有空格,>和(之間也沒有
矢量< 字符串> X ;
ý = 的static_cast < 炭*> (X );
//在類型與指針操做符之間留空格也能夠,但要保持一致。
vector < char *> x ;
9.19。垂直留白
總述
垂直留白越少越好。
說明
這不只僅是規則而是原則問題了:不在僅萬不得已,不要使用空行。尤爲是:兩個函數定義之間的空行不要超過2行,函數體首尾不要留空行,函數體中也不要隨意添加空行。
基本原則是:同一屏能夠顯示的代碼越多,越容易理解程序的控制流程。固然,過密集的代碼塊和過於疏鬆的代碼塊一樣難看,這取決於你的判斷。但一般是垂直留白越少越好。
下面的規則可讓加入的空行更有效:有點可讀性。
摘錄自谷歌開源項目開源指南。