C++编程命名规范
编辑: 捞刀客
在软件开发过程中,代码的命名规范是一项很突出的内容。一套定义完整、结构清晰明了的命名规范将大大提升源代码的可读性和软件的可维护性。
当然没有一种命名规则可以让所有的程序员都满意,程序设计教科书一般也不会指定命名规则。但是制定一套统一的命名规则,并在项目中贯彻实施,是团队开发的产品质量一项必不可少的工作。
第一部分:文件结构
文件头注释
所有C++的源文件均必须包含一个规范的文件头,文件头包含了该文件的名称、功能概述、作者、版权和版本历史信息等内容。标准文件头的格式为:
/*! @file
********************************************************************************
<PRE>
模块名 : <文件所属的模块名称>
文件名 : <文件名>
相关文件 : <与此文件相关的其它文件>
文件实现功能 : <描述该文件实现的主要功能>
作者 : <作者部门和姓名>
版本 : <当前版本号>
--------------------------------------------------------------------------------
多线程安全性 : <是/否>[,说明]
异常时安全性 : <是/否>[,说明]
--------------------------------------------------------------------------------
备注 : <其它说明>
--------------------------------------------------------------------------------
修改记录 :
日 期 版本 修改人 修改内容
YYYY/MM/DD X.Y <作者或修改者名> <修改内容>
</PRE>
*******************************************************************************/
头文件
头文件通常由以下几部分组成:
文件头注释 | 每个头文件,无论是内部的还是外部的,都应该由一个规范的文件头注释作为开始。 |
预处理块 | 为了防止头文件被重复引用,应当用ifndef/define/endif结构产生预处理块。 |
函数和类/结构的声明等 | 声明模块的接口 |
需要包含的内联函数定义文件(如果有的话) | 如果类中的内联函数较多,或者一个头文件中包含多个类的定义(不推荐),可以将所有内联函数定义放入一个单独的内联函数定义文件中,并在类声明之后用“#include”指令把它包含进来。 |
头文件的编码规则:
引用文件的格式 | 用 #include <filename.h> 格式来引用标准库和系统库的头文件(编译器将从标准库目录开始搜索)。 用 #include "filename.h" 格式来引用当前工程中的头文件(编译器将从该文件所在目录开始搜索)。 |
分割多组接口(如果有的话) | 如果在一个头件中定义了多个类或者多组接口(不推荐),为了便于浏览,应该在每个类/每组接口间使用分割带把它们相互分开。 |
内联文件
如上所述,在内联函数较多的情况下,为了避免头文件过长、版面混乱,可以将所有的内联函数定义移到一个单独的文件中去,然后再用#include指令将它包含到类声明的后面。这样的文件称为一个内联函数定义文件。
按照惯例,应该将这个文件命名为“filename.inl”,其中“filename”与相应的头文件和实现文件相同。
内联函数定义文件由以下几部分组成:
文件头注释 | 每内联函数定义文件都应该由一个规范的文件头注释作为开始 |
内联函数定义 | 内联函数的实现体 |
内联函数定义文件的编码规则:
分割多组接口(如果有的话) | 如果在一个内联函数定义文件中定义了多个类或者多组接口的内联函数(不推荐),必须在每个类/每组接口间使用分割带把它们相互分开。 |
文件组成中为什么没有预处理块? | 与头文件不同,内联函数定义文件通常不需要定义预处理块,这是因为他们总是被包含在与其相应的头文件预处理块内。 |
实现文件
实现文件包含所有数据和代码的实现体。实现文件的格式为:
文件头注释 | 每个实现文件都应该由一个规范的文件头注释作为开始 |
对配套头文件的引用 | 引用声明了此文件实现的类、函数及数据的头文件 |
对一些仅用于实现的头文件的引用(如果有的话) | 将仅与实现相关的接口包含在实现文件里(而不是头文件中)是一个非常好的编程习惯。这样可以有效地屏蔽不应该暴露的实现细节,将实现改变对其它模块的影响降低到最少 。 |
程序的实现体 | 数据和函数的定义 |
实现文件的编码规则:
分割每个部分 | 在本地(静态)定义和外部定义间,以及不同接口或不同类的实现之间,应使用分割带相互分开。 |
第二部分:命名规则
【规则1】标识符应简单明了,望文知意。
标识符采用英文单词。切忌使用汉语拼音来命名。程序中的英文单词一般不要太复杂,用词应当准确。例如不要把CurrentValue写成NowValue。
尽量不要使用单词缩写或首字母缩写。只有当标识符过长时才考虑使用单词缩写。在使用缩写时,不要自创缩写,尽量使用被广泛接受的缩写。
【规则2】标识符长度应当符合“min-length && max-information”原则。
一般的讲,长名字能更好地表达含义,所以函数名、变量名、类名长达十几个字符不足为怪。但是名字也不是越长越好。例如:变量名maxval就比maxValueUntilOverflow更好用。单字符的名字也是有用的,常见的如i,j,k,m,n,x,y,z等,它们通常用作函数内的局部变量。一般来讲:作用域越大,名字可以越长,作用域越小,名字可以越短。
【规则3】命名规则尽量与所采用的操作系统或开发工具的风格保持一致。
例如Windows应用程序的标识符通常采用“大小写”混排的方式,如AddChild。而Unix应用程序的标识符通常采用“小写加下划线”的方式,如add_child。别把这两类风格混在一起用。
【规则4】程序中不要出现仅靠大小写区分的标识符。
例如:int x和int X;void foo() 和void FOO() 等。
【规则5】避免在不同级别的作用域中重名。
程序中不要出现标识符完全相同的局部变量和全局变量,尽管两者因作用域的不同而不会发生语法错误,但会使人产生误解。类的函数重载除外。
【规则6】正确命名具有互斥意义的标识符。
使用正确的反义词组命名具有互斥意义的变量或相反动作的函数。
如:"MinValue"和"MaxValue","GetName()" 和 "SetName()"
【规则7】尽量避免名字中出现数字编号。
如Value1,Value2等,除非逻辑上的确需要编号。这是为了防止程序产生无意义的名字,降低程序的可读性。
【规则8】使用库标志
在开发动态库时,为了防止软件库中的一些标识符和其它软件库中标识符冲突,可以为各种标识符加上能反映软件性质的前缀。
例如三维图形标准OpenGL的所有库函数均以gl开头,所有常量(或宏定义)均以GL开头。
详细说明和举例
“匈牙利”法最大的特征就是类型前缀。例如:
int nNum = 0; // n 为类型信息,表明 nNum 是一个 int 类型
class CUser; // C 为类型信息,表明 CUser 是一个类
但是正因为如此,“匈牙利”法命名规范也给人一种繁琐的感觉。例如:
int i, j, k;
float x, y, z;
倘若采用“匈牙利”命名规则,则应当写成:
int iI, iJ, ik; // 前缀 i表示int类型
float fX, fY, fZ; // 前缀 f表示float类型
对于此类情况,可以不加类型前缀。而对于下列情况我会加:
int iHeigh;
Bool bFlag;
总之,类型前缀是一个可选的命名规则。下面分类进行详细的规范。
一、 类和接口
1、 命名:类名都以大写字母“C”开头,后跟一个或多个单词。每个单词的首字母要大写。接口以大写"I"开头,代表Interface。
2、 组成形式:推荐用"名词"或"形容词+名词"的形式,例如:"CAnalyzer", "CFastVector" ....
二、 函数
1、 命名:函数的名称由一个或多个单词组成。每个单词的首字母要大写。最长不得超过20个字符。
2、 组成形式:全局函数应当使用"动词"或者"动词+名词"(动宾词组)的形式。例如:"GetName()", "DrawBox()"。
类成员函数应当只使用“动词”,被省略掉的名词就是对象本身。例如:
" box->Draw();"。
3、 全局函数:以小写前缀"g"或”m”开头。
4、 回调和事件处理函数 :习惯以单词“On”开头。例如:"_OnTimer()", "OnExit()"
三、 变量
变量是程序中使用最多的标识符,变量的命名规范是一套C++命名规范中最重要的部分:
1、 命名:变量名由作用域前缀+类型前缀+一个或多个单词组成。变量用小写字母开头的单词组合而成,第二个单词的首字母要大写。例如:int nDrawMode。变量最长不得超过20个字符。
特殊的:对于某些用途简单明了的局部变量,也可以使用简化的方式,如:i, j, k, x, y, z
2、 组成形式:变量的名字应当使用"名词"或者"形容词+名词"。例如:"nCode", "m_nState","nMaxWidth"," oldValue "," newValue "。
3、 作用域前缀:作用域前缀标明一个变量的可见范围。作用域可以有如下几种:
前缀 | 说明 | 例子 |
无 | 局部变量 |
|
m_ | 类的成员变量(member) | Int m_width |
ms_ | 类的静态成员变量(static member) | static int ms_initValue; |
s_ | 静态变量(static) | static int s_initValue; |
g_ | 外部全局变量(global) | int g_howManyPeople; |
sg_ | 静态全局变量(static global) |
|
gg_ | 进程间共享的共享数据段全局变量(global global) |
|
说明:作用域前缀不同于下面的类型前缀,应该坚决执行。原因是:
1)变量作用域和链接性改变的情况是很少的,例如,很少的情况下会把一个成员变量改成静态变量
2)编程中使用的工具常常不会直观的显示变量的作用域和链接性
4、 类型前缀:作用域前缀标明一个变量的可见范围。类型前缀标明一个变量的类型,有如下几种:
前缀 | 说明 | 例子 |
b | 布尔型变量(bool, BOOL) | bEnable |
ch | 字符型变量(char TCHAR) | chName |
lpsz | LPSTR、LPCSTR、LPCTSTR | lpszName |
n | 整型和位域变量(int, UINT,__int32,__int64) | nLength |
l | long | lOffset |
by | BYTE |
|
w | WORD | wPos |
dw | DWORD | dwRange |
f | 浮点型变量(float) |
|
d | double |
|
p | 指针型变量和迭代子(pointer) | pDoc |
lp | 远指针 |
|
e | 枚举型变量(enumeration) |
|
pfn | 特别针对指向函数的指针变量和函数对象指针(pointer of function) |
|
g | 数组(grid) |
|
h | handle Windows对象句柄 | hWnd |
四、 常量
常量名由全大写字母组成,单词间通过下划线来界定,如: DELIMITER, MAX_BUFFER。类型前缀的定义与变量命名规则中的相同。
五、 结构体、宏、枚举变量、联合体
全部由前缀+大写字母组成,单词间使用下划线界定。
1、 结构体:加小写前缀"_"或"__" ,之后以大写字母开头。
例:
typedef struct _point
{
int x;
int y;
} POINT;
2、 宏:大写字母组成,单词间使用下划线界定
例:#define MAXNUMBER 100
3、 枚举变量:加小写前缀"enum"。
例:
typedef enum _file_open_mode
{
OPEN_READONLY,
OPEN_READWRITE
}FILE_OPEN_MODE;
第三部分:代码风格与版式
代码风格的重要性怎么强调都不过分。一段稍长一点的无格式代码基本上就是不可读的。 先来看一下这方面的整体原则:
1. 空行的使用
空行起着分隔程序段落的作用。空行得体(不过多也不过少)将使程序的布局更加清晰。空行不会浪费内存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。所以不要舍不得用空行。
- 在每个类声明之后、每个函数定义结束之后都要加2行空行。
- 在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。
2. 语句与代码行
- 一行代码只做一件事情,如只定义一个变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。
- "if"、"for"、"while"、"do"、"try"、"catch" 等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加 "{ }" 。这样可以防止书写和修改代码时出现失误。
3. 缩进和对齐
- 程序的分界符 "{" 和 "}" 应独占一行并且位于同一列,同时与引用它们的语句左对齐。
- "{ }" 之内的代码块在 "{" 右边一个制表符(4个半角空格符)处左对齐。如果出现嵌套的 "{ }",则使用缩进对齐。
- 如果一条语句会对其后的多条语句产生影响的话,应该只对该语句做半缩进(2个半角空格符),以突出该语句。
例如:
void
|
4. 最大长度
代码行最大长度宜控制在70至80个字符以内。代码行不要过长,否则眼睛看不过来,也不便于打印(2009年更新:随着GUI开发环境和高分宽屏的普及,此规则可以视情况适当放宽)。
5. 长行拆分
长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。
例如:
|
6. 空格的使用
1. 关键字之后要留空格。象 "const"、"virtual"、"inline"、"case" 等关键字之后至少要留一个空格,否则无法辨析关键字。象 "if"、"for"、"while"、"catch" 等关键字之后应留一个空格再跟左括号 "(",以突出关键字。
2. 函数名之后不要留空格,紧跟左括号 "(",以与关键字区别。
3. "(" 向后紧跟。而 ")"、","、";" 向前紧跟,紧跟处不留空格。
4. "," 之后要留空格,如 Function(x, y, z)。如果 ";" 不是一行的结束符号,其后要留空格,如 for (initialization; condition; update)。
5. 赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如"="、"+=" ">="、"<="、"+"、"*"、"%"、"&&"、"||"、"<<", "^" 等二元操作符的前后应当加空格。
6. 一元操作符如 "!"、"~"、"++"、"--"、"&"(地址运算符)等前后不加空格。
7. 象"[]"、"."、"->"这类操作符前后不加空格。
8. 对于表达式比较长的 for、do、while、switch 语句和 if 语句,为了紧凑起见可以适当地去掉一些空格,如 for (i=0; i<10; i++) 和 if ((a<=b) && (c<=d)) 等。
例如:
|
7. 修饰符的位置
为便于理解,应当将修饰符 "*" 和 "&" 紧靠数据类型。 例如:
char* name;
|
8. 注释
- 注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不可放在下方。
- 边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。
- 注释应当准确、易懂,防止注释有二义性。错误的注释不但无益反而有害。
- 当代码比较长,特别是有多重嵌套时,应当在一些段落的结束处加注释,便于阅读。
专注领域: 机器视觉,智能监控,网络视频。
应用平台:WINDOWS,LINUX, EMBEDDED
合作网站: http:// www.hdy.net.cn 怡心小院
http:// www.m20.com.cn 麦子频道
QQ: 494761986 EMAIL:buffi@qq.com