for(initialization;condition;update)
{
dosomething();
}
//空行
other();
for(initialization;condition;update)
dosomething();
other();
1.3.空格
1.3.1.关键字之后要留空格:
const,virtual,inline,if,while,for
1.3.2.函数名之后不要留空格
1.3.3.“(”向后紧跟“,”,“、”,“.”,“;”,“)”向前紧跟
1.3.4.“,”后要留空格,“”;之后如果不是一行的结束,后面要留空格
1.3.5.赋值操作符,比较,算术,逻辑,第二元操作符前后加空格
1.3.6.一元操作符!
、~、++、--、—等前后不加空格
1.3.7.像[]、“.”、—>等前后不加空格
例:
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();
1.4.长行拆分
1.4.1.代码行最长度宜控制在70到80个字符以内,代码行不宜过长
1.4.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();
}
修饰的位置
1.4.3.修饰符*和&应靠近变量名:
char*name;int*x,y
1.5.类的版式
1.5.1.将public写前面,protect在其后,private写后面,以行为中心
1.5.2.变量在函数声明之后
1.5.3.逻辑相关的函数排列在一起
例:
classA
{
public:
voidFunc1(void);
voidFunc2(void);
//空行
inti,j;
…
protect:
voidFunc3(void);
voidFunc4(void);
floatx,y;
…
private:
voidFunc5(void);
voidFunc6(void);
//空行
stringa,b;
…
}
2.代码注释规则(范例文件)
2.1.每个文件头
2.1.1.阐述该文件包含的内容
2.1.2.对include的文件要注释
2.2.每个函数的头部
2.2.1.注明函数名称、函数说明、输入参数和返回值
2.3.每个类(即头文件)
2.3.1.注明类的用途
2.4.函数当中需要注释不易明折的地方
2.4.1.算法
2.4.2.设计思想
2.4.3.独到的用处
2.5.原则
2.5.1.注释和代码一样要及时更新
2.5.2.注释不是代码的重复
2.5.3.全部使用“//”方式注释
2.5.4.注释尽量不要单独占一行
2.5.5.注释要简洁、明了
2.5.6.边写代码边写注释,MFC自带的代码注释不用修改
2.5.7.对于所有有物理含义的变量、常量,如果其命名不是充分自注释的,在声明时都必须加以注释,说明其物理含义。
变量、常量、宏的注释应放在其上方相邻位置或右方。
2.5.8.将注释与其上面的代码用空行隔开
/*
*函数名称:
*函数说明:
*输入参数:
*返回值:
*/
voidFunction(floatx,floaty,floatz)
{
…
}
if(…)
{
…
while(…)
{
…
}//endofwhile
…
}//endofif
3.命名规范
3.1.共性规则
3.1.1.不可用汉语拼音,用英文、用词应准确
3.1.2.应用大小写混排方式,如:
AddChild
3.1.3.程序中不要出现仅靠大小写区分的相似的标识符
3.1.4.程序中不要出现标识符完全相同的简单变量和全局变量
3.1.5.变量名应当使用“名词”或“形容词+名词”,如:
floatfValue;
floatfOldVlaue;
floatfNewValue;
3.1.6.函数名应使用动词或动词+名词,如:
voidGetValue();
3.1.7.用正确的反义词组命名具有互斥定义的变量或相反动作的函数,如:
intnMinValue;
intnMaxValue;
intnSetValue;
intnGetValue;
3.1.8.对于借来的代码,不改变其命名
3.2.命名规则
3.2.1.类名和函数名用大写字母开头的单词组合而成,如:
classNode;
classLeafNode;
voidSetValue();
3.2.2.变量和参数用小写字母开头的单词组合而成,如:
BOOFbflag,;
intnDrawMode;
floatfHeight;
stringstrTitle;
charcSource;
3.2.3.常量全用大写字母,用下划线分割单词DDD_SAF
3.2.4.静态变量前加前缀s_,如:
Staticints_initValue;
3.2.5.如果不得已需要全局变量,全局变量前加前缀g_,如:
intg_howManyPeople
3.2.6.类的数据成员加前缀m_,如:
voidobjet:
:
SetValue(intwidth,intheight)
{
m_width=width;
m_height=height
}
3.2.7.适当使用匈牙利命名法
4.程序规范
4.1.语句
4.1.1.对运算符很多的情况下,应该使用括号区分运算的顺序
例如:
word=(high<<8)|low
if((a|b)&&(a&c))
优先级
运算符
结合律
从
高
到
低
排
列
()[]->.
从左至右
!
~++--(类型)sizeof
+-*&
从右至左
*/%
从左至右
+-
从左至右
<<>>
从左至右
<<=>>=
从左至右
==!
=
从左至右
&
从左至右
^
从左至右
|
从左至右
&&
从左至右
||
从右至左
?
:
从右至左
=+=-=*=/=%=&=^=
|=<<=>>=
从左至右
4.1.2.if语句
4.1.2.1.布尔变量与零值比较时,不可将布尔变量直接与TRUE、FALSE或者1、0进行比较
假设布尔变量名字为flag,它与零值比较的标准if语句如下:
if(flag)//表示flag为真
if(!
flag)//表示flag为假
其它的用法都属于不良风格,例如:
if(flag==TRUE)
if(flag==1)
if(flag==FALSE)
if(flag==0)
4.1.2.2.整型变量与零值比较时,应当将整型变量用“==”或“!
=”直接与0比较
假设整型变量的名字为value,它与零值比较的标准if语句如下:
if(value==0)
if(value!
=0)
不可模仿布尔变量的风格而写成
if(value)//会让人误解value是布尔变量
if(!
value)
4.1.2.3.浮点变量与零值比较时,不可将浮点变量用“==”或“!
=”与任何数字比较
千万要留意,无论是float还是double类型的变量,都有精度限制。
所以一定要
避免将浮点变量用“==”或“!
=”与数字比较,应该设法转化成“>=”或“<=”形式。
假设浮点变量的名字为x,应当将
if(x==0.0)//隐含错误的比较
转化为
if((x>=-EPSINON)&&(x<=EPSINON))
其中EPSINON是允许的误差(即精度)。
4.1.2.4.指针变量与零值比较时,应当将指针变量用“==”或“!
=”与NULL比较
指针变量的零值是“空”(记为NULL)。
尽管NULL的值与0相同,但是两者意义不同。
假设指针变量的名字为p,它与零值比较的标准if语句如下:
if(p==NULL)//p与NULL显式比较,强调p是指针变量
if(p!
=NULL)
不要写成
if(p==0)//容易让人误解p是整型变量
if(p!
=0)
或者
if(p)//容易让人误解p是布尔变量
if(!
p)
4.1.2.5.程序中有时会遇到if/else/return的组合,应该将如下不良风格的程序
例如:
if(condition)
returnx;
returny;
改写为
if(condition)
{
returnx;
}
else
{
returny;
}
或者改写成更加简练的
return(condition?
x:
y);
4.1.3.循环语句
4.1.3.1.在多生循环中,如果有可能,应将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨越循环层的次数
例:
for(row=0;row<100;row++)
{
for(col=0;col<5;col++)
{
sum=sum+a[row][col];
}
}
for(col=0;col<5;col++)
{
for(row=0;row<100;row++)
{
sum=sum+a[row][col];
}
}
4.1.3.2.如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面
例:
for(i=0;i{
if(condition)
DoSomething();
else
DoOtherthing();
}
if(condition)
{
for(i=0;iDoSomething();
}
else
{
for(i=0;iDoOtherthing();
}
4.1.3.3.switch语句的case分支如果不要break,要注释,同时要加default语句
例:
switch语句的基本格式是:
switch(variable)
{
casevalue1:
…
break;
casevalue2:
…
break;
…
default:
…
break;
}
4.2.常量
4.2.1.只使用const常量,不使用宏常量(自定义)
4.2.2.需要对外公开的常量放在头文件(*.h)中,不需要对外公开的常量放在定义文件(*.cpp)的头部
4.2.3.不能在类中初始化const常量,只能通过枚举常量来解决
例:
错误:
classA
{
constintSIZE=100;(错误)
intarray[SIZE];
}
正确:
classA
{…
enum{SIZE1=100,SIZE2=200};//枚举常量
intarray1[SIZE1];
intarray2[SIZE2];
};
4.3.函数
4.3.1.参数规则
4.3.1.1.参数书写要完整,不能只写类型,不写参数名,如果函数没有参数,用void填充
例如:
voidSetValue(intwidth,intheight);//良好的风格
voidSetValue(int,int);//不良的风格
floatGetValue(void);//良好的风格
floatGetValue();//不良的风格
4.3.1.2.如果参数是指针,且仅作输入用,则应在类型前加const,以免指针在函数体内被意外修改;一般地,应将目的参数放在前面,源参数放在后面
例:
voidStrngCopy(char*strlDestintion,consthar*StrSource)
4.3.1.3.如果参数以值传递的方式传递对象,则宜改用“const&”方法传递,能提高效率
4.3.1.4.避免函数有太多的参数,参数个数应尽量控制在5个以内
4.3.1.5.尽量不要使用类型和数目不确定的参数
例:
intprintf(constcha*format[,argument]…);
4.3.1.6.对于内置类型参数要传值
4.3.1.7.需要传指针不传引用的情形
a)内部需要用NULL状态(引用没有NULL状态)
b)若参数是被new出来的,是将在函数内被释放
4.3.2.返回值规则
4.3.2.1.函数的输出值结果用输出参数(一般为指针)获得,状态用return返回
4.3.2.2.如果返回一个对象,一般用引用传递,但有的情况下必须用值传递
例如:
classString
{
…
//赋值函数
String&operate=(constString&other);
//相加函数,如果没有friend修饰则只许有一个右侧参数
friendStringoperate+(constString&s1,constString&s2);
private:
char*m_data;
}
String的赋值函数operate=的实现如下:
String&String:
:
operate=(constString&other)
{
if(this==&other)
{
return*this;
}
deletem_data;
m_data=newchar[strlen(other.data)+1];
strcpy(m_data,other.data);
return*this;//返回的是*this的引用,无需拷贝过程
}
对于赋值函数,应当用“引用传递”的方式返回String对象。
如果用“值传递”的方式,虽然功能仍然正确,但由于return语句要把*this拷贝到保存返回值的外部存储单元之中,增加了不必要的开销,降低了赋值函数的效率。
例如:
Stringa,b,c;
…
a=b;//如果用“值传递”,将产生一次*this拷贝
a=b=c;//如果用“值传递”,将产生两次*this拷贝
String的相加函数operate+的实现如下:
Stringoperate+(constString&s1,constString&s2)
{
Stringtemp;
deletetemp.data;//temp.data是仅含‘\0’的字符串
temp.data=newchar[strlen(s1.data)+strlen(s2.data)+1];
strcpy(temp.data,s1.data);
strcat(temp.data,s2.data);
returntemp;
}
对于相加函数,应当用“值传递”的方式返回String对象。
如果改用“引用传递”,那么函数返回值是一个指向局部对象temp的“引用”。
由于temp在函数结束时被自动销毁,将导致返回的“引用”无效。
例如:
c=a+b;
此时a+b并不返回期望值,c什么也得不到,流下了隐患。
4.3.2.3.尽量保持函数只有唯一出口
4.3.2.4.若函数返回状态,尝试用枚举作类型
4.3.2.5.当函数返回引用或指针时,用文字描述其有效性
4.3.2.6.禁止成员函数返回成员的引用或指针
4.3.3.函数内部规则
4.3.3.1.在函数体的“入口处”,对参数的有效性进行检查,应正确使用断言(assert),断言assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况
例:
void*memcpy(void*pvTo,constvoid*pvFrom,size_tsize)
{
assert((pvTo!
=NULL)&&(pvFrom!
=NULL));//使用断言
byte*pbTo=(byte*)pvTo;//防止改变pvTo的地址
byte*pbFrom=(byte*)pvFrom;//防止改变pvFrom的地址
while(size-->0)
*pbTo++=*pbFrom++;
returnpvTo;
}
4.3.3.2.在函数的“出口处”,应对return语句的正确性和效率进行检查
4.3.3.3.return语句不可返回指向“找内存”(内部变量)的指针或引用,因为该内存在函数体结束时被自动销毁
4.3.3.4.要搞清楚返回的究竟是值、指针,还是引用
4.3.3.5.如果函数返回值是一个对象,要考虑return语句的效率
4.3.4.通用规则
4.3.4.1.函数的功能要单一,不要设计多用途函数
4.3.4.2.函数体规模要小,尽量控制在100行代码以内,不包括注释和空格行
4.3.4.3.尽