Creative Commons License
本Blog采用 知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议 进行许可。 —— Fox <游戏人生>

游戏人生

游戏人生 != ( 人生 == 游戏 )
站点迁移至:http://www.yulefox.com。请订阅本博的朋友将RSS修改为http://feeds.feedburner.com/yulefox
posts - 62, comments - 508, trackbacks - 0, articles - 7

[译]Google C++编程风格指南(七)

Posted on 2008-07-23 11:43 Fox 阅读(4378) 评论(7)  编辑 收藏 引用 所属分类: T技术碎语

原文地址:

  • 格式

代码风格和格式确实比较随意,但一个项目中所有人遵循同一风格是非常容易的,作为个人未必同意下述格式规则的每一处,但整个项目服从统一的编程风格是很重要的,这样做才能让所有人在阅读和理解代码时更加容易。

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转为空格。

译者注:在前段时间的关于Debian开发学习日记一文中,曾给出针对C/C++编码使用的vim配置。

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
    DropEverything();
    #endif                // Wrong!  Do not indent "#endif"
    BackToNormal();
  }

13. 类格式(Class Format)

声明属性依次序是public:protected:private:,每次缩进1个空格(译者注,为什么不是两个呢?也有人提倡private在前,对于声明了哪些数据成员一目了然,还有人提倡依逻辑关系将变量与操作放在一起,都有道理:-)

类声明(对类注释不了解的话,参考第六篇中的类注释一节)的基本格式如下:

class MyClass : public OtherClass {
 public:      // Note the 1 space indent!
  MyClass();  // Regular 2 space indent.
  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_;
  DISALLOW_COPY_AND_ASSIGN(MyClass);
};

注意:

1) 所以基类名应在80列限制下尽量与子类名放在同一行;

2) 关键词public:、protected:private:要缩进1个空格(译者注,MSVC多使用tab缩进,且这三个关键词没有缩进)

3) 除第一个关键词(一般是public)外,其他关键词前空一行,如果类比较小的话也可以不空;

4) 这些关键词后不要空行;

5) public放在最前面,然后是protectedprivate

6) 关于声明次序参考第三篇声明次序一节。

14. 初始化列表(Initializer Lists)

构造函数初始化列表放在同一行或按四格缩进并排几行。

两种可以接受的初始化列表格式:

