C编程规范总结

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C++编程规范
1.简介
2.为规范项目中以C++ 为基础语言的代码风格,提到代码的强壮性、可保护性,提高开发效率,
3.特拟定本规范。

4.本规范拥有法律效力,除特别说明,也许项目组获得职权部门书面赞同,本规范必定执行。

5.目的
6.宣告本规范的目的是:
7.增加代码的强壮性、可读性、易保护性;减稀有经验和无经验开发人员编程所需的脑力工作;
8.在项目范围内一致代码风格;
9.经过人为以及自动的方式对最后软件应用质量标准;
10.使新的开发人员快速适应项目环境;
11.支持项目资源的复用:赞同开发人员从一个项目地域(或子项目团队)搬动到另一个,而不需要重新适应新的子项目团队的氛围。

12.适用范围
13.本规范适用于公司全部以C++语言为基础的平台下开发的项目。

14.归纳
15.本规范包括内容:
16.如何组织项目代码;
17.编程风格(如何编写实质的源代码);
18.如何记录源代码;
19.代码内名称和源文件所使用的命名约定;
20.何时使用某些语言构造以及何时应防范某些语言构造。

21.基根源则
22.清楚、可理解的源代码是影响软件可靠性和可保护性的主要要素。

清楚、可理解的代码能够表示为以下三个简单的基础原理:
23.最小混淆:软件的生计期中,源代码的读远比写多,规范、标准更是这样。

理想情况下,源代码读起来应该象英语同样描述了所要做的事,这同时还带来了它执行的好处。

程序实质上是为人编写,而不是为计算机编写的。

阅读代码是一个复杂的脑力过程,它可由一致标准来简化,在本文中还指最小混淆原则。

整个项目中统同样式是软件开发团队在编程标准上完成一致的主要原因,它不应视为一种处分或对创办性和生产力的阻拦。

24.保护的唯一点:只要可能,设计决策就应在源中只表述一点,它的多数结果应程序化的派生于此点。

不遵守这一原则严重损害了可保护性、可靠性和可理解性。

25.最小搅乱:防范将源代码与可视搅乱(如内容较少或对理解软件目的不起作用的信息)相混淆:
26.所表达的精神但是于苛刻;而对正确安全的使用语言特点供应指导。

优秀软件的重点在
于:
认识每一个特点以及它的限制和潜藏的危险;的确认识此特点可安全的使用于哪一个环境中;做出使用高度可视特点的决定;
在合适的地方小心适当的使用特点。

3文件构造


C
++/C
程序平时分为两个文件。

一个文件用于保留
程序的声明(
decl
aration
),称为头

件。

另一个文件用于保留程
序的实现(
impleme
ntation
),称
为定义(
de
finiti
on
)文件。

1.C++/C程序的头文件以“.h”为后缀,C程序的定义文件以“.c”为后缀,C++程序的定义文件平时以“.cpp”为后缀(也有一些系统以“.cc”或“.cxx”为后缀)。

2.版权和版本的声明
3.版权和版本的声明位于头文件和定义文件的开头,主要内容有:
4.版权信息。

5.文件名称,表记符,大纲。

6.当前版本号,作者/更正者,完成日期。

7.版本历史信息。

8./**
9.Copyright(c)2004,光庭导航数据(武汉)有限公司
10.Allrightsreserved.
11.*
12.文件名称:
13.大纲:简要描述本文件的内容
14.*
15.当前版本:
16.作者:输入作者(或更正者)名字
17.完成日期:2004年×月×日
18.*
19.取代版本:
20.原作者:输入原作者(或更正者)名字
21.完成日期:2004年月日
22.**/
23.【说明】
24.关于类的版权和版本声明要保持C++工程和RoseUML模型的一致,鉴于在RoseUML模型中编写这些声明比较麻烦以致工作量增加,因此能够在VC中使用“VC助手”工具帮助快速编写该类的版权和版本声明,在VC中编写好声明后要将该C++工程反转到RoseUML模型中,以保持C++工程和RoseUML模型的一致。

