本文移植自个人的原博客(原文

C/C++代码规范。包括命名规则,排版规则,文档及注释,编码要求等等。

总结

C/C++ Google Coding Style

命名规则

起个合适的名字

类的名称

类的名称要能告诉我们,这个类是什么。因此,类的名称通常是名词。

类的名字不需要告诉我们,它从哪个类继承而来的。

有时候加个后缀是很有用的。比如类是一个代理(Agents)时,起名叫DownloadAgent更能表达真实的意图。

方法和函数的名称

方法和函数通常都要执行某种行为,因此,名称要能清楚的说明它做什么:CheckForErrors() 而不是ErrorCheck(),DumpDataToFile() 而不是 DataFile()。 这样也可以很容易的区别函数和数据。

函数名总以动词开头,后面跟随其它名称。这样看起来更自然些。

可以加一些必要的后缀:

Max – 表示取最大值。

Cnt – 表示当前的计数值。

Key – 表示键值。

例如:RetryMax 表示可接收的最大数,RetryCnt表示当前接收的数量。

前缀也同样有用:

Is – 用于询问一些问题。只要看到Is开头,就知道这是一个查询。

Get – 用于获取一个值。

Set – 用于设置一个值。

例如:IsHitRetryLimit。

含有度量单位的名称

如果一个变量用于表示时间,重量或其它度量单位,应把度量单位添加到名称中,以便开发人员更早一步发现问题。

例如:

uint32 mTimeoutMsecs;
uint32 mMyWeightLbs;

缩写名称不要全部大写

无论是什么缩写名称,我们总以一个大写字母开头,后面跟随的字母全部用小写。

例如:

class FluidOz;            // 而不是 FluidOZ
class NetworkAbcKey; // 而不是 NetworkABCKey

类的命名

用大写字母作为单词的分隔,每个单词的首字母大写,其它字母均小写。

名字的第一个字母应大写。

不含有下划线。

例如:

class NameOneTwo; 
class Name;

类库或程序库命名

使用命名空间防止名字冲突。

如果编译器没有实现命名空间,需要用前缀来避名名字冲突,不过前缀不要过长(2个字母比较好)。

例如:(Tong Su 完成了一个数据结构的库,它可以使用JJ作为库的前缀,所以类名就象下面这样)

class TsLinkList{

}

方法和函数的命名

使用与类名相同的规则。

例如:

class NameOneTwo{
public:
int DoIt();
void HandleError();
}

类属性的命名

属性(通常是非公有数据成员)名字以字母’m’开头。

在 ‘m(m_)’ 后面,使用与类名相同的规则。

‘m(m_)’ 总是位于其它修饰符(如表示指针的 ‘p’)的前面。

例如:

class NameOneTwo{
public:
int VarAbc();
int ErrorNumber();
private:
int mVarAbc;
int mErrorNumber;
String* mpName;
}

方法和函数参数的命名

第一个字母必须小写。

第一个字母后面的单词使用与类名相同的规则。

例如:

class NameOneTwo{
public:
int StartYourEngines(Engine&rSomeEngine,Engine&rAnotherEngine);
}

局部变量的命名

所有字母都用小写。

使用下划线作为单词的分隔。

例如:

int NameOneTwo::HandleError(int errorNumber){
int error= OsErr();
Time time_of_error;
ErrorProcessor error_processor;
}

指针变量的命名前缀

指针变量多数情况应在前面加 ‘p’。

星号应靠近类型,而不是变量名。

例如:

String* pName=new String;

特别的:String* pName, name; 应分成两行来写:

String* pName;
String name;

引用变量和返回引用函数的命名前缀

引用必须用‘r’作前缀修饰。

例如:

class Test{
public:
void DoSomething(StatusInfo&rStatus);
StatusInfo& rStatus();
constStatusInfo& Status() const; // 这里返回的是常量引用,所以不符合本规则
private:
StatusInfo& mrStatus;
}

全局变量的命名前缀

全局变量总是以 ‘g(g_)’ 作为前缀。

例如:

Logger g_Log;
Logger* g_pLog;

全局常量的命名

全局常量全部大写,并以下划线分隔单词。

例如:

const intA_GLOBAL_CONSTANT = 5;

静态变量的命名前缀

静态变量以 ‘s’ 作为前缀。

例如:

class Test{
public:
private:
staticStatusInfo m_sStatus;
}

自定义类型typedef的命名

类型定义名称指的是用typedef定义的名称。

类型定义名称使用与类名相同的规则,并使用Type作为后缀。

例如:

typedef uint16  ModuleType;
typedef uint32 SystemType;

宏定义的命名

所有单词的字母都用大写,并使用下划线分隔。

例如:

#define MAX(a,b) blah
#define IS_ERR(err) blah

C 函数的命名

C++项目中,应尽量少用C函数。

C函数使用GNU规范,所有字母都使用小写,并用下划线作为单词的分隔。

例如:

int some_bloody_function(){

}

特别的,为了兼容C/C++,在必要的时候,在C++中应以下面的格式定义C函数:

extern “C” int some_bloody_function();

或在C/C++中推荐使用下面的格式:

#ifdef__cplusplus__
extern “C”{
#endif
int some_bloody_function(){}
#ifdef__cplusplus__
}
#endif

枚举的命名

所有字母都大写,并用下划线作为单词分隔。

例如:

enumPinStateType{
PIN_OFF,
PIN_ON;
};
enum{STATE_ERR,STATE_OPEN,STATE_RUNNING,STATE_DYING};

排版规则

布局和模板

类的布局模板

请使用下面的模板来创建一个新的类:

/**     
* 用一行来描述类
*
*#include "XX.h" <BR>
*-llib
*
* 类的详细说明
*
* @seesomething
*/
#ifndef SORUTION_PROJECT_CLASSNAME_H

#define SORUTION_PROJECT_CLASSNAME_H
// 在这里包含系统头文件

// 在这里包含项目头文件

// 在这里包含局部头文件

// 在这里放置前置引用

class XX{
public:
// 类的生命周期控制函数,如构造和析构,以及状态机

/***Default constructor.*/
XX(void);
/***Copy constructor.*/
XX(const XX& from);
/***Destructor.*/
virtual ~XX(void);
// 在这里放置类的运算操作符

/**
*Assignment operator.
*
*@param from THe value to assign to this object.
*
*@return A reference to this object.
*/
XX& operator=(XX&from);

// 在这里放置类的操作

// 在这里放置属性存取

// 在这里放置类的状态查询

protected:
private:
};
// 内联方法定义

// 外部引用

#endif // SORUTION_PROJECT_CLASSNAME_H

定义的顺序是: public, protected, private。

要清楚public/protected/private都应该放置哪些东西。

保护头文件不被重复包含

应使用宏定义来保护头文件不被重复包含:

#ifndef SORUTION_PROJECT_CLASSNAME_H
#define SORUTION_PROJECT_CLASSNAME_H
#endif // SORUTION_PROJECT_CLASSNAME_H

如果使用命名空间的时候,要把命名空间加到文件名前面:

#ifndef SORUTION_PROJECT_NAMESPACE_CLASSNAME_H
#define SORUTION_PROJECT_NAMESPACE_CLASSNAME_H
#endif

方法和函数的布局

对于有较多参数的函数的写法。

如果参数较多,一行写不下,我们应该分成几行来写,并且每个参数都另起一行对齐:

int AnyMethod(
int arg1,
int arg2,
int arg3,
int arg4);

int AnyMethod(int arg1 
,int arg2
,int arg3
,int arg4);

尽量使一行不要超过78个字母

有许多编辑器屏幕只有78个字母宽。

保证一行只写一条语句

一行最多只写一条语句。

一行只定义一个变量。

圆括号规则

圆括号与关键字之间应放一个空格。

圆括号与函数名之间不要有空格。

Return 语句不要使用圆括号。

使用goto continue break 和?:

Goto

尽量避免使用Goto 语句。

Continue and Break

Continue和break实际上起到与goto一样的作用,因此,尽量少用为上。并且,Continue与break最好不要连用。

A?B:C

用括号把条件表达式括起来。

不要在 ? : 中写上过多的代码,操作表达式应尽可能简洁。

操作语句应分行写,除非它们能够简洁的放在一行当中。

例如:

(condition) ?funct1() : func2();

(condition)
? longstatement
: anotherlong statement;

运算符号的规则

一元操作符如(!、~ 等等)应贴近操作对象。

if (!IsOk)
return ++v;

二元操作符如(+、%、== 等等)应在前后留空格。

if ( v1 == v2)
return v1 * 3;

++ 和 – 尽量使用前置运算。在C++中,不管 ++i 还是 i++,总是++i更容易生成优化代码。

变量声明语句块

变量应该是随用随声明,不要集中在函数前(有些C语言不支持,则不在此要求之列)。特别是在for语句的循环变量,应只在for语句中定义。

声明语句块必须要对齐。类型,变量,等号和初始化值要分别对齐。

例如:

DWORD      mDword;
DWORD* mpDword;
char* mpChar;
char mChar;

mDword = 0;
mpDword = NULL;
mpChar = NULL;
mChar = 0;

文档及注释

应当使用文档自动生成工具,来生成相关的程序文档。

文件或程序库的文档注释

可以为整个文件编写文档。

例如:

/** @file file.h

* Abrief file description.

* Amore elaborated file description.

*/

类文档注释

在类定义前面应加上类说明文档。

例如:

/** WindowsNT

* @brief Windows Nice Try.

* @author Bill Gates

* @author Several species of small furryanimals gathered together

* in a cave and grooving with a pict.

* @version 4.0

* @date 1996-1998

* @bug It crashes a lot and requires hugeamounts of memory.

* @bug The class introduces the more bugs, thelonger it is used.

* @warning This class may explode in your face.

* @warning If you inherit anything from thisclass, you're doomed.

*/

class WindowsNT {};

函数文档注释

所有的参数都应该有文档说明(param),所有的返回代码都应该有文档说明(return),所有的例外都应该有文档说明(exception)。可以使用(see)引用有关的开发资源。

/**

* 赋值操作符

*

*@param val 将要赋给本对象的值

*

*@return 本对象的引用

*/

XX& operator =(XX& val);

注释属性

一些自动文档工具定义的属性可以包含在文档中,常用的有:

n 前提条件 (pre)
定义调用这个函数的前提条件

n 警告说明 (warning)
定义一些关于这个函数必须知道的事情。

n 备注说明 (remarks)
定义一些关于这个函数的备注信息。

n 将要完成的工作 (todo)
说明哪些事情将在不久以后完成。

n 使用例子说明 (example)
一个图片能表达100句话,一个好的例子能解答1000个问题。

例如:

/**

* 复制一个字串

*

*@pre

* - 需要保证(from != 0)

* - 需要保证(to != 0)

*

*@warning

* 缓冲区必需足够大,以便容纳的下要拷贝的字串。

*

*@example teststrcpy.cpp

*

*@param from 要拷贝的字串

*@param to 用于容纳字串的缓冲区

*@return void

*/

void strcpy(constchar* from, char* to);

Include 语句注释

如果有必要,#include语句也应有注释,它可以告诉我们,为什么要包含这个头文件。

语句块注释

语句块的注释可以用在语句块的开头和结束位置:

编码要求

不要忽略编译器的警告

编译器的警告,通常能够指示出编码存在的笔误或逻辑错误。因此,不能轻视编译器的任何警告。正确的作法是,不允许代码在编译时产生任何警告信息。

应使用源代码管理器

根据开发规模,选择合适的源代码管理器。使用源代码管理器是非常必要的。

固有的类方法成员

默认构造函数(DefaultConstructor)

如果构造函数的所有参数都是可选的,那么这个构造函数也是默认构造函数。如果没有定义任何普通构造函数,则编译将自动生成一个。

虚析构函数(Virtual Destructor)

如果一个类可以被继承,那么应该使用虚析构函数。如果没有定义虚析构函数,则编译器将自动生成一个。

拷贝构造函数(Copy Constructor)

如果一个类不应该被拷贝,应该定义一个私有的拷贝构造函数,并且不定义它的实现。如果不知道一个类是否应该被拷贝,就认为它是不可拷贝的,直到你确认它应该被拷贝。如果没有定义拷贝构造函数,则编译器将自动生成一个。

赋值操作(AssignmentOperator)

如果一个类不应该被赋值,应该定义一个私有的赋值操作函数,并且不定义它的实现。如果不知道一个类是否应该被赋值,就认为它是不可赋值的,直到你确认它应该被赋值。如果没有定义赋值操作函数,则编译器将自动生成一个。

使用命名空间

根名字一般是设计者的名字。比如公司名称等等。

不要在全局空间使用using语句。

初始化所有的变量

无论如何,都要初始化所有的变量。我们无法保证编译器会给个什么样的初值。

保持函数短小精悍

一般情况下,一个函数最好在一个屏幕内,不要超过三个屏幕。

对空语句进行注释

For和while语句如果跟随一个空语句,需要对此语句进行注释,并且空语句应另起一行。

while(*dest++ = *srC++)
;// VOID

不要用if语句的默认方法测试非零值

If语句只用于检测布尔值(bool),不要用默认的方法测试非零值。

建议使用:

if (f() != FAIL)

不建议使用下面的表达式:

if (f())

布尔类型

早期的C++没有布尔类型,但新的C++标准增加了布尔类型。如果可以使用内置的布尔类型的情况下,应使用布尔类型。

避免在语句中内含赋值

只有一种情况可以在语句中内含赋值,它要能使代码显得更易理解。

例如:

while (EOF != (c= getchar())){

}

正确的使用Const

C/C++ 提供const 关键字,用于指示不应该被修改的对象或数据。正确的使用Const既可以提供编译器的优化指示,也能够避免一些编码错误。

不要在头文件定义数据

不要把数据定义放在头文件。

不要直接使用数字

直接使用数字,会使源代码难以理解和维护。我们可以用#define或者常量来改变这一状况。

如果可以,使用内联函数代替宏。

例如:

#ifndef MAX
#define MAX(x,y) (((x) > (y) ? (x) : (y)) // 取最大数
#endif

使用内联函数可以达到相同的效果,而且更安全:

inline int max(int x, int y){
return (x> y ? x : y);
}

在宏展开时,使用括号可以避免宏展开后产生的二义性。

例如:

#define ADD(x,y) x + y

必须写成:

#define ADD(x,y) ((x) + (y))

和全局变量一样,宏也会与其它名称产生冲突。

下面两条规则有助于解决这个问题:

一、在宏名称前加上库的名字

二、避免使用简单而常用的名字,如:MAX和MIN。