// When it all fits on one line:
MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1) {

// When it requires multiple lines, indent 4 spaces, putting the colon on
// the first initializer line:
MyClass::MyClass(int var)
    : some_var_(var),             // 4 space indent
      some_other_var_(var + 1) {  // lined up
  ...
  DoSomething();
  ...
}

15. 命名空间格式化(Namespace Formatting)

命名空间内容不缩进。

命名空间不添加额外缩进层次,例如:

namespace {

void foo() {  // Correct.  No extra indentation within namespace.
  ...
}

}  // namespace

不要缩进:

namespace {

  // Wrong.  Indented when it should not be.
  void foo() {
    ...
  }

}  // namespace

16. 水平留白(Horizontal Whitespace)

水平留白的使用因地制宜。不要在行尾添加无谓的留白。

普通

void f(bool b) {  // Open braces should always have a space before them.
  ...
int i = 0;  // Semicolons usually have no space before them.
int x[] = { 0 };  // Spaces inside braces for array initialization are
int x[] = {0};    // optional.  If you use them, put them on both sides!
// Spaces around the colon in inheritance and initializer lists.
class Foo : public Bar {
 public:
  // For inline function implementations, put spaces between the braces
  // and the implementation itself.
  Foo(int b) : Bar(), baz_(b) {}  // No spaces inside empty braces.
  void Reset() { baz_ = 0; }  // Spaces separating braces from implementation.
  ...

添加冗余的留白会给其他人编辑时造成额外负担,因此,不要加入多余的空格。如果确定一行代码已经修改完毕,将多余的空格去掉;或者在专门清理空格时去掉(确信没有其他人在使用)。

循环和条件语句

if (b) {          // Space after the keyword in conditions and loops.
} else {          // Spaces around else.
}
while (test) {}   // There is usually no space inside parentheses.
switch (i) {
for (int i = 0; i < 5; ++i) {
switch ( i ) {    // Loops and conditions may have spaces inside
if ( test ) {     // parentheses, but this is rare.  Be consistent.
for ( int i = 0; i < 5; ++i ) {
for ( ; i < 5 ; ++i) {  // For loops always have a space after the
  ...                   // semicolon, and may have a space before the
                        // semicolon.
switch (i) {
  case 1:         // No space before colon in a switch case.
    ...
  case 2: break;  // Use a space after a colon if there's code after it.

操作符

x = 0;              // Assignment operators always have spaces around
                    // them.
x = -5;             // No spaces separating unary operators and their
++x;                // arguments.
if (x && !y)
  ...
v = w * x + y / z;  // Binary operators usually have spaces around them,
v = w*x + y/z;      // but it's okay to remove spaces around factors.
v = w * (x + z);    // Parentheses should have no spaces inside them.

模板和转换

vector<string> x;           // No spaces inside the angle
y = static_cast<char*>(x);  // brackets (< and >), before
                            // <, or between >( in a cast.
vector<char *> x;           // Spaces between type and pointer are
                            // okay, but be consistent.
set<list<string> > x;       // C++ requires a space in > >.
set< list<string> > x;      // You may optionally make use
                            // symmetric spacing in < <.

17. 垂直留白(Vertical Whitespace)

垂直留白越少越好。

这不仅仅是规则而是原则问题了:不是非常有必要的话就不要使用空行。尤其是:不要在两个函数定义之间空超过2行,函数体头、尾不要有空行,函数体中也不要随意添加空行。

基本原则是:同一屏可以显示越多的代码,程序的控制流就越容易理解。当然,过于密集的代码块和过于疏松的代码块同样难看,取决于你的判断,但通常是越少越好。

函数头、尾不要有空行:

void Function() {

  // Unnecessary blank lines before and after

}

代码块头、尾不要有空行:

while (condition) {
  // Unnecessary blank line after

}
if (condition) {

  // Unnecessary blank line before
}

if-else块之间空一行还可以接受:

if (condition) {
  // Some lines of code too small to move to another function,
  // followed by a blank line.

} else {
  // Another block of code
}

______________________________________

译者:首先说明,对于代码格式,因人、因系统各有优缺点,但同一个项目中遵循同一标准还是有必要的:

1. 行宽原则上不超过80列,把22寸的显示屏都占完,怎么也说不过去;

2. 尽量不使用非ASCII字符,如果使用的话,参考UTF-8格式(尤其是UNIX/Linux下,Windows下可以考虑宽字符),尽量不将字符串常量耦合到代码中,比如独立出资源文件,这不仅仅是风格问题了;

3. UNIX/Linux下无条件使用空格,MSVC的话使用Tab也无可厚非;

4. 函数参数、逻辑条件、初始化列表:要么所有参数和函数名放在同一行,要么所有参数并排分行;

5. 除函数定义的左大括号可以置于行首外,包括函数/类/结构体/枚举声明、各种语句的左大括号置于行尾,所有右大括号独立成行;

6. ./->操作符前后不留空格,*/&不要前后都留,一个就可,靠左靠右依各人喜好;

7. 预处理指令/命名空间不使用额外缩进,类/结构体/枚举/函数/语句使用缩进;

8. 初始化用=还是()依个人喜好,统一就好;

9. return不要加();

10. 水平/垂直留白不要滥用,怎么易读怎么来。

Feedback

# re: [译]Google C++编程风格指南(七)  回复  更多评论   

2008-07-25 10:07 by tangfl
4) 圆括号与参数间没有看过; ???

# re: [译]Google C++编程风格指南(七)  回复  更多评论   

2008-07-25 14:27 by Fox
空格,呵呵,谢谢提醒!:D

# re: [译]Google C++编程风格指南(七)  回复  更多评论   

2010-07-18 04:13 by AvilaNoelle22
It's good that we can get the <a href="http://bestfinance-blog.com/topics/home-loans">home loans</a> moreover, it opens up completely new possibilities.

# re: [译]Google C++编程风格指南(七)  回复  更多评论   

2010-08-02 02:48 by annotated bibliography
That's not easy to deal with several things at the same time, especially when you have to create the essay papers. The only purchase essays service would manage even with complicated tasks.

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理