Google C++ 代码规范学习笔记

今天来抽空来学习一下 Google 的 C++ 代码规范,有关于这个规范的文件来自于下面这个网址

参考链接:

Google C++ Code Style

C++ 参考手册

从第一个网站里面学到了一些对自己代码格式等方面规范有帮助的东西,所以来总结一下,同时这个网站里的内容很多,有大量些项目的指导,由于水平有限就只摘了我认为有用的一部分内容。

内联函数

注意的是10行以下的代码才被允许使用内联函数,如果一个函数超过了10行,那么:

滥用内联将导致程序变得更慢. 内联可能使目标代码量或增或减, 这取决于内联函数的大小. 内联非常短小的存取函数通常会减少代码大小, 但内联一个相当大的函数将戏剧性的增加代码大小. 现代处理器由于更好的利用了指令缓存, 小巧的代码往往执行更快。

所以我们得出一个结论:
  • 不要内联超过 10 行的函数

  • 联那些包含循环 switch 语句的函数常常是得不偿失 (除非在大多数情况下, 这些循环或 switch 语句从不被执行).

#include<>的路径和顺序

包含的顺序为:

  • C 系统文件

  • C++ 系统文件

  • 其他库的 .h 文件

  • 本项目内 .h 文件

关于局部变量的定义问题

主要遵循这个原则:

将函数变量尽可能置于最小作用域内, 并在变量声明时进行初始化.

我们提倡在尽可能小的作用域中声明变量, 离第一次使用越近越好. 这使得代码浏览者更容易定位变量声明的位置, 了解变量的类型和初始值. 特别是,应使用初始化的方式替代声明再赋值, 比如:

1
int j = g();	// 好——初始化时声明
1
2
int i;
i = f(); // 坏——初始化和声明分离

属于 if, while 和 for 语句的变量应当在这些语句中正常地声明,这样子这些变量的作用域就被限制在这些语句中了

还有一点需要注意的是再循环里的变量的定义,如果需要定义一个对象,那么我们一般在循环体外定义,来提高循环的效率。

关于类型强制转换的使用说明

关于变量类型的强制转换,我们一般有两种方法:
一个是 C 标准的类型转换,还有一个就是 C++ 建议的类型转换。

1
int y = (int)x	// C 标准的类型强制转换
1
static_cast< new_type >( expression )	// C++ 建议的类型强制转换,返回一个新类型的值。

关于输入输出

关于输入输出我们还是有两种选择,一种为 C 的 printf() 和 scanf(),另一种为 C++ 的 cin cout 流输入输出。关于这个问题,我们一般选择 C 的输入输出,
其原因是 C++ 的流输入输出不会判别输出的类型,从而发生一些不可预知的错误。
所以,我们使用 printf+read/write 的形式进行 I/O

关于前置自增和后置自增(i++)/(++i)的问题

有关于这一问题,Google 给出了这样的解释:

不考虑返回值的话, 前置自增 (++i) 通常要比后置自增 (i++) 效率更高. 因为后置自增 (或自减) 需要对表达式的值 i 进行一次拷贝. 如果 i 是迭代器或其他非数值类型, 拷贝的代价是比较大的. 既然两种自增方式实现的功能一样, 为什么不总是使用前置自增呢?

所以,我们得到结论:

1
对简单数值 (非对象), 两种都无所谓. 对迭代器和模板类型, 使用前置自增 (自减)。即在循环中的循环变量统一使用前置自增。

命名规则

文件命名

可以接受字母,下划线

普通变量命名

只可以接受字母,下划线,数字,不接受大写字母。

常量命名

以大写字母K开头

注释说明

注释应在每行的开头,或者应该在行尾空两格进行注释。

代码格式

终于来了:

  • 行长度

    建议不超过80个字符,注释可以超过。

  • 变量名与&符号不加空格
  • if 语句中条件括号内不加空格
1
2
3
if (condition) {

}

注意所有情况下 if 和左圆括号间都有个空格. 右圆括号和左大括号之间也要有个空格:

1
2
3
if(condition)     // 差 - IF 后面没空格.
if (condition){ // 差 - { 前面没空格.
if(condition){ // 变本加厉地差.

如果能增强可读性, 简短的条件语句允许写在同一行. 只有当语句简单并且没有使用 else 子句时使用:

1
2
if (x == kFoo) return new Foo();
if (x == kBar) return new Bar();

如果语句中某个 if-else 分支使用了大括号的话, 其它分支也必须使用:

1
2
3
4
5
6
7
8
9
10
11
12
// 不可以这样子 - IF 有大括号 ELSE 却没有.
if (condition) {
foo;
} else
bar;

// 不可以这样子 - ELSE 有大括号 IF 却没有.
if (condition)
foo;
else {
bar;
}

1
2
3
4
5
6
// 只要其中一个分支用了大括号, 两个分支都要用上大括号.
if (condition) {
foo;
} else {
bar;
}

如果一个布尔表达式超过 标准行宽, 断行方式要统一一下.

下例中, 逻辑与 (&&) 操作符总位于行尾:

1
2
3
4
5
if (this_one_thing > this_other_thing &&
a_third_thing == a_fourth_thing &&
yet_another && last_one) {
...
}

函数的返回值

不要在 return 表达式里加上非必须的圆括号:

1
2
3
4
5
6
return result;                  // 返回值很简单, 没有圆括号.
// 可以用圆括号把复杂表达式圈起来, 改善可读性.
return (some_long_condition &&
another_condition);
return (value); // 毕竟您从来不会写 var = (value);
return(result); // return 可不是函数!

留白

通用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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; } // 用括号把大括号与实现分开.

循环和条件语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (b) {          // if 条件语句和循环语句关键字后均有空格.
} else { // else 前后有空格.
}
while (test) {} // 圆括号内部不紧邻空格.
switch (i) {
for (int i = 0; i < 5; ++i) {
switch ( i ) { // 循环和条件语句的圆括号里可以与空格紧邻.
if ( test ) { // 圆括号, 但这很少见. 总之要一致.
for ( int i = 0; i < 5; ++i ) {
for ( ; i < 5 ; ++i) { // 循环里内 ; 后恒有空格, ; 前可以加个空格.
switch (i) {
case 1: // switch case 的冒号前无空格.
...
case 2: break; // 如果冒号有代码, 加个空格.

操作符:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 赋值运算符前后总是有空格.
x = 0;

// 其它二元操作符也前后恒有空格, 不过对于表达式的子式可以不加空格.
// 圆括号内部没有紧邻空格.
v = w * x + y / z;
v = w*x + y/z;
v = w * (x + z);

// 在参数和一元操作符之间不加空格.
x = -5;
++x;
if (x && !y)