25.使用VC助手的方法:
26.点击助手工具栏的Options按钮
27.点击Completion页面的Edit按钮
28.找到
/**:
/************************************************************************/
/*?*/
/************************************************************************/
更正为:
/**:
/**
Copyright(c)2004,光庭导航数据(武汉)有限公司
Allrightsreserved.
?
文件名称:
大纲:简要描述本文件的内容
*
当前版本:
作者:输入作者(或更正者)名字
完成日期:2004年月日
*
取代版本:
原作者:输入原作者(或更正者)名字
完成日期:2004年月日
**/
使用方法:在VC中输入"/**"等待出现提示,尔后回车即出现类说明。

【提示3-1-1】经过上述方法能够在“VC助手”中编写各种模板以提高编写代码的效率。

头文件的构造
头文件由三部分内容组成:
头文件开头处的版权和版本声明。

预办理块。

函数和类构造声明等。

【规则3-2-1
】为了防范头文件被重复引用,应该用ifndef/define/endif构造产生预办理块。

【规则3-2-2】用#include <>格式来引用标准库的头文件(编译器将从标准
库目录开始

寻)。

【规则】用“”格式来引用非标准库的头文件(编译器将从
3-2-3#include用户的工作目录开始找寻)。

【规则
3-2-4】头文件中只存放“声明”而不存放“定义”
在C++语法中,类的成员函数能够在声明的同时被定义,而且自动成为内联函数。

这诚然会带来书写上的方便,但却造成了风格不一致,弊大于利。

建议将成员函数的定义与声明分开,无论该函数体有多么小。

即即是缺省的构造函数和析构函数也不同样意在头文件中定义。

【规则3-2-5】不倡议使用全局变量,尽量不要在头文件中出现象externintvalue 这类声
明。

若是要保留全局变量,那么全局变量要保留在一个类中。

定义文件的构造
定义文件有三部分内容:
定义文件开头处的版权和版本声明。

对一些头文件的引用。

程序的实现体(包括数据和代码)
头文件的作用
经过头文件来调用库功能。

在很多场合,源代码不便(或严禁)向用户宣告,只要向用户供应头文件和二进制的库即可。

用户只要要依照头文件中的接口声明来调用库功能,而不用关心接口怎么实现的。

编译器会从库中提取相应的代码。

头文件能加强种类安全检查。

若是某个接口被实现或被使用时,其方式与头文件中的声
明不一致,编译器就会指出错误,这一简单的规则能大大减少程序员调试、改错的负担。

目录构造
若是一个软件的头文件数目比很多(如高出十个),平时应将头文件和定义文件分别保留于不同样
的目录,以便于保护。

比方可将头文件保留于include目录,将定义文件保留于source目录(能够是多级目录)。

若是某些头文件是私有的,它不会被用户的程序直接引用,则没有必要公开其“声明”。

为了加
强信息隐蔽,这些私有的头文件能够和定义文件存放于同一个目录。

程序的版式
版式诚然不会影响程序的功能,但会影响可读性。

程序的版式追求清楚、雅观,是程序风格的
重要组成要素。

空行
空行起着分开程序段落的作用。

空行得体(但是多也但是少)将使程序的布局更加清楚。

空行
不会浪费内存。

【规则4-1-1】在每个类声明此后、每个函数定义结束此后都要加一行空行。

【规则4-1-2】在一个函数体内,逻揖上亲近相关的语句之间不能够加空行,而在逻辑上有区其余
段落之间必定加空行。

比方:
、“->”这类操作符前后不加空格。

对齐
【规则
4-4-1】程序的分界符‘}’应独占一行而且与引用它们的语句左对齐。

‘{’能够另
起一行,于‘}’左对齐,也能够放在引用它们的语句后边。


规则4-4-2】{}之内的代码块在引用语句的右边间隔一个“Tab”处左对齐。

长行
拆分
【规则4-5-1】代码行最大长度不能够高出80个字符。

【规则4-5-2】长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。

拆分出的新行要进行合适的缩进,使排版整齐,语句可读。

修饰符的地址
【规则4-6-1】修饰符凑近数据种类和变量名。

比方:
int* x;

左至右
!
+
+
-
-
(种类)
sizeof

右至左
+*&
*%
从左至右
+
从左至右
< <
>
>

