9. 格式
每個人都可能有自己的代碼風(fēng)格和格式, 但如果一個項目中的所有人都遵循同一風(fēng)格的話, 這個項目就能更順利地進行. 每個人未必能同意下述的每一處格式規(guī)則, 而且其中的不少規(guī)則需要一定時間的適應(yīng), 但整個項目服從統(tǒng)一的編程風(fēng)格是很重要的, 只有這樣才能讓所有人輕松地閱讀和理解代碼.
為了幫助你正確的格式化代碼, 我們寫了一個emacs 配置文件.
9.1. 行長度
總述
每一行代碼字符數(shù)不超過 80.
我們也認(rèn)識到這條規(guī)則是有爭議的, 但很多已有代碼都遵照這一規(guī)則, 因此我們感覺一致性更重要.
優(yōu)點
提倡該原則的人認(rèn)為強迫他們調(diào)整編輯器窗口大小是很野蠻的行為. 很多人同時并排開幾個代碼窗口, 根本沒有多余的空間拉伸窗口. 大家都把窗口最大尺寸加以限定, 并且 80 列寬是傳統(tǒng)標(biāo)準(zhǔn). 那么為什么要改變呢?
缺點
反對該原則的人則認(rèn)為更寬的代碼行更易閱讀. 80 列的限制是上個世紀(jì) 60 年代的大型機的古板缺陷; 現(xiàn)代設(shè)備具有更寬的顯示屏, 可以很輕松地顯示更多代碼.
結(jié)論
80 個字符是最大值.
如果無法在不傷害易讀性的條件下進行斷行, 那么注釋行可以超過 80 個字符, 這樣可以方便復(fù)制粘貼. 例如, 帶有命令示例或 URL 的行可以超過 80 個字符.
包含長路徑的#include語句可以超出80列.
頭文件保護可以無視該原則.
9.2. 非 ASCII 字符
總述
盡量不使用非 ASCII 字符, 使用時必須使用 UTF-8 編碼.
說明
即使是英文, 也不應(yīng)將用戶界面的文本硬編碼到源代碼中, 因此非 ASCII 字符應(yīng)當(dāng)很少被用到. 特殊情況下可以適當(dāng)包含此類字符. 例如, 代碼分析外部數(shù)據(jù)文件時, 可以適當(dāng)硬編碼數(shù)據(jù)文件中作為分隔符的非 ASCII 字符串; 更常見的是 (不需要本地化的) 單元測試代碼可能包含非 ASCII 字符串. 此類情況下, 應(yīng)使用 UTF-8 編碼, 因為很多工具都可以理解和處理 UTF-8 編碼.
十六進制編碼也可以, 能增強可讀性的情況下尤其鼓勵 —— 比如"\xEF\xBB\xBF", 或者更簡潔地寫作u8"\uFEFF", 在 Unicode 中是零寬度 無間斷的間隔符號, 如果不用十六進制直接放在 UTF-8 格式的源文件中, 是看不到的.
(Yang.Y 注:"\xEF\xBB\xBF"通常用作 UTF-8 with BOM 編碼標(biāo)記)
使用u8前綴把帶uXXXX轉(zhuǎn)義序列的字符串字面值編碼成 UTF-8. 不要用在本身就帶 UTF-8 字符的字符串字面值上, 因為如果編譯器不把源代碼識別成 UTF-8, 輸出就會出錯.
別用 C++11 的char16_t和char32_t, 它們和 UTF-8 文本沒有關(guān)系,wchar_t同理, 除非你寫的代碼要調(diào)用 Windows API, 后者廣泛使用了wchar_t.
9.3. 空格還是制表位
總述
只使用空格, 每次縮進 2 個空格.
說明
我們使用空格縮進. 不要在代碼中使用制表符. 你應(yīng)該設(shè)置編輯器將制表符轉(zhuǎn)為空格.
9.4. 函數(shù)聲明與定義
總述
返回類型和函數(shù)名在同一行, 參數(shù)也盡量放在同一行, 如果放不下就對形參分行, 分行方式與函數(shù)調(diào)用一致.
說明
函數(shù)看上去像這樣:
ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) { DoSomething(); ...}
如果同一行文本太多, 放不下所有參數(shù):
ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2, Type par_name3) { DoSomething(); ...}
甚至連第一個參數(shù)都放不下:
ReturnType LongClassName::ReallyReallyReallyLongFunctionName( Type par_name1, // 4 space indent Type par_name2, Type par_name3) { DoSomething(); // 2 space indent ...}
注意以下幾點:
使用好的參數(shù)名.
只有在參數(shù)未被使用或者其用途非常明顯時, 才能省略參數(shù)名.
如果返回類型和函數(shù)名在一行放不下, 分行.
如果返回類型與函數(shù)聲明或定義分行了, 不要縮進.
左圓括號總是和函數(shù)名在同一行.
函數(shù)名和左圓括號間永遠沒有空格.
圓括號與參數(shù)間沒有空格.
左大括號總在最后一個參數(shù)同一行的末尾處, 不另起新行.
右大括號總是單獨位于函數(shù)最后一行, 或者與左大括號同一行.
右圓括號和左大括號間總是有一個空格.
所有形參應(yīng)盡可能對齊.
缺省縮進為 2 個空格.
換行后的參數(shù)保持 4 個空格的縮進.
未被使用的參數(shù), 或者根據(jù)上下文很容易看出其用途的參數(shù), 可以省略參數(shù)名:
class Foo { public: Foo(Foo&&); Foo(const Foo&); Foo& operator=(Foo&&); Foo& operator=(const Foo&);};
未被使用的參數(shù)如果其用途不明顯的話, 在函數(shù)定義處將參數(shù)名注釋起來:
class Shape { public: virtual void Rotate(double radians) = 0;};class Circle : public Shape { public: void Rotate(double radians) override;};void Circle::Rotate(double /*radians*/) {}// 差 - 如果將來有人要實現(xiàn), 很難猜出變量的作用.void Circle::Rotate(double) {}
屬性, 和展開為屬性的宏, 寫在函數(shù)聲明或定義的最前面, 即返回類型之前:
MUST_USE_RESULT bool IsOK();
9.5. Lambda 表達式
總述
Lambda 表達式對形參和函數(shù)體的格式化和其他函數(shù)一致; 捕獲列表同理, 表項用逗號隔開.
說明
若用引用捕獲, 在變量名和&之間不留空格.
int x = 0;auto add_to_x = [&x](int n) { x += n; };
短 lambda 就寫得和內(nèi)聯(lián)函數(shù)一樣.
std::set
9.6. 函數(shù)調(diào)用
總述
要么一行寫完函數(shù)調(diào)用, 要么在圓括號里對參數(shù)分行, 要么參數(shù)另起一行且縮進四格. 如果沒有其它顧慮的話, 盡可能精簡行數(shù), 比如把多個參數(shù)適當(dāng)?shù)胤旁谕恍欣?
說明
函數(shù)調(diào)用遵循如下形式:
bool retval = DoSomething(argument1, argument2, argument3);
如果同一行放不下, 可斷為多行, 后面每一行都和第一個實參對齊, 左圓括號后和右圓括號前不要留空格:
bool retval = DoSomething(averyveryveryverylongargument1, argument2, argument3);
參數(shù)也可以放在次行, 縮進四格:
if (...) { ... ... if (...) { DoSomething( argument1, argument2, // 4 空格縮進 argument3, argument4); }
把多個參數(shù)放在同一行以減少函數(shù)調(diào)用所需的行數(shù), 除非影響到可讀性. 有人認(rèn)為把每個參數(shù)都獨立成行, 不僅更好讀, 而且方便編輯參數(shù). 不過, 比起所謂的參數(shù)編輯, 我們更看重可讀性, 且后者比較好辦:
如果一些參數(shù)本身就是略復(fù)雜的表達式, 且降低了可讀性, 那么可以直接創(chuàng)建臨時變量描述該表達式, 并傳遞給函數(shù):
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);
如果某參數(shù)獨立成行, 對可讀性更有幫助的話, 那也可以如此做. 參數(shù)的格式處理應(yīng)當(dāng)以可讀性而非其他作為最重要的原則.
此外, 如果一系列參數(shù)本身就有一定的結(jié)構(gòu), 可以酌情地按其結(jié)構(gòu)來決定參數(shù)格式:
// 通過 3x3 矩陣轉(zhuǎn)換 widget.my_widget.Transform(x1, x2, x3, y1, y2, y3, z1, z2, z3);
9.7. 列表初始化格式
總述
您平時怎么格式化函數(shù)調(diào)用, 就怎么格式化列表初始化.
說明
如果列表初始化伴隨著名字, 比如類型或變量名, 格式化時將將名字視作函數(shù)調(diào)用名,{}視作函數(shù)調(diào)用的括號. 如果沒有名字, 就視作名字長度為零.
// 一行列表初始化示范.return {foo, bar};functioncall({foo, bar});pair
9.9. 條件語句
總述
傾向于不在圓括號內(nèi)使用空格. 關(guān)鍵字if和else另起一行.
說明
對基本條件語句有兩種可以接受的格式. 一種在圓括號和條件之間有空格, 另一種沒有.
最常見的是沒有空格的格式. 哪一種都可以, 最重要的是保持一致. 如果你是在修改一個文件, 參考當(dāng)前已有格式. 如果是寫新的代碼, 參考目錄下或項目中其它文件. 還在猶豫的話, 就不要加空格了.
if (condition) { // 圓括號里沒有空格. ... // 2 空格縮進.} else if (...) { // else 與 if 的右括號同一行. ...} else { ...}
如果你更喜歡在圓括號內(nèi)部加空格:
if ( condition ) { // 圓括號與空格緊鄰 - 不常見 ... // 2 空格縮進.} else { // else 與 if 的右括號同一行. ...}
注意所有情況下if和左圓括號間都有個空格. 右圓括號和左大括號之間也要有個空格:
if(condition) // 差 - IF 后面沒空格.if (condition){ // 差 - { 前面沒空格.if(condition){ // 變本加厲地差.if (condition) { // 好 - IF 和 { 都與空格緊鄰.
如果能增強可讀性, 簡短的條件語句允許寫在同一行. 只有當(dāng)語句簡單并且沒有使用else子句時使用:
if (x == kFoo) return new Foo();if (x == kBar) return new Bar();
如果語句有else分支則不允許:
// 不允許 - 當(dāng)有 ELSE 分支時 IF 塊卻寫在同一行if (x) DoThis();else DoThat();
通常, 單行語句不需要使用大括號, 如果你喜歡用也沒問題; 復(fù)雜的條件或循環(huán)語句用大括號可讀性會更好. 也有一些項目要求if必須總是使用大括號:
if (condition) DoSomething(); // 2 空格縮進.if (condition) { DoSomething(); // 2 空格縮進.}
但如果語句中某個if-else分支使用了大括號的話, 其它分支也必須使用:
// 不可以這樣子 - IF 有大括號 ELSE 卻沒有.if (condition) { foo;} else bar;// 不可以這樣子 - ELSE 有大括號 IF 卻沒有.if (condition) foo;else { bar;}// 只要其中一個分支用了大括號, 兩個分支都要用上大括號.if (condition) { foo;} else { bar;}
9.9. 循環(huán)和開關(guān)選擇語句
總述
switch語句可以使用大括號分段, 以表明 cases 之間不是連在一起的. 在單語句循環(huán)里, 括號可用可不用. 空循環(huán)體應(yīng)使用{}或continue.
說明
switch語句中的case塊可以使用大括號也可以不用, 取決于你的個人喜好. 如果用的話, 要按照下文所述的方法.
如果有不滿足case條件的枚舉值,switch應(yīng)該總是包含一個default匹配 (如果有輸入值沒有 case 去處理, 編譯器將給出 warning). 如果default應(yīng)該永遠執(zhí)行不到, 簡單的加條assert:
switch (var) { case 0: { // 2 空格縮進 ... // 4 空格縮進 break; } case 1: { ... break; } default: { assert(false); }}
在單語句循環(huán)里, 括號可用可不用:
for (int i = 0; i < kSomeNumber; ++i) printf("I love you\n");for (int i = 0; i < kSomeNumber; ++i) { printf("I take it back\n");}
空循環(huán)體應(yīng)使用{}或continue, 而不是一個簡單的分號.
while (condition) { // 反復(fù)循環(huán)直到條件失效.}for (int i = 0; i < kSomeNumber; ++i) {} // 可 - 空循環(huán)體.while (condition) continue; // 可 - contunue 表明沒有邏輯.while (condition); // 差 - 看起來僅僅只是 while/loop 的部分之一.
9.10. 指針和引用表達式
總述
句點或箭頭前后不要有空格. 指針/地址操作符 (*,&) 之后不能有空格.
說明
下面是指針和引用表達式的正確使用范例:
x = *p;p = &x;x = r.y;x = r->y;
注意:
在訪問成員時, 句點或箭頭前后沒有空格.
指針操作符*或&后沒有空格.
在聲明指針變量或參數(shù)時, 星號與類型或變量名緊挨都可以:
// 好, 空格前置.char *c;const string &str;// 好, 空格后置.char* c;const string& str;int x, *y; // 不允許 - 在多重聲明中不能使用 & 或 *char * c; // 差 - * 兩邊都有空格const string & str; // 差 - & 兩邊都有空格.
在單個文件內(nèi)要保持風(fēng)格一致, 所以, 如果是修改現(xiàn)有文件, 要遵照該文件的風(fēng)格.
9.11. 布爾表達式
總述
如果一個布爾表達式超過標(biāo)準(zhǔn)行寬, 斷行方式要統(tǒng)一一下.
說明
下例中, 邏輯與 (&&) 操作符總位于行尾:
if (this_one_thing > this_other_thing && a_third_thing == a_fourth_thing && yet_another && last_one) { ...}
注意, 上例的邏輯與 (&&) 操作符均位于行尾. 這個格式在 Google 里很常見, 雖然把所有操作符放在開頭也可以. 可以考慮額外插入圓括號, 合理使用的話對增強可讀性是很有幫助的. 此外, 直接用符號形式的操作符, 比如&&和~, 不要用詞語形式的and和compl.
9.12. 函數(shù)返回值
總述
不要在return表達式里加上非必須的圓括號.
說明
只有在寫x=expr要加上括號的時候才在returnexpr;里使用括號.
return result; // 返回值很簡單, 沒有圓括號.// 可以用圓括號把復(fù)雜表達式圈起來, 改善可讀性.return (some_long_condition && another_condition);return (value); // 畢竟您從來不會寫 var = (value);return(result); // return 可不是函數(shù)!
9.13. 變量及數(shù)組初始化
總述
用=,()和{}均可.
說明
您可以用=,()和{}, 以下的例子都是正確的:
int x = 3;int x(3);int x{3};string name("Some Name");string name = "Some Name";string name{"Some Name"};
請務(wù)必小心列表初始化{...}用std::initializer_list構(gòu)造函數(shù)初始化出的類型. 非空列表初始化就會優(yōu)先調(diào)用std::initializer_list, 不過空列表初始化除外, 后者原則上會調(diào)用默認(rèn)構(gòu)造函數(shù). 為了強制禁用std::initializer_list構(gòu)造函數(shù), 請改用括號.
vector
此外, 列表初始化不允許整型類型的四舍五入, 這可以用來避免一些類型上的編程失誤.
int pi(3.14); // 好 - pi == 3.int pi{3.14}; // 編譯錯誤: 縮窄轉(zhuǎn)換.
9.14. 預(yù)處理指令
總述
預(yù)處理指令不要縮進, 從行首開始.
說明
即使預(yù)處理指令位于縮進代碼塊中, 指令也應(yīng)從行首開始.
// 好 - 指令從行首開始 if (lopsided_score) {#if DISASTER_PENDING // 正確 - 從行首開始 DropEverything();# if NOTIFY // 非必要 - # 后跟空格 NotifyClient();# endif#endif BackToNormal(); }// 差 - 指令縮進 if (lopsided_score) { #if DISASTER_PENDING // 差 - "#if" 應(yīng)該放在行開頭 DropEverything(); #endif // 差 - "#endif" 不要縮進 BackToNormal(); }
9.15. 類格式
總述
訪問控制塊的聲明依次序是public:,protected:,private:, 每個都縮進 1 個空格.
說明
類聲明 (下面的代碼中缺少注釋, 參考類注釋) 的基本格式如下:
class MyClass : public OtherClass { public: // 注意有一個空格的縮進 MyClass(); // 標(biāo)準(zhǔn)的兩空格縮進 explicit 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_;};
注意事項:
所有基類名應(yīng)在 80 列限制下盡量與子類名放在同一行.
關(guān)鍵詞public:,protected:,private:要縮進 1 個空格.
除第一個關(guān)鍵詞 (一般是public) 外, 其他關(guān)鍵詞前要空一行. 如果類比較小的話也可以不空.
這些關(guān)鍵詞后不要保留空行.
public放在最前面, 然后是protected, 最后是private.
關(guān)于聲明順序的規(guī)則請參考聲明順序一節(jié).
9.16. 構(gòu)造函數(shù)初始值列表
總述
構(gòu)造函數(shù)初始化列表放在同一行或按四格縮進并排多行.
說明
下面兩種初始值列表方式都可以接受:
// 如果所有變量能放在同一行: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 space indent some_other_var_(var + 1) { // lined up DoSomething();}// 右大括號 } 可以和左大括號 { 放在同一行// 如果這樣做合適的話MyClass::MyClass(int var) : some_var_(var) {}
9.17. 命名空間格式化
總述
命名空間內(nèi)容不縮進.
說明
命名空間不要增加額外的縮進層次, 例如:
namespace {void foo() { // 正確. 命名空間內(nèi)沒有額外的縮進. ...}} // namespace
不要在命名空間內(nèi)縮進:
namespace { // 錯, 縮進多余了. void foo() { ... }} // namespace
聲明嵌套命名空間時, 每個命名空間都獨立成行.
namespace foo {namespace bar {
9.19. 水平留白
總述
水平留白的使用根據(jù)在代碼中的位置決定. 永遠不要在行尾添加沒意義的留白.
說明
通用
void f(bool b) { // 左大括號前總是有空格. ...int i = 0; // 分號前不加空格.// 列表初始化中大括號內(nèi)的空格是可選的.// 如果加了空格, 那么兩邊都要加上.int x[] = { 0 };int x[] = {0};// 繼承與初始化列表中的冒號前后恒有空格.class Foo : public Bar { public: // 對于單行函數(shù)的實現(xiàn), 在大括號內(nèi)加上空格 // 然后是函數(shù)實現(xiàn) Foo(int b) : Bar(), baz_(b) {} // 大括號里面是空的話, 不加空格. void Reset() { baz_ = 0; } // 用括號把大括號與實現(xiàn)分開. ...
添加冗余的留白會給其他人編輯時造成額外負擔(dān). 因此, 行尾不要留空格. 如果確定一行代碼已經(jīng)修改完畢, 將多余的空格去掉; 或者在專門清理空格時去掉(尤其是在沒有其他人在處理這件事的時候). (Yang.Y 注: 現(xiàn)在大部分代碼編輯器稍加設(shè)置后, 都支持自動刪除行首/行尾空格, 如果不支持, 考慮換一款編輯器或 IDE)
循環(huán)和條件語句
if (b) { // if 條件語句和循環(huán)語句關(guān)鍵字后均有空格.} else { // else 前后有空格.}while (test) {} // 圓括號內(nèi)部不緊鄰空格.switch (i) {for (int i = 0; i < 5; ++i) {switch ( i ) { // 循環(huán)和條件語句的圓括號里可以與空格緊鄰.if ( test ) { // 圓括號, 但這很少見. 總之要一致.for ( int i = 0; i < 5; ++i ) {for ( ; i < 5 ; ++i) { // 循環(huán)里內(nèi) ; 后恒有空格, ; ?前可以加個空格.switch (i) { case 1: // switch case 的冒號前無空格. ... case 2: break; // 如果冒號有代碼, 加個空格.
操作符
// 賦值運算符前后總是有空格.x = 0;// 其它二元操作符也前后恒有空格, 不過對于表達式的子式可以不加空格.// 圓括號內(nèi)部沒有緊鄰空格.v = w * x + y / z;v = w*x + y/z;v = w * (x + z);// 在參數(shù)和一元操作符之間不加空格.x = -5;++x;if (x && !y) ...
模板和轉(zhuǎn)換
// 尖括號(< and >) 不與空格緊鄰, < 前沒有空格, > 和 ( 之間也沒有.vector
9.19. 垂直留白
總述
垂直留白越少越好.
說明
這不僅僅是規(guī)則而是原則問題了: 不在萬不得已, 不要使用空行. 尤其是: 兩個函數(shù)定義之間的空行不要超過 2 行, 函數(shù)體首尾不要留空行, 函數(shù)體中也不要隨意添加空行.
基本原則是: 同一屏可以顯示的代碼越多, 越容易理解程序的控制流. 當(dāng)然, 過于密集的代碼塊和過于疏松的代碼塊同樣難看, 這取決于你的判斷. 但通常是垂直留白越少越好.
下面的規(guī)則可以讓加入的空行更有效:
函數(shù)體內(nèi)開頭或結(jié)尾的空行可讀性微乎其微.
在多重 if-else 塊里加空行或許有點可讀性.
譯者 (YuleFox) 筆記
對于代碼格式, 因人, 系統(tǒng)而異各有優(yōu)缺點, 但同一個項目中遵循同一標(biāo)準(zhǔn)還是有必要的;
行寬原則上不超過 80 列, 把 22 寸的顯示屏都占完, 怎么也說不過去;
盡量不使用非 ASCII 字符, 如果使用的話, 參考 UTF-8 格式 (尤其是 UNIX/Linux 下, Windows 下可以考慮寬字符), 盡量不將字符串常量耦合到代碼中, 比如獨立出資源文件, 這不僅僅是風(fēng)格問題了;
UNIX/Linux 下無條件使用空格, MSVC 的話使用 Tab 也無可厚非;
函數(shù)參數(shù), 邏輯條件, 初始化列表: 要么所有參數(shù)和函數(shù)名放在同一行, 要么所有參數(shù)并排分行;
除函數(shù)定義的左大括號可以置于行首外, 包括函數(shù)/類/結(jié)構(gòu)體/枚舉聲明, 各種語句的左大括號置于行尾, 所有右大括號獨立成行;
./->操作符前后不留空格,*/&不要前后都留, 一個就可, 靠左靠右依各人喜好;
預(yù)處理指令/命名空間不使用額外縮進, 類/結(jié)構(gòu)體/枚舉/函數(shù)/語句使用縮進;
初始化用=還是()依個人喜好, 統(tǒng)一就好;
return不要加();
水平/垂直留白不要濫用, 怎么易讀怎么來.
關(guān)于 UNIX/Linux 風(fēng)格為什么要把左大括號置于行尾 (.cc文件的函數(shù)實現(xiàn)處, 左大括號位于行首), 我的理解是代碼看上去比較簡約, 想想行首除了函數(shù)體被一對大括號封在一起之外, 只有右大括號的代碼看上去確實也舒服; Windows 風(fēng)格將左大括號置于行首的優(yōu)點是匹配情況一目了然.
譯者(acgtyrant)筆記
80 行限制事實上有助于避免代碼可讀性失控, 比如超多重嵌套塊, 超多重函數(shù)調(diào)用等等.
Linux 上設(shè)置好了 Locale 就幾乎一勞永逸設(shè)置好所有開發(fā)環(huán)境的編碼, 不像奇葩的 Windows.
Google 強調(diào)有一對 if-else 時, 不論有沒有嵌套, 都要有大括號. Apple 正好有栽過跟頭.
其實我主張指針/地址操作符與變量名緊鄰,int*a,bvsint*a,b, 新手會誤以為前者的b是int*變量, 但后者就不一樣了, 高下立判.
在這風(fēng)格指南里我才剛知道 C++ 原來還有所謂的Alternative operator representations, 大概沒人用吧.
注意構(gòu)造函數(shù)初始值列表(Constructer Initializer List)與列表初始化(Initializer List)是兩碼事, 我就差點混淆了它們的翻譯.
事實上, 如果您熟悉英語本身的書寫規(guī)則, 就會發(fā)現(xiàn)該風(fēng)格指南在格式上的規(guī)定與英語語法相當(dāng)一脈相承. 比如普通標(biāo)點符號和單詞后面還有文本的話, 總會留一個空格; 特殊符號與單詞之間就不用留了, 比如if(true)中的圓括號與true.
本風(fēng)格指南沒有明確規(guī)定 void 函數(shù)里要不要用 return 語句, 不過就 Google 開源項目 leveldb 并沒有寫; 此外從Is a blank return statement at the end of a function whos return type is void necessary?來看,return;比return;更約定俗成(事實上 cpplint 會對后者報錯, 指出分號前有多余的空格), 且可用來提前跳出函數(shù)棧.
10. 規(guī)則特例
前面說明的編程習(xí)慣基本都是強制性的. 但所有優(yōu)秀的規(guī)則都允許例外, 這里就是探討這些特例.
10.1. 現(xiàn)有不合規(guī)范的代碼
總述
對于現(xiàn)有不符合既定編程風(fēng)格的代碼可以網(wǎng)開一面.
說明
當(dāng)你修改使用其他風(fēng)格的代碼時, 為了與代碼原有風(fēng)格保持一致可以不使用本指南約定. 如果不放心, 可以與代碼原作者或現(xiàn)在的負責(zé)人員商討. 記住,一致性也包括原有的一致性.
10.2. Windows 代碼
總述
Windows 程序員有自己的編程習(xí)慣, 主要源于 Windows 頭文件和其它 Microsoft 代碼. 我們希望任何人都可以順利讀懂你的代碼, 所以針對所有平臺的 C++ 編程只給出一個單獨的指南.
說明
如果你習(xí)慣使用 Windows 編碼風(fēng)格, 這兒有必要重申一下某些你可能會忘記的指南:
不要使用匈牙利命名法 (比如把整型變量命名成iNum). 使用 Google 命名約定, 包括對源文件使用.cc擴展名.
Windows 定義了很多原生類型的同義詞 (YuleFox 注: 這一點, 我也很反感), 如DWORD,HANDLE等等. 在調(diào)用 Windows API 時這是完全可以接受甚至鼓勵的. 即使如此, 還是盡量使用原有的 C++ 類型, 例如使用constTCHAR*而不是LPCTSTR.
使用 Microsoft Visual C++ 進行編譯時, 將警告級別設(shè)置為 3 或更高, 并將所有警告(warnings)當(dāng)作錯誤(errors)處理.
不要使用#pragmaonce; 而應(yīng)該使用 Google 的頭文件保護規(guī)則. 頭文件保護的路徑應(yīng)該相對于項目根目錄 (Yang.Y 注: 如#ifndefSRC_DIR_BAR_H_, 參考#define 保護一節(jié)).
除非萬不得已, 不要使用任何非標(biāo)準(zhǔn)的擴展, 如#pragma和__declspec. 使用__declspec(dllimport)和__declspec(dllexport)是允許的, 但必須通過宏來使用, 比如DLLIMPORT和DLLEXPORT, 這樣其他人在分享使用這些代碼時可以很容易地禁用這些擴展.
然而, 在 Windows 上仍然有一些我們偶爾需要違反的規(guī)則:
通常我們禁止使用多重繼承, 但在使用 COM 和 ATL/WTL 類時可以使用多重繼承. 為了實現(xiàn) COM 或 ATL/WTL 類/接口, 你可能不得不使用多重實現(xiàn)繼承.
雖然代碼中不應(yīng)該使用異常, 但是在 ATL 和部分 STL(包括 Visual C++ 的 STL) 中異常被廣泛使用. 使用 ATL 時, 應(yīng)定義_ATL_NO_EXCEPTIONS以禁用異常. 你需要研究一下是否能夠禁用 STL 的異常, 如果無法禁用, 可以啟用編譯器異常. (注意這只是為了編譯 STL, 自己的代碼里仍然不應(yīng)當(dāng)包含異常處理).
通常為了利用頭文件預(yù)編譯, 每個每個源文件的開頭都會包含一個名為StdAfx.h或precompile.h的文件. 為了使代碼方便與其他項目共享, 請避免顯式包含此文件 (除了在precompile.cc中), 使用/FI編譯器選項以自動包含該文件.
資源頭文件通常命名為resource.h且只包含宏, 這一文件不需要遵守本風(fēng)格指南.
11. 結(jié)束語
運用常識和判斷力, 并且保持一致.
編輯代碼時, 花點時間看看項目中的其它代碼, 并熟悉其風(fēng)格. 如果其它代碼中if語句使用空格, 那么你也要使用. 如果其中的注釋用星號 (*) 圍成一個盒子狀, 那么你同樣要這么做.
風(fēng)格指南的重點在于提供一個通用的編程規(guī)范, 這樣大家可以把精力集中在實現(xiàn)內(nèi)容而不是表現(xiàn)形式上. 我們展示的是一個總體的的風(fēng)格規(guī)范, 但局部風(fēng)格也很重要, 如果你在一個文件中新加的代碼和原有代碼風(fēng)格相去甚遠, 這就破壞了文件本身的整體美觀, 也讓打亂讀者在閱讀代碼時的節(jié)奏, 所以要盡量避免.
好了, 關(guān)于編碼風(fēng)格寫的夠多了; 代碼本身才更有趣. 盡情享受吧!
-
Google
+關(guān)注
關(guān)注
5文章
1754瀏覽量
57379 -
編程
+關(guān)注
關(guān)注
88文章
3565瀏覽量
93536 -
C++
+關(guān)注
關(guān)注
21文章
2100瀏覽量
73453
原文標(biāo)題:Google C++ 編程規(guī)范 - 6
文章出處:【微信號:C_Expert,微信公眾號:C語言專家集中營】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論