C++编码规范(Google C++ Style Guide)

I 关于编码规范

1.最重要的一点:明确规范,然后保持一致。BE CONSISTENT!

II Header Files 

1.The #define Guard

Guard的命名规则为<PROJECT>_<PATH>_<FILE>_H_。比如foo工程中的文件foo/src/bar/baz.h应定义如下Guard:

#ifndef FOO_BAR_BAZ_H_

#define FOO_BAR_BAZ_H_

...

#endif // FOO_BAR_BAZ_H_

 

2.头文件依赖

在头文件中尽量使用forward declaration替代#include,这样可以减少头文件之间的相互依赖,从而可能使得增量编译时需要重新编译的编译单元减少。

如果要在一个头文件中使用类Foo,则在以下情况下,可以仅使用forward declaration:

1)声明类型为Foo *或Foo &的数据成员。

2)声明参数或返回类型为Foo的函数。

3)声明类型为Foo的静态数据成员。因为静态数据成员是在类定义之外定义的。

如果在源文件(.cpp或.cc)中需要使用符号Foo,应该直接在源文件中使用#include引入Foo的定义,而不是依赖于其它头文件的间接包含。一个例外是,如果Foo在myfile.cc中使用,可以在myfile.h中包含相应头文件。

3.内联函数

只有在函数足够小时才定义内联函数,比如小于10行。

不要把析构函数声明为内联函数,因为析构函数往往比你编写的要长得多,因为编译器会插入相关成员的析构操作。

不要将函数有loop和switch的函数声明的内联函数。

有些函数即使声明为内联函数也不会内联,比如虚函数和递归函数。

4.The -inl.h Files

由于内联函数需要在头文件中实现,这样编译器才能在相应函数被调用的地方插入内联函数实现代码。

对于简单的内联函数,比如accessors和mutators,应该在.h文件中定义。

更复杂的内联函数应该在单独的-inl.h文件中定义。

使用-inl.h文件的另一种情况是函数模板的定义,从而使得你的模板定义更具可读性。

5.函数参数的顺序

C++的函数参数有三种类型:input, output, or both. 规则是将输入参数放在输出参数之前。

通常输入参数应该是值类型或const引用,输出参数是非const指针。

6.Names and Order of Includes

头文件应分组包含,通常以如下顺序包含头文件:类定义头文件,C库,C++库,其它三方库,工程定义头文件

所有工程定义的头文件,应该以工程源码根目录为起点,列出完整路径,绝不应该使用"."和".."来表示以当前目录为基准的相对路径。

例:

#include "foo/public/fooserver.h" // Preferred location.

#include <sys/types.h>

#include <unistd.h>

#include <hash_map>

#include <vector>

#include "base/basictypes.h"

#include "base/commandlineflags.h"

#include "foo/public/bar.h"

 

III Scoping

1.命名空间

命名空间定义结束时应使用注释标明对应的命名空间。

1.1 Unnamed namespaces

鼓励在.cc文件中使用Unnamed namespaces,这样可以防止运行时命名冲突。通常将局部于.cc文件的一些helper函数放在Unnamed namespace中。

绝不在.h文件中使用Unnamed namespace.

namespace { // This is in a .cc file.

// The content of a namespace is not indented

enum { kUnused, kEOF, kError }; // Commonly used tokens.

bool AtEof() { return pos_ == kEOF; } // Uses our namespace's EOF.

} // namespace

 

1.2 Named namespaces

在头文件和forward declarations之后,命名空间包含整个源文件。

绝不使用using指令导入整个命名空间,以免造成命名空间污染。可以使用using导入单个符号。

using指令可以出现在.cc文件中的任何位置,以及头文件的函数、方法和类中。

1.3 Namespace alias

命名空间别名可以出现在.cc文件中的任何位置,头文件中包含整个头文件的命名空间内,以及头文件中的函数和方法中。

如果在.h中使用了命名空间别名,则包含此头文件的文件也可以见到此别名,因此在头文件中使用命名空间别名应慎重。

// Shorten access to some commonly used names in .cc files.

namespace fbz = ::foo::bar::baz;

// Shorten access to some commonly used names (in a .h file).

namespace librarian {

// The following alias is available to all files including

// this header (in namespace librarian):

// alias names should therefore be chosen consistently

// within a project.

namespace pd_s = ::pipeline_diagnostics::sidetable;

inline void my_inline_function() {

// namespace alias local to a function (or method).

namespace fbz = ::foo::bar::baz;

...

}

} // namespace librarian

 

2.嵌套类

不要使用public nested class,除非你确定它是接口的一部分。这种情况下直接定义这个类(不定义为嵌套类)通常更直观。更进一步,可以使用命令空间来达到将这个类和全局作用域隔离的目的。

3.Nonmember, Static Member, and Global Functions

有一些函数不属于任何类或者不适合作为某个类的成员,则最好的处理方式是将其定义为非成员函数,并声明一个命名空间来管理这些函数。不要仅为了组织一组函数而定义一个类。

如果这些函数只在当前.cc文件中使用,则可以使用unnamed namespace或者声明为static linkage。

如果这些函数共享某些静态数据,则应该将它们定义为一个新的类的静态成员函数。

尽量不使用全局函数。

4.局部变量

让局部变量的作用域尽可能小,并且在定义的时候初始化,而不是先定义再赋值

应该在使用变量之前才定义变量,不使用以前C风格的在函数一开始定义所有局部变量的方式。

5.静态变量和全局变量

绝不使用类类型的静态变量或全局变量,因为对象创建和销毁顺序的不确定性往往造成难以发现的bug

具有静态存储 (static storage duration)的对象,包括静态变量、全局变量、类的静态成员变量和函数内的静态变量,都应该是“平凡数据对象 (POD, Plain Old Data)”:ints, chars, floats, 指针或者以 POD为元素的arrays/structs 。

C++规范没有完备地说明静态成员初始化式的调用顺序,因此不要用函数调用来初始化静态POD变量,除非调用的函数不依赖于其它全局的东西。

对象的销毁顺序与创建顺序相反,因为创建顺序不确定,所以销毁的顺序也是不确定的(例如,在程序结束时,一个静态变量可能已经被销毁了,但是代码依然在执行(可能在另一个线程中)并且试图访问这个静态变量)。结果是静态变量只允许为POD,这一规则完全排除了将vector(使用C风格数组)和string(使用const char[])作为静态变量使用。

如果一定要将一个类对象作为静态或全局的,应该使用指针(不会被释放),然后在main函数初始化(或者使用pthread_once()初始化)。要注意这个指针应该是“平凡”的指针(raw pointer),而不是智能指针(smart pointer)。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/516c9d2cf5741910a363157b2394f0db.html