左至右
<
<
=>
>
=

左至右
= =
!
=

左至右
&
从左至右
^
从左至右
|
从左至右
& &
从左至右
| |
从右至左
? :
从右至左
= += -= *= /= %= &= ^= 从左至右
|= <<= >>=
表6-1运算符的优先级与结合律
【规则6-1-1】若是代码行中的运算符比很多,用括号确定表达式的操作次序,防范使用默认的优先级。

由于将表6-1熟记是比较困难的,为了防范产生歧义并提高可读性,应该用括号确定表达式的
操作次序。

复合表达式
如a=b=c=0 这样的表达式称为复合表达式。

赞同复合表达式存在的原因是:(1)书写简短;
(2)能够提高编译效率。

但要防范滥用复合表达式。

同时建议尽量防范使用复合表达式。

【规则6-2-1】不要编写太复杂的复合表达式。

【规则6-2-2】不要有多用途的复合表达式。

【规则6-2-3】不要把程序中的复合表达式与“真切的数学表达式”混淆。

if 语句
if语句是C++/C语言中最简单、最常用的语句,但是很多程序员用隐含错误的方式写if语句。

本节以“与零值比较”为例,张开谈论。

布尔变量与零值比较
【规则6-3-1】不能将布尔变量直接与 TRUE、FALSE 也许1、0进行比较,而是直接判断该
值。

整型变量与零值比较
【规则6-3-2】应该将整型变量用“
==”或“!=”直接与0比较。

不能模拟布尔变量的风格而
写。

浮点变量与零值比较
【规则6-3-3】不能将浮点
变量用“==”或“!=”与任何数字比较。

千万要留意,无论是float还是double种类的变量,都有精度限制。

因此必然要防范将浮点变
量用“==”或“!=”与数字比较,应该想法转变为“>=”或“<=”形式。

指针变量与零值比较
【规则6-3-4】应该将指针
变量用“==”或“!=”与NULL比较。

循环语句的效率
C++/C循环语句中,for语句使用频率最高, while语句其次,do语句很少用。

本节重点论述循环体的效率。

提高循环体效率的基本方法是降低循环体的复杂性。

【建议6-4-1】在多重循环中,若是有可能,应该将最长的循环放在最内层,最短的循环放在最外
层,以减少CPU跨切循环层的次数。

【建议6-4-2】若是循环体内存在逻辑判断,而且循环次数很大,宜将逻辑判断移到循环
体的外面。

示例6-4(c)的程序比示例 6-4(d)多执行了N-1次逻辑判断。

而且由于前者老要进行逻辑判断,
打断了循环“流水线”作业,使得编译器不能够对循环进行优化办理,降低了效率。

若是N特别大,
最好采用示例6-4(d)的写法,能够提高效率。

若是N特别小,两者效率差别其实不明显,采用示例6-4(c)的写法比较好,由于程序更加简短。

for(i=0;i<N;i++)
{
if(condition) DoSomething();if(condition) {
for(i=0;i<N;i++) DoSomething();
e lse
}DoOtherthing();
}
else
{
for(i=0;i<N;i++)
DoOtherthing();
}

6 -4(c)效率低但程序简短表6
-4(d)
效率高但程序不简短
fo
r语句的循环控制变量
【规则6-5-1】不能在for循环体内更正循环变量,防范for循环失去控制。

【建议6-5-1】建议for语句的循环控制变量的取值采用“半开半闭区间”写法。

如:x值属于半开半闭区间“0=<x<N”,起点到终点的间隔为 N,循环次数为N。

switc
h
语句
s
witc h
是多分支选择
语句,而
i
f
语句只有两个分支可供选择。

诚然
能够用嵌套的
i
f
语句来实现
多分支选择,但那样的程序冗长
难读。

这是
switch
语句存在的原因。

s
witc
语句的基本格式是:
h
switch(variable)
{
casevalue1:
break;
casevalue2:
break;
default:
break;
}
【规则6-6-1】每个
c ase 语句的结
尾必定加
b
reak
,省得以致多个分支重叠。

也不同样意设
计多个分支重叠。


规则
6-6-2】
switch 最后必定有default分支。

