SNIP04编码规范与范例C++规范V12.docx
《SNIP04编码规范与范例C++规范V12.docx》由会员分享,可在线阅读,更多相关《SNIP04编码规范与范例C++规范V12.docx(19页珍藏版)》请在冰豆网上搜索。
SNIP04编码规范与范例C++规范V12
PC客户端项目组
C++编程标准——命名和风格
我们采用微软.NetFramework命名规范,和MicroSoft保持一致,便于融合使用微软的最佳实践,也便于阅读。
实际上这涉及到两种命名规则:
Pascal命名规则和Camel命名规则,他们出现的场景不同。
这两种规范都要求变量命名时少用缩略词和无意义的字母,变量名称能够概括代表的功能,可以由多个词连续排列构成,每个单词之间称为逻辑断点。
●Pascal规范:
变量名称能够概括功能,由单词不间断组合在一起,每个单词的首字母都大写,如:
MyClass
●Camel规范:
变量名称能够概括功能,由单词不间断组合在一起,变量首字母小写,其它的每个单词都大写,如:
myClass
●匈牙利命名规则:
十分必要的情况下,在变量前加上数据类型前缀,尽量不要使用匈牙利命名规则,因为晦涩难懂。
我们采用Resharper作为规范检查工具
目录
PC客户端项目组1
C++编程标准——命名和风格1
1命名规则4
1.1总则4
1.2类/结构5
1.3函数5
1.4变量6
1.5常量7
1.6枚举、联合、typedef7
1.7宏、枚举值8
2文件结构8
2.1文件头注释8
2.2头文件9
2.3实现文件10
3代码风格与版式11
3.1语句与代码行12
3.2缩进和对齐12
3.3最大长度13
3.4长行拆分13
3.5空格的使用14
3.6修饰符的位置16
3.7注释16
4注释分类说明17
4.1源文件头部注释17
4.2函数头部注释17
4.3全局变量的注释18
附:
QT控件命名规范20
特别说明:
21
1命名规则
1.1总则
在软件开发这一高度抽象而且十分复杂的活动中,命名规则的重要性更显得尤为突出。
一套定义良好并且完整的、在整个项目中统一使用的命名规范将大大提升源代码的可读性和软件的可维护性。
在引入细节之前,先说明一下命名规范的整体原则:
同一性
在编写一个子模块或派生类的时候,要遵循其基类或整体模块的命名风格,保持命名风格在整个模块中的同一性。
标识符组成
标识符采用英文单词或其组合,应当直观且可以拼读,可望文知意,用词应当准确。
最小化长度&&最大化信息量原则
在保持一个标识符意思明确的同时,应当尽量缩短其长度。
避免过于相似
不要出现仅靠大小写区分的相似的标识符,例如“i”与“I”,“function”与“Function”等等。
避免在不同级别的作用域中重名
程序中不要出现名字完全相同的局部变量和全局变量,尽管两者的作用域不同而不会发生语法错误,但容易使人误解。
正确命名具有互斥意义的标识符
用正确的反义词组命名具有互斥意义的标识符,如:
"nMinValue"和"nMaxValue","GetName()"和"SetName()"....
避免名字中出现数字编号
尽量避免名字中出现数字编号,如Value1,Value2等,除非逻辑上的确需要编号。
这是为了防止程序员偷懒,不肯为命名动脑筋而导致产生无意义的名字(因为用数字编号最省事)。
1.2类/结构
除了异常类等个别情况(不希望用户把该类看作一个普通的、正常的类之情况)外,C++类/结构的命名应该遵循以下准则:
C++类/结构的命名
类的名称按实际情况,每个单词的首字母大写,保持文件名与类名一致。
推荐的组成形式
类的命名推荐用"名词"或"形容词+名词"的形式,例如:
"CAnalyzer","CFastVector"....
不同于C++类的概念,传统的C结构体只是一种将一组数据捆绑在一起的方式。
传统C结构体的命名规则为:
传统C结构体的命名
传统C结构体的名称全部由大写字母组成,单词间使用下划线界定,例如:
"SERVICE_STATUS_S","DRIVER_INFO"....
1.3函数
函数的命名
函数的名称由一个或多个单词组成。
为便于界定,每个单词的首字母要大写。
推荐的组成形式
函数名应当使用"动词"或者"动词+名词"(动宾词组)的形式。
例如:
"GetName()","SetValue()","Erase()","Reserve()"....
保护成员函数
保护成员函数的开头应当加上一个下划线“_”以示区别,例如:
"_SetState()"....
私有成员函数
类似地,私有成员函数的开头建议加上两个下划线“__”,例如:
"__DestroyImp()"....不强制。
虚函数
虚函数习惯以“Do”开头,如:
"DoRefresh()","_DoEncryption()"....
回调和事件处理函数
回调和事件处理函数习惯以单词“On”开头。
例如:
"OnTimer()","OnExit()"....
1.4变量
变量应该是程序中使用最多的标识符了,变量的命名规范可能是一套C++命名准则中最重要的部分:
变量的命名
变量名由作用域前缀+类型前缀+一个或多个单词组成。
为便于界定,每个单词的首字母要大写。
对于某些用途简单明了的局部变量,也可以使用简化的方式,如:
i,j,k,x,y,z....
作用域前缀
作用域前缀标明一个变量的可见范围。
作用域可以有如下几种:
前缀
说明
无
局部变量
m_
类的成员变量(member)
sm_或ms_
类的静态成员变量(staticmember)
s_
静态变量(static)
g_
外部全局变量(global)
sg_
静态全局变量(staticglobal)
gg_
进程间共享的共享数据段全局变量(globalglobal)
除非不得已,否则应该尽可能少使用全局变量。
类型前缀
类型前缀标明一个变量的类型,可以有如下几种:
前缀
说明
n
整型和位域变量(number)
e_
枚举型变量(enumeration)强制要求
c
字符型变量(char)
b
布尔型变量(bool)
f
浮点型变量(float)
p
指针型变量和迭代子(pointer)强制要求
pfn
特别针对指向函数的指针变量和函数对象指针(pointeroffunction),强制要求
arr
数组(Array)
str
字符串,强制要求
i
类的实例(instance)
对于经常用到的类,也可以定义一些专门的前缀,如:
std:
:
string和std:
:
wstring类的前缀可以定义为"st",std:
:
vector类的前缀可以定义为"v"等等。
类型前缀可以组合使用,例如"gc"表示字符数组,"ppn"表示指向整型的指针的指针等等。
推荐的组成形式
变量的名字应当使用"名词"或者"形容词+名词"。
例如:
"nCode","m_nState","nMaxWidth"....
1.5常量
C++中引入了对常量的支持,常量的命名规则如下:
常量的命名
常量名由类型前缀+全大写字母组成,单词间通过下划线来界定,如:
cDELIMITER,nMAX_BUFFER....
类型前缀的定义与变量命名规则中的相同。
Define要大写,Const命名规则与普通变量一致。
1.6枚举、联合、typedef
枚举、联合及typedef语句都是定义新类型的简单手段,它们的命名规则为:
枚举、联合、typedef的命名
枚举、联合、typedef语句生成的类型名由全大写字母组成,单词间通过下划线来界定,如:
FAR_PROC,ERROR_TYPE....
1.7宏、枚举值
宏、枚举值的命名
宏和枚举值由全大写字母组成,单词间通过下划线来界定,如:
ERROR_UNKNOWN,OP_STOP....
2文件结构
2.1文件头注释
所有C++的源文件均必须包含一个规范的文件头,文件头包含了该文件的名称、功能概述、作者、版权和版本历史信息等内容。
标准文件头的格式为:
/*!
@file
********************************************************************************
文件名 :
<文件名>
文件描述:
<描述该文件实现的主要功能>
作者 :
<作者部门和姓名>
版本 :
<当前版本号>
创建时间
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
备注 :
<其它说明>建议使用备注,不强制要求
--------------------------------------------------------------------------------
*******************************************************************************/
每行不要超过80个字符的宽度。
强制要求
2.2头文件
头文件通常由以下几部分组成:
文件头注释
所有C++的源文件均必须包含一个规范的文件头,文件头包含了该文件的名称、功能概述、作者、版权和版本历史信息等内容。
预处理块
为了防止头文件被重复引用,应当用ifndef/define/endif结构产生预处理块。
函数和类/结构的声明等
声明模块的接口
需要包含的内联函数定义文件(如果有的话)
如果类中的内联函数较多,或者一个头文件中包含多个类的定义(不推荐),可以将所有内联函数定义放入一个单独的内联函数定义文件中,并在类声明之后用“#include”指令把它包含进来。
头文件的编码规则:
-
用#include格式来引用标准库和系统库的头文件(编译器将从标准库目录开始搜索)。
用#include"filename.h"格式来引用当前工程中的头文件(编译器将从该文件所在目录开始搜索)。
如果在一个头件中定义了多个类或者多组接口(不推荐),为了便于浏览,应该在每个类/每组接口间使用分割带把它们相互分开。
一个头文件内只能定义一个类或一组接口。
如上所述,在内联函数较多的情况下,为了避免头文件过长、版面混乱,可以将所有的内联函数定义移到一个单独的文件中去,然后再用#include指令将它包含到类声明的后面。
这样的文件称为一个内联函数定义文件。
按照惯例,应该将这个文件命名为“filename.inl”,其中“filename”与相应的头文件和实现文件相同。
内联函数定义文件由以下几部分组成:
文件头注释
每内联函数定义文件都应该由一个规范的文件头注释作为开始
内联函数定义
内联函数的实现体
内联函数定义文件的编码规则:
分割多组接口(如果有的话)
如果在一个内联函数定义文件中定义了多个类或者多组接口的内联函数(不推荐),必须在每个类/每组接口间使用分割带把它们相互分开。
文件组成中为什么没有预处理块?
与头文件不同,内联函数定义文件通常不需要定义预处理块,这是因为他们总是被包含在与其相应的头文件预处理块内。
2.3实现文件
实现文件包含所有数据和代码的实现体。
实现文件的格式为:
文件头注释
每个实现文件都应该由一个规范的文件头注释作为开始
引用声明了此文件实现的类、函数及数据的头文件
将仅与实现相关的接口包含在实现文件里(而不是头文件中)是一个非常好的编程习惯。
这样可以有效地屏蔽不应该暴露的实现细节,将实现改变对其它模块的影响降低到最少。
数据和函数的定义
实现文件的编码规则:
在本地(静态)定义和外部定义间,以及不同接口或不同类的实现之间,应使用分割带相互分开。
3代码风格与版式
代码风格的重要性怎么强调都不过分。
一段稍长一点的无格式代码基本上就是不可读的。
先来看一下这方面的整体原则:
建议使用空行分隔程序段落。
空行得体(不过多也不过少)将使程序的布局更加清晰。
空行不会浪费内存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。
所以不要舍不得用空行。
∙在每个类声明之后、每个函数定义结束之后都要加2行空行。
∙在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。
3.1语句与代码行
∙一行代码只做一件事情,如只定义一个变量,或只写一条语句。
这样的代码容易阅读,并且方便于写注释。
∙"if"、"for"、"while"、"do"、"try"、"catch"等语句自占一行,执行语句不得紧跟其后。
不论执行语句有多少都要加"{}"。
这样可以防止书写和修改代码时出现失误。
3.2缩进和对齐
∙程序的分界符"{"和"}"应独占一行并且位于同一列,同时与引用它们的语句左对齐。
∙"{}"之内的代码块在"{"右边一个四个空格处左对齐。
如果出现嵌套的"{}",则使用缩进对齐。
例如:
void
Function(intx)
{
CSessionLockiLock(mxLock);
for(初始化;终止条件;更新)
{
//...
}
try
{
//...
}
catch(constexception&err)
{
//...
}
catch(...)
3.3最大长度
代码行最大长度宜控制在70至80个字符以内。
代码行不要过长,否则眼睛看不过来,也不便于打印(2009年更新:
随着GUI开发环境和高分宽屏的普及,此规则可以视情况适当放宽)。
3.4长行拆分
长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。
拆分出的新行要进行适当的缩进,使排版整齐,语句可读。
例如:
if ((very_longer_variable1>=very_longer_variable2)
&&(very_longer_variable3<=very_longer_variable4)
&&(very_longer_variable5<=very_longer_variable6))
{
DoSomething();
}
3.5 空格的使用
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))等。
例如:
void Func1(intx,inty,intz); // 良好的风格
void Func1(intx,inty,intz); // 不良的风格
//===========================================================
if (year>=2000) // 不良的风格
if(year>=2000) // 不良的风格
if ((a>=b)&&(c<=d)) // 良好的风格
if(a>=b&&c<=d) // 不良的风格
//===========================================================
for (i=0;i<10;i++) // 良好的风格
for(i=0;i<10;i++) // 不良的风格
for (i=0;I<10;i++)// 过多的空格
//===========================================================
x =a
a:
b; // 良好的风格
x=a
a:
b; // 不好的风格
//===========================================================
int* x=&y; // 良好的风格
int *x=&y; // 不良的风格
//===========================================================
array[5] =0; // 不要写成 array[5]=0;
a.Function(); // 不要写成 a.Function();
b->Function(); // 不要写成 b->Function();
3.6修饰符的位置
为便于理解,应当将修饰符"*"和"&"紧靠数据类型。
例如:
char*name;
int*x;
int y; //为避免y被误解为指针,这里必须分行写。
int*Function(void*p);
3.7注释
∙注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不可放在下方。
∙边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。
不再有用的注释要删除。
∙注释应当准确、易懂,防止注释有二义性。
错误的注释不但无益反而有害。
∙当代码比较长,特别是有多重嵌套时,应当在一些段落的结束处加注释,便于阅读。
4注释分类说明
4.1源文件头部注释
Ø 列出:
版权、作者、编写日期和描述。
Ø示例:
/*************************************************
Copyright:
bupt
Author:
Date:
2010-08-25
Description:
描述主要实现的功能
**************************************************/
每行不要超过80个字符的宽度。
4.2函数头部注释
Ø 列出:
函数的目的/功能、输入参数、输出参数、返回值、等。
Ø示例:
下面这段函数的注释比较标准,当然,并不局限于此格式,但上述信息建议
要包含在内。
/*************************************************
Function:
//函数名称
Description:
//函数功能、性能等的描述
Input:
//输入参数说明,包括每个参数的作
//用、取值说明及参数间关系。
Output:
//对输出参数的说明。
Return:
//函数返回值的说明
Others:
//其它说明
*************************************************/
如果其命名不是充分自注释的,必须加以注释。
对数据结构的注释应放在其上方相邻位置,不可放在下面;对结构中的每个域的注释放在此域的右方。
Ø示例:
可按如下形式说明枚举/数据/联合结构。
/*sccpinterfacewithsccpuserprimitivemessagename*/
enumSCCP_USER_PRIMITIVE
{
N_UNITDATA_IND,/*sccpnotifysccpuserunitdatacome*/
N_NOTICE_IND, /*sccpnotifyusertheNo.7networkcannot*/
/*transmissionthismessage*/
N_UNITDATA_REQ,/*sccpuser'sunitdatatransmissionrequest*/
};
4.3全局变量的注释
Ø 包括对其功能、取值范围、哪些函数或过程存取它以及存取时注意事项等的说明。
示例:
/*TheErrorCodewhenSCCPtranslate*/
/*GlobalTitlefailure,asfollows*/ //变量作用、含义
注释总是加在程序的需要一个概括性说明或不易理解或易理解错的地方。
注释语言应该简练、易懂而又含义准确,避免二义性;所采用的语种首选是中文,如有输入困难、编译环境限制或特殊需求也可采用英文。
注释应与其描述的代码相近,对代码的注释统一放在其上方,避免在一行代码或表达式中间使用注释。
上方注释与其上面的代码用空行隔开(较紧凑的代码除外)。
注意:
注释应与所描述内容进行同样的缩进。
附:
QT控件命名规范
控件名称
命名前缀
示例命名
QPushButton
btn
btnOK
QToolButton
toolBtn
toolBtnSelect
QRadioButton
radioBtn
radioBtn
QCheckBox
checkBox
checkBoxMan
QListView
listView
listViewFriend
Q