for(initialization;condition;update)
{
dosomething();
}
//空行
other();
for(initialization;condition;update)
dosomething();
other();
示例3-4(a)风格良好的代码行示例3-4(b)风格不良的代码行
3.2.3.代码行内的空格
1)关键字之后要留空格。
象const、virtual、inline、case等关键字之后至少要留一个空格,否则无法辨析关键字。
象if、for、while等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。
2)函数名之后不要留空格,紧跟左括号‘(’,以与关键字区别。
3)‘(’向后紧跟,‘)’、‘,’、‘;’向前紧跟,紧跟处不留空格。
4)‘,’之后要留空格,如Function(x,y,z)。
如果‘;’不是一行的结束符号,其后要留空格,如for(initialization;condition;update)。
5)赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如“=”、“+=”“>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格。
6)一元操作符如“!
”、“~”、“++”、“--”、“&”(地址运算符)等前后不加空格。
7)象“[]”、“.”、“->”这类操作符前后不加空格。
8)对于表达式比较长的for语句和if语句,为了紧凑起见可以适当地去掉一些空格,如for(i=0;i<10;i++)和if((a<=b)&&(c<=d))
示例:
VoidFunc1(intx,inty,intz);//良好的风格
VoidFunc1(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-5代码行内的空格
3.2.4.对齐和缩进
1)程序的分界符‘{’和‘}’应独占一行并且位于同一列,同时与引用它们的语句左对齐。
{}之内的代码块在‘{’右边数格处左对齐。
2)实现(成员)函数时,可能需要几个小功能模块。
每个小模块之间最好用{}间隔起来,以保证每个小模块之间的局部变量互不干扰。
尽量避免出现各个小模块之间公用的局部变量,因为上个模块可能会在局部变量中留下下个模块不希望的值
示例:
…
voidCmyClass:
:
DoSomething(...)
{
inti=0,j=0,k=0;
//小功能模块1使用i,j,k
//小功能模块2使用i,j
//小功能模块3使用k
}
voidCMyClass:
:
DoSomething(...)
{
{
inti=0,j=0,k=0;
//小功能模块1使用i,j,k
}
{
inti=0,j=0;
//小功能模块2使用i,j
}
{
intk=0;//小功能模块3使用k
}
}
示例3-6(a)局部变量相互干扰示例3-6(b)独立的局部变量
3)统一采用Tab键采用4空格的缩进格式,缩进要用Tab键,不要采用空格。
3.2.5.长行拆分
1、代码行最大长度宜控制在70至80个字符以内。
代码行不要过长,否则眼睛看不过来,也不便于打印。
2、长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。
拆分出的新行要进行适当的缩进,使排版整齐,语句可读。
示例:
if((very_longer_variable1>=very_longer_variable12)
&&(very_longer_variable3<=very_longer_variable14)
&&(very_longer_variable5<=very_longer_variable16))
{
dosomething();
}
virtualCmatrixCMultiplyMatrix(CMatrixleftMatrix,
CMatrixrightMatrix);
for(very_longer_initialization;
very_longer_condition;
very_longer_update)
{
dosomething();
}
示例3-7长行的拆分
3.2.6.修饰符的位置
修饰符*和&统一靠近该靠近变量名,而不靠近数据类型。
若将修饰符*靠近数据类型,例如:
int*x;从语义上讲此写法比较直观,即x是int类型的指针。
但上述写法的弊端是容易引起误解,例如:
int*x,y;此处y容易被误解为指针变量。
3.2.7.注释
1、同一采用英语注释。
2、重要的算法,复杂的函数体的开头,必须要有注释加以说明。
3、代码维护过程中,任何修改必须注明修改者,时间和简单的原因,源代码如果不是确定不要的,应当注释掉,而不是删除。
4、注释要简明扼要,准确、易懂,防止注释有二义性。
如代码本来就是清楚的,则不必加注释。
5、边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。
不再有用的注释要删除。
6、尽量避免在注释中使用缩写,特别是不常用缩写。
7、注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不可放在下方。
8、当代码比较长,特别是有多重嵌套时,应当在一些段落的结束处加注释,便于阅读。
3.2.8.类的版式
类的书写顺序:
:
类名
{
public类型的成员变量
public类型的成员函数
protected类型的成员变量
protected类型的成员函数
private类型的成员变量
private类型的成员函数
};
把公用的成员放在最前面,可以方便使用你这个类的同事找到他需要的外部可以调用的公用成员函数(或许还有极少的成员变量),他并不关心这个类的内部数据和详细实现。
各种类型的成员中间可以按其功能属性分组,各组间用空行隔开。
类的声明和其inline函数放在头文件(.h)中。
不要在类的声明中直接定义,而放在类的声明之后。
在头文件中加上宏定义保证该类只被include一次。
类的非inline成员函数放在同一个模块(.cpp)中实现。
例如:
你定义的一个类CMyClass,我们来看看它的头文件.h格式:
//MyClass.h:
declaratingtheclassCMyClass
//
#ifndef__MYCLASS_H__
#define__MYCLASS_H__
CMyClass
{
…
};
//下面定义CMyClass的所有inline函数
…
#endif//__MYCLASS_H__
下面是实现文件.cpp的格式:
//MyClass.cpp:
definingtheclassCMyClass
//
#include“stdafx.h”
#include“MyClass.h”
注意:
类的实现文件(.CPP)头上在#include之后要加上下面的语句,可以发现程序中的“MemoryLeak”。
#ifdef_DEBUG
#definenewDEBUG_NEW
#undefTHIS_FILE
staticcharTHIS_FILE[]=__FILE__;
#endif
//下面定义CMyClass的所有非inline函数...
3.3.命名规则
3.3.1.一般命名规则
1、标识符应当直观且可以拼读,可望文知意,不必进行“解码”。
标识符最好采用英文单词或其组合,便于记忆和阅读。
切忌使用汉语拼音来命名。
程序中的英文单词一般不会太复杂,用词应当准确。
例如不要把CurrentValue写成NowValue。
2、标识符的长度应当符合“min-length&&max-information”原则。
一般来说,长名字能更好地表达含义,所以函数名、变量名、类名长达十几个字符不足为怪。
那么名字是否越长约好?
不见得!
例如变量名maxval就比maxValueUntilOverflow好用。
单字符的名字也是有用的,常见的如i,j,k,m,n,x,y,z等,它们通常可用作函数内的局部变量。
3、程序中不要出现仅靠大小写区分的相似的标识符。
4、程序中不要出现标识符完全相同的局部变量和全局变量,尽管两者的作用域不同而不会发生语法错误,但会使人误解。
5、变量的名字应当使用“名词”或者“形容词+名词”。
例如:
floatvalue;
floatoldValue;
floatnewValue;
6、全局函数的名字应当使用“动词”或者“动词+名词”(动宾词组)。
类的成员函数应当只使用“动词”,被省略掉的名词就是对象本身。
例如:
DrawBox();//全局函数
box->Draw();//类的成员函数
7、用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。
例如:
intminValue;
intmaxValue;
intSetValue(…);
intGetValue(…);
8、尽量避免名字中出现数字编号,如Value1,Value2等,除非逻辑上的确需要编号。
3.3.2.变量和常量命名
变量的命名要遵循匈牙利命名法。
这样可由命名得知变量的类型和用途,它通过在数据和函数名中加入额外的信息以增进程序员对程序的理解。
1、变量和参数用小写字母开头的单词组合而成。
类型缩写约定如下:
n----->int
u----->UINT
w----->WORD
dw----->DWORD
b----->BOOL
by----->BYTE
ch----->CHAR
sz----->CHARARRAY
l----->LONG
f----->float
d----->double
q----->SINT64
uq----->UINT64
v----->void
fn----->function
h----->HANDLE
rect----->RECT,Crect
pt----->POINT,Cpoint
2、常量全用大写的字母,用下划线分割单词。
例如:
constintMAX=100;
constintMAX_LENGTH=100;
3、静态变量加前缀s_
4、指针前面加上p
5、全局变量加上前缀g_
6、类中的成员变量加上前缀m_
3.3.3.类和函数的命名
1、类名和函数名用大写字母开头的单词组合而成。
2、所有的类以C作为前缀,对话框的类以Dlg作为后缀。
3.3.4.ID的命名
VC中用到大量的资源来管理ID,对于这些ID的命名必须遵循一定的命名规则定义。
有关该部分的命名,有待进一步整理和统一。
四、开发规范
4.1.常量的使用
程序中通常情况下,不允许直接在程序中填写数字或字符串,否则的话,程序的可读性极差,以后的维护人员很难理解程序中,一个“1”或“3”的含义;同时难以维护,如果该数字或字符串需要改变,一个地方一个地方的改正即麻烦又不安全。
所以必须将程序中的直接出现的数字或字符串,以常量的形式加以定义。
4.1.1.const与#define
在C++程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量。
4.1.2.常量定义规则
1、需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部。
为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。
2、如果某一常量与其它常量密切相关,应在定义中包含这种关系,而不应给出一些孤立的值。
例如:
constfloatRADIUS=100;
constfloatDIAMETER=RADIUS*2;
3、将用到的字符串放入资源文件中,以便文种的翻译与转换。
例如:
Good:
CStringStr;
Str.LoadString(IDS_TitleName);
SetWindowText(Str);
...
AfxMessageBox(IDS_Error_String);
Bad:
SetWindowText(“WindowTitleName”);
...
AfxMessageBox(“InvalidNumber”);
4.1.3.类中的常量
有时我们希望某些常量只在类中有效。
由于#define定义的宏常量是全局的,不能达到目的,于是想当然地觉得应该用const修饰数据成员来实现。
const数据成员的确是存在的,但其含义却不是我们所期望的。
const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。
不能在类声明中初始化const数据成员。
以下用法是错误的,因为类的对象未被创建时,编译器不知道SIZE的值是什么。
classA
{…
constintSIZE=100;//错误,企图在类声明中初始化const数据成员
intarray[SIZE];//错误,未知的SIZE
};
const数据成员的初始化只能在类构造函数的初始化表中进行,例如
classA
{…
A(intsize);//构造函数
constintSIZE;
};
A:
:
A(intsize):
SIZE(size)//构造函数的初始化表
{
…
}
Aa(100);//对象a的SIZE值为100
Ab(200);//对象b的SIZE值为200
怎样才能建立在整个类中都恒定的常量呢?
别指望const数据成员了,应该用类中的枚举常量来实现。
例如
classA
{…
enum{SIZE1=100,SIZE2=200};//枚举常量
intarray1[SIZE1];
intarray2[SIZE2];
};
枚举常量不会占用对象的存储空间,它们在编译时被全部求值。
枚举常量的缺点是:
它的隐含数据类型是整数,其最大值有限,且不能表示浮点数(如PI=3.14159)。
4.2.变量的使用
1、禁止使用全局性的变量
在面向对象的系统中,应该是所有的一切都是基于对象的。
原来习惯的全局函数完全可以放在一个类中。
必须使用全局变量和函数,必须提出申请。
2、变量一定要初始化
任何变量使用前,必须初始化。
如果你声明一个变量,千万不要以为编译器会自动将之赋零值!
你随手给它赋个初值并不麻烦,却会使程序更可靠,何乐而不为呢?
例如:
floatfTemp=0.0;
3、成员变量存取说明符的使用
类中的成员变量尽可能地不定义为public变量,而通过相应的