即使程
序真的不需要
d
efaul
t
办理,也应
该保留语句default:break;这样做其实不是多此一举,而是为了防范别人误以为你忘了default 办理。

goto 语句
【建议6-7-1】防范使用goto语句。

常量
常量是一种表记符,它的值在运行期间恒定不变。

C语言用#define来定义常量(称为宏常量)。

C++ 语言除了#define外还可以够用const来定义常量(称为const常量)。

为什么需要常量
若是不使用常量,直接在程序中填写数字或字符串,将会有什么麻烦?
程序的可读性(可理解性)变差。

程序员自己会忘记那些数字或字符串是什么意思,用户则更加不知它们从哪处来、表示什么。

在程序的很多地方输入同样的数字或字符串,难保不发生书写错误。

若是要更正数字或字符串,则会在很多地方变动,既麻烦又简单出错。

【规则7-1-1】尽量使用含义直观的常量来表示那些将在程序中多次出现的数字或字符串。

比方PI等等
const 与#define 的比较
C++ 语言能够用const来定义常量,也能够用#define来定义常量。

但是前者比后者有很多的
优点:
const常量有数据种类,而宏常量没有数据种类。

编译器能够对前者进行种类安全检查。

而对后者只进行字符取代,没有种类安全检查,而且在字符取代可能会产买卖料不到的错误。

有些集成化的调试工具能够对const常量进行调试,但是不能够对宏常量进行调试。

但#define来定义常量也有其优点就是不用占用内存空间。

【建议7-2-1】在对内存无过多要求的C++程序中只使用const常量而不使用宏常量,即
const常量完满取代宏常量。

而在对内存有很大限制的嵌入式开发环境中则需要使用宏常
量。

【规则7-3-1】需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部。

为便于管理,能够把不同样模块的常量集中存放在一个公共的头文件中。

【规则7-3-2】若是某一常量与其余常量亲近相关,应在定义中包括这类关系,而不应给出一些孤立的值。

类中的常量
有时我们希望某些常量只在类中有效。

由于#define定义的宏常量是全局的,不能够达到目。


样才能建立在整个类中都恒定的常量呢?应该用类中的列举常量来实现。

列举常量不会占用
对象的
储藏空间,它们在编译时被全部求值。

列举常量的缺点是:它的隐含数据种类是整数,其最大值有限,且不能够表示浮点数(如PI=)。

函数设计
函数是C++/C程序的基本功能单元,其重要性不言而喻。

函数设计的细微缺点很简单以致该函
数被错用,因此光使函数的功能正确是不够的。

函数接口的两个要素是参数和返回值。

C语言中,函数的参数和返回值的传达方式有两种:值
传达(pass byvalue)和指针传达(pass bypointer)。

C++ 语言中多了引用传达(pass by
reference)。

由于引用传达的性质象指针传达,而使用方式却象值传达,大家经常百思不解,简单
引起凌乱。

参数的规则
【规则8-1-1
】参数的书写要完满,不要迷恋省事只写参数的种类而省略参数名字。

【规则
8-1-2】参数命名要合适,次序要合理。

比方,编写字符串拷贝函数
StringCopy,它有两个参数。

若是把参
数名字起为
str1和
str2。

voidStringCopy(char*str1,char*str2);
那么我们很难搞清楚终归是把str1拷贝到str2中,还是恰巧倒过来。

能够把参数名字起得更有意义,如叫strSource 和strDestination 。

这样从名字上就可以看出应
该把
strSource拷贝到strDestination。

【规则
8
-1-3
】若是参数是指针,且仅作输入用,则应
在种类前加
const,以防范该
指针在
函数体内被不测更正。

【规则
8
-1-4
】若是输入参数以值传达的方式传达对象,则
宜改用“
const&”方式
来传达,
这样能够省去临时对象的构造和析构过程,从而提高效率,关于一般
int型等等的变量则
不需要采用“const&”方式。

【建议
8-1-1】防范函数有太多的参数,参数个数尽
量控制在
5个之内。

若是参数
太多,
在使用时简单将参数种类或次序搞错。

【建议
8
-1-2
】尽量不要使用种类和数目不确定的参数。

