1.行寬原則上不超過80列,把22寸的顯示屏都占完,怎麼也說不過去;2.盡量不使用非ASCII字符;3.UNIX/Linux下無條件使用空格,MSVC的話使用Tab也無可厚非;4.函數參數、邏輯條件、初始化列表:要麼所有參數和函數名放在同一行,要麼所有參數並排分行……
格式
代碼風格和格式確實比較隨意,但一個項目中所有人遵循同一風格是非常容易的,作為個人未必同意下述格式規則的每一處,但整個項目服從統一的編程風格是很重要的,這樣做才能讓所有人在閱讀和理解代碼時更加容易。
1.行長度(Line Length)
每一行代碼字符數不超過80。
我們也認識到這條規則是存有爭議的,但如此多的代碼都遵照這一規則,我們感覺一致性更重要。
優點:提倡該原則的人認為強迫他們調整編輯器窗口大小很野蠻。很多人同時並排開幾個窗口,根本沒有多余空間拓寬某個窗口,人們將窗口最大尺寸加以限定,一致使用80列寬,為什麼要改變呢?
缺點:反對該原則的人則認為更寬的代碼行更易閱讀,80列的限制是上個世紀60年代的大型機的古板缺陷;現代設備具有更寬的顯示屏,很輕松的可以顯示更多代碼。
結論:80個字符是最大值。例外:
1) 如果一行注釋包含了超過80字符的命令或URL,出於復制粘貼的方便可以超過80字符;
2) 包含長路徑的可以超出80列,盡量避免;
3) 頭文件保護(防止重復包含第一篇)可以無視該原則。
2.非ASCII字符(Non-ASCII Characters)
盡量不使用非ASCII字符,使用時必須使用UTF-8格式。
哪怕是英文,也不應將用戶界面的文本硬編碼到源代碼中,因此非ASCII字符要少用。特殊情況下可以適當包含此類字符,如,代碼分析外部數據文件時,可以適當硬編碼數據文件中作為分隔符的非ASCII字符串;更常用的是(不需要本地化的)單元測試代碼可能包含非ASCII字符串。此類情況下,應使用UTF-8格式,因為很多工具都可以理解和處理其編碼,十六進制編碼也可以,尤其是在增強可讀性的情況下——如"\xEF\xBB\xBF"是Unicode的zero-width no-break space字符,以UTF-8格式包含在源文件中是不可見的。
3.空格還是制表位(Spaces vs.Tabs)
只使用空格,每次縮進2個空格。
使用空格進行縮進,不要在代碼中使用tabs,設定編輯器將tab轉為空格。
4.函數聲明與定義(Function Declarations and Definitions)
返回類型和函數名在同一行,合適的話,參數也放在同一行。
函數看上去像這樣:
ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {
DoSomething();
...
}
如果同一行文本較多,容不下所有參數:
ReturnType ClassName::ReallyLongFunctionName(Type par_name1,
Type par_name2,
Type par_name3) {
DoSomething();
...
}
甚至連第一個參數都放不下:
ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
Type par_name1, // 4 space indent
Type par_name2,
Type par_name3) {
DoSomething(); // 2 space indent
...
}
注意以下幾點:
1) 返回值總是和函數名在同一行;
2) 左圓括號(open parenthesis)總是和函數名在同一行;
3) 函數名和左圓括號間沒有空格;
4) 圓括號與參數間沒有空格;
5) 左大括號(open curly brace)總在最後一個參數同一行的末尾處;
6) 右大括號(close curly brace)總是單獨位於函數最後一行;
7) 右圓括號(close parenthesis)和左大括號間總是有一個空格;
8) 函數聲明和實現處的所有形參名稱必須保持一致;
9) 所有形參應盡可能對齊;
10) 缺省縮進為2個空格;
11) 獨立封裝的參數保持4個空格的縮進。
如果函數為const的,關鍵字const應與最後一個參數位於同一行。
// Everything in this function signature fits on a single line
ReturnType FunctionName(Type par) const {
...
}
// This function signature requires multiple lines, but
// the const keyword is on the line with the last parameter.
ReturnType ReallyLongFunctionName(Type par1,
Type par2) const {
...
}
如果有些參數沒有用到,在函數定義處將參數名注釋起來:
// Always have named parameters in interfaces.
class Shape {
public:
virtual void Rotate(double radians) = 0;
}
// Always have named parameters in the declaration.
class Circle : public Shape {
public:
virtual void Rotate(double radians);
}
// Comment out unused named parameters in definitions.
void Circle::Rotate(double /*radians*/) {} // Bad - if someone wants to implement later, it's not clear what the
// variable means.
void Circle::Rotate(double) {}
譯者注:關於UNIX/Linux風格為什麼要把左大括號置於行尾(.cc文件的函數實現處,左大括號位於行首),我的理解是代碼看上去比較簡約,想想行首除了函數體被一對大括號封在一起之外,只有右大括號的代碼看上去確實也舒服;Windows風格將左大括號置於行首的優點是匹配情況一目了然。
5.函數調用(Function Calls)
盡量放在同一行,否則,將實參封裝在圓括號中。
函數調用遵循如下形式:
bool retval = DoSomething(argument1, argument2, argument3);
如果同一行放不下,可斷為多行,後面每一行都和第一個實參對齊,左圓括號後和右圓括號前不要留空格:
bool retval = DoSomething(averyveryveryverylongargument1,
argument2, argument3);
如果函數參數比較多,可以出於可讀性的考慮每行只放一個參數:
bool retval = DoSomething(argument1,
argument2,
argument3,
argument4);
如果函數名太長,以至於超過行最大長度,可以將所有參數獨立成行:
if (...) {
...
...
if (...) {
DoSomethingThatRequiresALongFunctionName(
very_long_argument1, // 4 space indent
argument2,
argument3,
argument4);
}
6.條件語句(Conditionals)
更提倡不在圓括號中添加空格,關鍵字else另起一行。
對基本條件語句有兩種可以接受的格式,一種在圓括號和條件之間有空格,一種沒有。
最常見的是沒有空格的格式,那種都可以,還是一致性為主。如果你是在修改一個文件,參考當前已有格式;如果是寫新的代碼,參考目錄下或項目中其他文件的格式,還在徘徊的話,就不要加空格了。
if (condition) { // no spaces inside parentheses
...// 2 space indent.
} else { // The else goes on the same line as the closing brace.
...
}
如果你傾向於在圓括號內部加空格:
if ( condition ) { // spaces inside parentheses - rare
...// 2 space indent.
} else { // The else goes on the same line as the closing brace.
...
}
注意所有情況下if和左圓括號間有個空格,右圓括號和左大括號(如果使用的話)間也要有個空格:
if(condition) // Bad - space missing after IF.
if (condition){ // Bad - space missing before {.
if(condition){ // Doubly bad.if (condition) { // Good - proper space after IF and before {.
有些條件語句寫在同一行以增強可讀性,只有當語句簡單並且沒有使用else子句時使用:
if (x == kFoo) return new Foo();
if (x == kBar) return new Bar();
如果語句有else分支是不允許的:
// Not allowed - IF statement on one line when there is an ELSE clause
if (x) DoThis();
else DoThat();
通常,單行語句不需要使用大括號,如果你喜歡也無可厚非,也有人要求if必須使用大括號:
if (condition)
DoSomething(); // 2 space indent.
if (condition) {
DoSomething(); // 2 space indent.
}
但如果語句中哪一分支使用了大括號的話,其他部分也必須使用:
// Not allowed - curly on IF but not ELSE
if (condition) {
foo;
} else
bar;
// Not allowed - curly on ELSE but not IF
if (condition)
foo;
else {
bar;
} // Curly braces around both IF and ELSE required because
// one of the clauses used braces.
if (condition) {
foo;
} else {
bar;
}
7.循環和開關選擇語句(Loops and Switch Statements)
switch語句可以使用大括號分塊;空循環體應使用{}或continue。
switch語句中的case塊可以使用大括號也可以不用,取決於你的喜好,使用時要依下文所述。
如果有不滿足case枚舉條件的值,要總是包含一個default(如果有輸入值沒有case去處理,編譯器將報警)。如果default永不會執行,可以簡單的使用assert:
switch (var) {
case 0: { // 2 space indent
...// 4 space indent
break;
}
case 1: {
...
break;
}
default: {
assert(false);
}
}
空循環體應使用{}或continue,而不是一個簡單的分號:
while (condition) {
// Repeat test until it returns false.
}
for (int i = 0; i < kSomeNumber; ++i) {} // Good - empty body.
while (condition) continue; // Good - continue indicates no logic.while (condition); // Bad - looks like part of do/while loop.
8.指針和引用表達式(Pointers and Reference Expressions)
句點(.)或箭頭(->)前後不要有空格,指針/地址操作符(*、&)後不要有空格。
下面是指針和引用表達式的正確范例:
x = *p;
p = &x;
x = r.y;
x = r->y;
注意:
1) 在訪問成員時,句點或箭頭前後沒有空格;
2) 指針操作符*或&後沒有空格。
在聲明指針變量或參數時,星號與類型或變量名緊挨都可以:
// These are fine, space preceding.
char *c;
const string &str;
// These are fine, space following.
char* c; // but remember to do "char* c, *d, *e, ...;"!
const string& str; char * c; // Bad - spaces on both sides of *
const string & str; // Bad - spaces on both sides of &
同一個文件(新建或現有)中起碼要保持一致。
譯者注:個人比較習慣與變量緊挨的方式。
9.布爾表達式(Boolean Expressions)
如果一個布爾表達式超過標准行寬(80字符),如果斷行要統一一下。
下例中,邏輯與(&&)操作符總位於行尾:
if (this_one_thing > this_other_thing &&
a_third_thing == a_fourth_thing &&
yet_another & last_one) {
...
}
兩個邏輯與(&&)操作符都位於行尾,可以考慮額外插入圓括號,合理使用的話對增強可讀性是很有幫助的。
譯者注:個人比較習慣邏輯運算符位於行首,邏輯關系一目了然,各人喜好而已,至於加不加圓括號的問題,如果你對優先級了然於胸的話可以不加,但可讀性總是差了些。
10.函數返回值(Return Values)
return表達式中不要使用圓括號。
函數返回時不要使用圓括號:
return x; // not return(x);
11.變量及數組初始化(Variable and Array Initialization)
選擇=還是()。
需要做二者之間做出選擇,下面的形式都是正確的:
int x = 3;
int x(3);
string name("Some Name");
string name = "Some Name";
12.預處理指令(Preprocessor Directives)
預處理指令不要縮進,從行首開始。
即使預處理指令位於縮進代碼塊中,指令也應從行首開始。
// Good - directives at beginning of line
if (lopsided_score) {
#if DISASTER_PENDING // Correct -- Starts at beginning of line
DropEverything();
#endif
BackToNormal();
} // Bad - indented directives
if (lopsided_score) {
#if DISASTER_PENDING // Wrong! The "#if" should be at beginning of line