C++编码规范
一、 文件名命名规范
- 文件的名字需和承载的类之间有一定的关联性,而且常用的公共头文件需要定义得比较简短
- 文件名采用全部小写的方式,不允许大小写混合的方式
二、 源程序文件
- 源文件段落依下列顺序:
- 序言
- 系统头文件引用(include)
- 用户头文件引用(include)
- 全局常量宏定义(define)
- 全局函数宏定义(define)
- 全局类型定义(typedef)
- 全局枚举类型定义(enums)
- 全局变量说明(extern)
- 全局变量说明(non-static)
- 全局变量说明(static)
- 函数(通常从最高层开始,按层次横向排列。如果定义较多的独立的公用函数,可以考虑按字母顺序排列。
- 序言内容应包括版权声明、内容描述、格式如下:
/*********************************************************************
* Copyright 2016 by 99letou.
* All right reserved.
*
* 功能:*****文件
*
* Edit History:
*
* 2016/04/04 – Created by David.
* 2006/04/06 – Modified by John to print output in the new form.
*/
- 段落之间用空行分隔。
- 文件长度尽可能在1000行之内。
- 每行长度尽可能不要超过79列。
三、 头文件
- 头文件开头注释:
/**
* Description: This is a test program
*/
- 每个头文件采用下面的形式避免被重复引用:
#ifndef EXAMPLE_H
#define EXAMPLE_H
… /* body of example.h file */
#endif /* EXAMPLE_H */
- 多个源文件引用的说明放入头文件中。
- 说明函数或外部变量的头文件应在定义该函数或变量的文件中引用。
- 引用头文件不要使用绝对路径。使用<filename.h>引用系统头文件;使用“filename.h””引用用户头文件。
四、 数据变量说明
- 常量的命名规范:
常量名由全大写字母组成,单词间通过下划线来界定
- 成员变量的命名规范:
m+_+[变量类型]+变量名
m后带上明确表达变量意义的英文名词而不要用难以表达意义的缩写。且m后的变量名首字母必须大写。如:
class CMyGame
{
private :
CPoint m_Point;<―――普通类型
CGame* m_pGame;<―――指针类型
Int m_nGameID;<―――整数类型
}
- 函数参数的命名规范:
采用波浪法来定义函数参数变量。同样的,也必须使用可以明确表达变量意义的英文名词,而不仅仅是缩写。
[变量类型]+变量名
如定义一个读取文件的函数,并且用参数传入一个文件名
public boolean readFile(String strFileName)
{
…
m_sFileName = strFileName;
}
- 局部变量的命名规范:
采用全部小写的方式定义局部变量名。而且尽量用有意义的名词作为变量名。
局部变量不需要在前面增加变量类型标识。
public boolean readFile()
{
String filename = “e.ext”;
}
- 局部循环变量的命名规范:
用小写的i、j、k依次作为循环变量
如:
for (int i = 0 ; i < 10 ; i ++ )
{
for (int j = 0 ; j <10 ; j ++ )
{
}
}
- 临时变量的命名规范:
一般情况下尽量使用有意义的变量名,如果是某变量的临时变量,可采用前面增加tmp前缀的方式。如果实在没办法,可采用tmp、temp等作为临时变量名,但不可以过多定义诸如tmp1、tmp2等变量,如果出现此情况,则必定可以采用有意义的名词代替。
int tmpserialno = mSerialNo++;
tmpserialno %= 10;
mSerialNo = tmpserialno;
- 枚举常量
采用波浪法加E前缀来定义枚举常量。同样的,也必须使用可以明确表达变量意义的英文名词,而不仅仅是缩写。且E后首字母必须大写
正确写法:
enum ECHARTYPE
{
CHARTYPE_SPACE = _ISspace,
CHARTYPE_PRINT = _ISprint,
CHARTYPE_CNTRL = _IScntrl,
};
错误写法:
enum ECHARTYPE{ SPACE = _ISspace, PRINT = _ISprint, CNTRL = _IScntrl };
- 变量定义范围建议:
尽量在变量的有效范围内定义之,如果一个变量仅仅在某个函数内使用到,则把其定义为一个成员变量就是不适合的。在如某些变量只在循环内用到,则应该在循环体内定义之,而不要随意括大其定义范围。
- 变量类型:
基本类型(保留类型)
char | c | cMyChar; |
bool | b | bMyBool; |
int/short/long | n | nMyInt; |
float/double | f | fMyFloat; |
Long64 | l | lMyLong; |
String/char*(当明确知道是用作字符串的情况下) | str | strMyString |
map | map | mapPlayers |
vector | vec | vecPlayers |
list | lst | lstPlayers |
multimap | mmap | mmapState |
特殊类型
Handle | h | hFile; |
- 指针类型:
在变量类前增加p标识,作为标识此变量为指针类型,如:
int *pKey = NULL;
CDateTime* pDateTime = new CDateTime(0);
- 数组变量命名规范:
对于数组类型的变量,需要采用复数s以标识此为数组变量。如:
int m_nKeys[100];
double dValues[200];
CMyClass Strings[200];
特例:对于char buf[200]这种定义,由于其使用习惯原因,不需要采用数组前缀。
对于数组指针变量,同样在类型前增加p标识。在删除的时候,必须用delete[]的方式删除动态数组,以保证数组内的实例被调用析构函数。
CDateTime* pBirthdays = new CDateTime[20];
….
delete[] pBirthdays;
int* m_pnKeys = new int[20];
五、 类说明
- 类名命名规范
根据各种类的类型和用途,采用不同前缀+名词的命名方式。名词采用波浪法,必须使用可以明确表达变量意义的英文名词。
- 前缀
- 普通类(C)
一般类,采用C+名词的方式命名
例如:
Class CPerson
{
Protected :
String m_strName;
String
m_strNickName;
};
- 接口类(I)
对于无任何实现的纯虚类,称为接口类,这些类的特点都是无任何成员变量,存在需要其他实现类实现的纯虚函数。
接口类必须采用I+名词的方式命名。
例如:
class IDataInput
{
Public :
virtual ~IDataInput() {};
virtual void read() = 0;
}
- 结构体(T)
Struct,结构体,必须以T+名词的方式命名。
例如:
typedef struct _t_mystruct
{
Unsigned int number;
Unsigned int value;
Char name[255];
}TMyStruct,* TMyStructPtr;
- 模板类
模板类,必须以全小写命名。
例如:
template<class T,allocator A>
class screen : public map<T,A>
{
}
- 类成员的初始化(Member initialization lists)
成员的初始化必须在构造函数名的下一行开始
正确写法:
gribble::gribble()
:m_private_data(0), m_more_stuff(0), m_helper(0)
{
}
错误写法:
gribble::gribble():m_private_data(0),m_more_stuff(0), m_helper(0)
{
}
- 类成员变量必须采用private的定义,不允许使用public定义,并置于类定义的最后,另外用getter和setter的函数访问和修改之(在需要考虑程序大小的时候可以忽略此规则)
class CTest
{
public :
int getTopicID() //获取话题ID
{
return
m_nTopicID;
}
void setTopicID(int nTopicID) //设置话题id
{
m_nTopicID = nTopicID;
}
private:
TInt m_nTopicID;
}
- 一个方法只完成一个任务,不要把多个任务放在一个方法中,即使那些任务非常小
- 功能类的方法的大小尽可能不要超过100行
六、 函数说明
- 每个函数前加入注释块,描述函数功能、输入参数和返回值,必要时应说明其功能、用法、重要设计决策与副作用。
/**
* @description : PrintOneMsg
* @input param : nUserID — 用户ID
* @output param: nMoney– 玩家体力
* @return: 0 — success
other — false
* @exception CException reason
*/
int PrintOneMsg((int nUserID,int & nMoney)
{
function body;
return 0;
}
- 函数名称原则上采用动词名词的组合形式,函数名称采用波浪法,第一个单词首字母采用小写,后面的单词的首字母采用大写
如:int getTopicID()
- 函数的参数个数尽可能不要超过5个。
- 函数体的开、闭花括号写在第一列,分别占一行。函数体的局部变量说明和代码缩进4格。
- 业务逻辑函数的大小尽可能不要超过200行
七、 代码注释规范
为了能更好的体现代码即详细设计文档这个意图,在对代码注释的时候,需遵守下列的规范。
- 用“注释块”描述数据结构、算法等,放在被注释的程序段前:
/*
* Here is a block comment.
* The opening slash-star and closing star-slash are alone on a line.
* …
*/
- “单行注释”放在被注释的程序段前,并与该程序段对齐:
if (argc > 1)
{
// Get input file frmm command line
if (freopen(argv[1], “r”, stdin) == NULL)
{
perror(argv[1]);
}
}
- 放在代码行尾的“短注释”与被注释的代码分开至少一个空格:
if (a == EXCEPTION)
{
b = TRUE; // special case
BODY
}
else
{
b = isprime(a); //works only for odd a
BODY
}
- 函数抛出异常注释规范
对于可能抛出异常的类方法、函数,在注释中说明所抛出异常的类名和产生异常的原因;
注释中采用如下语法
/**
* @exception fully-qualified-class-name description
*/
fully-qualified-class-name:方法声明的异常名
description:产生异常的原因
例如:
/**
* 连接数据库
* @ exception CSQLException 数据库连接失败,具体原因请根据sqlcode查阅数据库错误代码说明
*/
virtual bool connect()=0;
八、 构造与析构
- 类构造函数,做非资源变量的初始化动作,原则上不进行任何可能出现错误的操作,如分配内存,调用资源等。
- delete 与new ,free与malloc,delete[] 与new[]必须成对使用,否则不保证会不会出现问题。
九、 异常处理
- 所有的异常抛出都必须以对象形式,而不是能以new 的指针形式。而捕获的时候就可以统一用引用的方式进行捕获。这样在性能和开销方面不会加大的同时,也无需关心指针的删除问题。
try
{
throw CDateTimeException(“datetime format error”);
}
catch (CDateTimeException& e)
{
cout<<e.what()<<end;
}
十、 其他良好的编程习惯
- 缩进采用tab而不是用space;tab的缩进显示距离建议为4个空格
- 用一个空行来分开代码和逻辑的分组;
- 花括号“{”、“}”需独立一行,而不像if for等可以和括号同一行,而且必须与if等缩进相同的空间
如:if ( test == 1 )
{
}
- 在每个运算符和括号的前后都空一格。
- 无warning编译
- 在switch语句中出现的fall-through应加以注释:
switch (expr)
{
case ABC: //注释
{
statement
break;
}
case DEF: //注释
{
statement
break;
}
default: XYZ:
{
statement;
break;
}
}