C返回值的规则
D【规则8-2-1】不要省略返回值的种类。

E语言中,凡不加种类说明的函数,一律自动按整型办理。

这样做不会有什么
好处,却简单被误解为void种类。

C++语言有很严格的种类安全检查,不同样意上述
情况发生。

由于
C++程序能
够调用
C函数,为
C++/C
void 【
规则
【规则
8-2-2】函数名字与返回值种类在语义上不能矛盾。

8-2-3】不要将正常值和错误标志混在一起返回。

正常值用输出参数获得,而错误
标志用return语句返回。

【建议8-2-1】若是函数的返回值是一个对象,有些场适用“引用传达”取代“值传达”
能够提高效率。

但不是全部场合都适适用“引用传达”,而有些场合只能用“值传达”而不能够用“引用传达”,否则会出错。

函数内部实现的规则
不同样功能的函数其内部实现各不同样,看起来忧如无法就“内部实现”完成一致的见解。

但根
据经验,我们能够在函数体的“入口处”和“出口处”从严把关,从而提高函数的质量。

【规则8-3-1】在函数体的“入口处”,对参数的有效性进行检查。

很多程序错误是由非法参数引起的,我们应该充分理解并正确使用“断言”(assert)来防范此
类错误。

【规则8-3-2】在函数体的“出口处”,对return语句的正确性和效率进行检查。

若是函数有返回值,那么函数的“出口处”是return语句。

我们不要小瞧return语句。

若是return语句写得不好,函数要么出错,要么效率低下。

注意事项以下:
return语句不能返回指向“栈内存”的“指针”也许“引用”,由于该内存在函数体结
束时被自动销毁。

比方
要搞清楚返回的终归是“值”、“指针”还是“引用”。

若是函数返回值是一个对象,要考虑return语句的效率。

比方returnString(s1+s2);
这是临时对象的语法,表示“创办一个临时对象并返回它”。

不要以为它与“先创办一个局部对
象temp并返回它的结果”是等价的,如
Stringtemp(s1+s2);
returntemp;
实质否则,上述代码将发生三件事。

第一,temp对象被创办,同时完成初始化;尔后
拷贝构造
函数把temp拷贝到保留返回值的外面储藏单元中;最后, temp在函数结束时被销毁
(调用析构函
数)。

但是“创办一个临时对象并返回它”的过程是不同样的,编译器直接把临时对象创办并初始化在
外面储藏单元中,省去了拷贝和析构的化费,提高了效率。

近似地,我们不要将
returnint(x+y);
,由于‘在.’类中对任何成员都有意义,已经成为标准用法。

5. 不能够重载当前C++运算符会集中没有的符号,如#,@,$等。

原因有两点,
一是难以理解,
二是难以确定优先级。

对已经存在的运算符进行重载时,不能够改变优先级规则,否则将引起凌乱。

函数内联
能够用内联取代宏代码
C
++。

语言支持函数内联,其目的是为了提高函数的执
行效率(速度)
在C程序中,能够用宏代码提高执行效率。

宏代码自己不是函数,但使用起来象函数。

预办理
器用复制宏代码的方式取代函数调用,省去了参数压栈、生
成汇编语言的
调用、返回参数、
C
ALL


等过程,从而提高了速度。

使用宏代码最大的缺点是简单出错。

r
etur
n
让我们看看C++ 的“函数内联”是如何工作的。

关于任何内联函数,编译器在符号表里放入函数的声明(包括名字、参数种类、返回值种类)。

若是编译器没有发现内联函数存在错误,那么该
函数的代码也被放入符号表里。

在调用一个内联函数时,编译器第一检查调用可否正确(进
行种类
安全检查,也许进行自动种类变换,自然对全部的函数都同样)。

若是正确,内联函数的代码就会直
接取代函数调用,于是省去了函数调用的开销。

这个过程与预办理有显着的不同样,由于预
办理器不
能进行种类安全检查,也许进行自动种类变换。

若是内联函数是成员函数,对象的地址(
this)会
被放在合适的地方,这也是预办理器办不到的。

C++ 语言的函数内联体系既具备宏代码的效率,又增加了安全性,而且能够自由操作类的数据
成员。

“断言assert”生怕是个例外。

assert是仅在Debug版本起作用的宏,它用于检查“不应该”
发生的情况。

为了不在程序的Debug版本和Release版本引起差别,assert不应该产生任何
副作用。

若是assert是函数,由于函数调用会引起内存、代码的变动,那么将以致Debug版本与Release版本存在差别。

因此assert不是函数,而是宏。

【建议10-5-1】基本上宏代码函数都能够用内联函数取代
若是编程人员依旧习惯用写宏代码函数,那么就应该遵守以下规则:
【规则10-5-2】用宏定义表达式时,要使用齐全的括号。

【规则10-5-3】将宏所定义的多条表达式放在大括号中。

【规则10-5-4】使用宏时,不同样意参数发生变化。

内联函数的编程风格
重点字inline必定与函数定义体放在一起才能使函数成为内联,仅将inline放在函数声
明前面
不起任何作用。

inline是一种“用于实现的重点字”,而不是一种“用于声明的重点字”。

一般地,用户能够阅读函数的声明,但是看不到函数的定义。

尽管在大多数教科书中内联函数的
声明、定义
体前面都加了inline重点字,但我以为inline不应该出现在函数的声明中。

这个细节
诚然不会影响
函数的功能,但是表现了高质量C++/C程序设计风格的一个基根源则:声明与定义不能混
作一谈,
用户没有必要、也不应该知道函数可否需要内联。

定义在类声明之中的成员函数将自动地成为内联函数。

将成员函数的定义体放在类声明之中诚然能带来书写上的方便,但不是一种优秀的编程风格。

慎用内联
内联能提高函数的执行效率,为什么不把全部的函数都定义成内联函数?
内联是以代码膨胀(复制)为代价,不过省去了函数调用的开销,从而提高函数的执行效率。

若是执行函数体内代码的时间,对照于函数调用的开销较大,那么效率的收获会很少。

另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,耗资更多的内存空间。

以下情况不宜使用内联:
若是函数体内的代码比较长,使用内联将以致内存耗资代价较高。

若是函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。

类的构造函数、析构函数与赋值函数
构造函数、析构函数与赋值函数是每个类最基本的函数。

构造函数与析构函数的起源
依照经验,很多灾以察觉的程序错误是由于变量没有被正确初始化或除去造成的,而初始化和
除去工作很简单被人忘记。

Stroustrup 在设计C++语言时充分考虑了这个问题并很好地予以解决:
把对象的初始化工作放在构造函数中,把除去工作放在析构函数中。

当对象被创办时,构造函数被自动执行。

当对象消亡时,析构函数被自动执行。

这下就不用担忧忘了对象的初始化和除去工作。

构造函数与析构函数的另一个特别之处是没有返回值种类,这与返回值种类为void 的函数不
同。

但由于类的的成员变量可能需要多次初始或销毁,若是把对象的初始和销毁工作放在构造函数和析构函数中则不能够达到这个要求。

【建议11-1-1】每个类(特别是实体类)都需要有两个函数Initial()和Release(),主要做类成员变量的初始化和销毁,在Initial()中对指针变量设置为 NULL,对必要一般成员变量设置初始值0,在Release()中Delete指针变量,并将指针变量重新设置为NULL。

在类
的构造函数中调用Initial(),析构函数中调用Release()。

构造和析构的次序
构造从类层次的最根处开始,在每一层中,第一调用基类的构造函数,尔后调用成员对象的构造函数。

析构则严格依照与构造相反的次序执行,该次序是唯一的,否则编译器将无法自动执行析构过程。

类的继承与组合
对象(Object)是类(Class)的一个实例(Instance)。

若是将对象比作房子,那么类就是房子的设计图纸。

因此面向对象设计的重点是类的设计,而不是对象的设计。

关于C++程序而言,设计孤立的类是比较简单的,难的是正确设计基类及其派生类。

本章不过论述“继承”(Inheritance)和“组合”(Composition)的见解。

继承
若是A是基类,B是A的派生类,那么B将继承A的数据和函数。

比方:。

相关文档
最新文档