C++编程规范
排版:
程序采用缩进风格编写,缩进的空格数为4个,不使用TAB键;程序块的分界符应个独占一行并且位于同一列,同时与引用它们的语句左对齐;
if,for,do,while,case,switch,default等语句各自占一行,且if,for,do,while,case等语句的执行语句部分无论多少都要加{};
相对独立的程序块之间、变量声明语句块之后必须加空格;较长的语句(〉80字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操
作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读;
不允许把多个语句写在一行中,即一行只写一条语句;不允许把多个变量写在一行中,即一行之定义一个变量;
在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或前后加空格;
进行非对等操作时,如果是关系密切的立即操作符(如-〉)后不应加空格;
判断语句中常量/宏放在==的左边,变量放在==的右边;
访问权限的编译开关和控制关键字public/private/protected与上一级代码保持对齐,不缩进;
注释:
说明性文件头部应进行注释,注释必须列出:版权说明、生成日期、作者、内容说明、修改日志等;
源文件头部应进行注释,列出:版权说明、生成日期、作者、模块目的/功能、主要函数、修改日志等;
修改代码同时修改相应的注释,以保证注释与代码的一致性,不再有用的注释要删除;
变量名、常量名、数据结构名(包括数组、结构、类、枚举等)如果不是充分自注释的,则必须加上注释;
较复杂的分支语句(条件分支、循环语句等)必须加上注释;
对于switch控制块中不含break的case语句,必须加上注释;
对于使用代码屏蔽警告的,必须加上注释,至少列出:屏蔽原因、批准人员、批准时间;
注释应与其描述的代码位置相近,对代码的注释通常放在其上方相邻位置,并与注释的代码采用相同的缩进,不可放在下面和语句中间,如放于上方
则需与其上面的代码用空行隔开;
对于变量名、常量名、数据结构子元素等单行语句,其注释语句可放在右边;
注释的内容要清楚、明了,含义准确,防止注释二义性;
注释中禁止使用缩写,除非已是业界通用或标准化的缩写;
远程序注释量必须达到20%以上;
命名:
标识符的命名必须清晰、明了,有明确含义;
命名中禁止使用缩写,除非已是业界通用或标准化的缩写;
禁止使用单个字符作为变量名(包括用于循环的临时变量),要使用有意义的单词;
除非必要,不要用数字或较奇怪的字符来定义标识符;
用正确的反义词组命名具有互斥意义的变量或相反动作的函数等;
除用于编译开关和防止头文件重复展开的宏外,禁止名字以下划线起始或结尾;
宏名字使用全大写的以下划线分隔的单词;
目录名,文件名全部采用小写,如果由多个单词构成,则单词之间采用下划线连接;
变量名采用名词或名词短语(除bool变量外):基本类型和容器类的变量名要使用小写的类型前缀,类实例变量前缀不作要求;如果由多个单词构成
,则每个单词的首字母大写,不用下划线分隔;
名字空间名,类名使用首字母大写的名词,如果由多个单词构成,则每个单词的首字母大写,不使用空格或下划线之类的进行分隔;
文件名全部小写,并与其提供的主要类或名字空间的名称相同;
函数名使用全小写的动词,或小写的动词加首字母大写的名词的动宾结构;
类成员变量应使用m_前缀;
全局变量应使用g_前缀;
用作接口的类成员变量应使用与函数名相同的命名方式;
函数:
明确函数功能,精确(而不是近似)地实现函数设计;
一个函数仅完成一件功能,禁止设计多用途面面俱到的函数;
为简单功能编写函数,以实现程序的自解释性;
禁止编写依赖于其它函数内部实现的函数;
编写可重入函数是的注意:
不用静态数据;不调用任何不可重入函数;不返回指向全局数据的指针,所有数据都由函数的调用者提供;
避免访问全局变量,如果必须访问全局变量,需要利用互斥信号量等来保护全局变量;
函数的功能应该是可以预测的,也就是只要输入数据相同就应产生同样的输出;
减少函数本身或函数间的递归调用;
不要在函数调用处的参数列表里发生运算;
对所调用函数的错误返回码要仔细、全面地处理;
对于外部接口函数,应对参数正确性进行检查及处理;
内部函数应对自身的所有输入进行ASSERT检查,包括自身用到的所有非局部变量;
函数结构要清晰:变量定义,参数检查,处理,清理,返回,都必须有空行分隔开;
变量必须在定义时同时初始化;
禁止定义超过1K bytes的单个函数局部变量,单个函数的局部变量所占空间不能超过2K bytes;
函数规模不能大于150行;
设计高扇入、合理扇出(小于7)的函数;
函数调用深度不能大于5;
函数参数个数应小于等于5个;
禁止编写变参函数;
避免函数中不必要语句,防止程序中的垃圾代码;
检查函数所有非参数输入的有效性,如数据文件、公共变量等;
函数的返回值要清楚、明了,让使用者不容易忽视错误情况;
if语句尽量加上else分支,对没有else分支的语句要小心对待;
switch语句必须有default分支;
重载运算符要保持自然语义;
如果函数不改变状态,则必须声明为const函数;
基本数据类型的输入参数,优先传值;
非基本类型的输入参数,优先传const引用;
输出参数,优先传引用;
宏:
用宏定义表达式时,要使用完备的括号;
使用宏时,不允许参数发生变化;
尽量减少宏的使用,使用const来定义常量,使用模板函数或内联函数来取代函数型宏;
模板:
不要使用全局变量,如果确有需要,可以使用单件模式;
独立编译模块的外部接口禁止返回申请的内存;
独立编译模块的外部接口禁止抛出异常;
与外部模块通信时使用低级数据类型,模块内部接口通信应使用高级数据类型;
头文件:
头文件必须自足,也即此头文件已包含了所有其依赖的文件;
禁止包含不需要的文件;
头文件必须有防止重复展开的宏;
头文件中不允许定义有链接的实体;
名字空间:
类型和非成员函数接口如果是协同工作的则放在同一个名字空间中,否则放在不同的名字空间中;
禁止不同名字空间的对象有初始化依赖关系;
禁止在头文件中使用usingnamespace 指令;
禁止在#include之前使用using namespace 指令;
类:
明确的定义类的类型:数据值类,基类;
禁止继承没有设计为基类的类;
多用组合,少用继承;
基类析构函数只能是公用虚函数或保护非虚函数;
一个类一个清晰的目的,不要写面面俱到的巨大的类;
在构造函数中初始化所有数据成员;
在构造函数中申请资源,在析构函数中释放资源;
将数据成员设为私有,C风格的结构体不受此限制;
禁止返回内部数据指针;
禁止在构造和析构函数中调用虚函数;
如果提供了析构函数则同时提供拷贝函数或显式禁止拷贝函数;
显式地启用或者禁止复制;
扩展功能时优先添加非成员非友员函数;
防止切片,考虑使用Clone代替拷贝操作;
考虑使用NVI;
考虑使用pimpl惯用法;
STL:
优先使用vector;
优先使用push_back来扩展序列;
在容器中只存放值,引用计数的智能指针或普通指针;
优先使用容器自带操作,使用惯用法;
考虑使用算法代替手工编写的循环;
使用迭代访问代替其它遍历容器方式;
算法和比较器的参数优先使用函数对象;
错误处理:
使用异常来报告错误;
通过值抛出异常,通过引用捕获异常;
保证所有的异常都得到了处理;
禁止使用函数异常声明;
函数必须提供强保证:保证回到初始状态或是到达操作成功状态;
析构和资源释放函数要保证不会失败;
使用ASSERT记录内部假设:当ASSERT被触发时,一定报告了一个代码bug;
禁止使用ASSERT报告运行时错误;
禁止在ASSERT中编写有副作用(改变系统状态)的表达式;
使用ASSERT( ! ”info message”)代替ASSERT(false);
安全:
禁止使用C风格的强制类型转换;
禁止对const变量进行转换;
禁止使用reinterpret_cast;
禁止使用memset,memcpy,memcmp;
禁止在联合体中放入一个数据而取出另一个数据;
不要使用strcpy,strncpy,sprintf等不安全的C语言遗留函数;
可维护性:
禁止使用goto语句;
禁止使用魔鬼数字;
使用括号明确表达式的运算顺序,避免使用默认优先级;
不要手动管理资源,交给对象或智能指针来管理;
尽量使用STL容器与算法;
尽量减小变量作用域;
同产品软件(项目组)内,应该使用相同的编辑器,使用相同的设置选项,工具安装到相同的目录,代码取到相同的位置,统一编译开关选项;
打开编译器的所有告警开关对程序进行编译,并消除所有告警;
编程指导:
可测性:
在同一项目组或产品组内,要有一套统一的为集成测试、联调、系统测试准备的调测开关及相应打印函数,并且要有详细的说明;
在同一项目组或产品组内,调测打印出的信息串的格式要有统一的形式。信息串中至少要有所在模块名(或源文件名)及行号;
编程的同时要为单元测试选择恰当的测试点,并仔细构造测试代码、测试用例,同时给出明确的注释说明。测试代码部分应作为(模块中的)一个子
模块,以方便测试代码在模块中的安装与拆卸(通过调测开关);
使用断言来发现软件问题,提高代码可测性;
用断言来检查程序正常运行时不应发生但在调测时有可能发生的非法情况;
对较复杂的断言加上明确的注释;
用断言确认函数的参数;
用断言保证没有定义的特性或功能不被使用;
用断言对程序开发环境(OS/Compiler/Hardware)的假设进行检查;
增加所有的断言和调试都必须由开关控制;
在软件系统中设置与取消有关测试手段,不能对软件实现的功能等产生影响;
用调测开关来切换软件的DEBUG版和正式版,而不要同时存在正式版本和DEBUG版本的不同源文件,以减少维护的难度;
在编写代码之前,应预先设计好程序调试与测试的方法和手段,并设计好各种调测开关及相应测试代码如打印函数等;
调测开关应分为不同级别和类型;
程序效率:
编程时要经常注意代码的效率:全局效率、局部效率、时间效率及空间效率;
在保证软件系统的正确性、稳定性、可读性及可测性的前提下,提高代码效率;
局部效率应为全局效率服务,不能因为提高局部效率而对全局效率造成影响;
通过对系统数据结构的划分与组织的改进,以及对程序算法的优化来提高空间效率:这种方式是解决软件空间效率的根本办法;
循环体内工作量最小化;
仔细分析有关算法,并进行优化;
仔细考查、分析系统及模块处理输入(如事务、消息等)的方式,并加以改进;
对模块中函数的划分及组织方式进行分析、优化,改进模块中函数的组织结构,提高程序效率;
编程时,要随时留心代码效率;优化代码时,要考虑周全;
不应花过多的时间拼命地提高调用不很频繁的函数代码效率;
要仔细地构造调用频繁或性能要求极高的函数;
在保证程序质量的前提下,通过压缩代码量、去掉不必要代码以及减少不必要的局部和全局变量,来提高空间效率;
在多重循环中,应将最忙的循环放在最内层;
尽量减少循环嵌套层次,限制在3层之内;
避免循环体内含判断语句,应将循环语句置于判断语句的代码块之中;
尽量用乘法或其它方法代替除法,特别是浮点运算中的除法;
不要一味追求紧凑的代码;
正确使用C++语言,避免不必要的性能损失;
质量保证:
在软件设计过程中构筑软件质量;
代码质量保证优先原则:
(1)正确性,指程序要实现设计要求的功能;
(2)稳定性、安全性,指程序稳定、可靠、安全;
(3)可测试性,指程序要具有良好的可测试性;
(4)规范/可读性,指程序书写风格、命名规则等要符合规范;
(5)全局效率,指软件系统的整体效率;
(6)局部效率,指某个模块/子模块/函数的本身效率;
(7)个人表达方式/个人方便性,指个人编程习惯;
只引用属于自己的存贮空间;
防止引用已经释放的内存空间;
过程/函数中分配的内存,在过程/函数退出之前要释放;
过程/函数中申请的(为打开文件而使用的)文件句柄,在过程/函数退出之前要关闭;
防止内存操作越界;
认真处理程序所能遇到的各种出错情况;
系统运行之初,要初始化有关变量及运行环境,防止未经初始化的变量被引用;
系统运行之初,要对加载到系统中的数据进行一致性检查;
严禁随意更改其它模块或系统的有关设置和配置;
不能随意改变与其它模块的接口;
充分了解系统的接口之后,再使用系统提供的功能;
编程时,要防止差1错误;
要时刻注意易混淆的操作符。当编完程序后,应从头至尾检查一遍这些操作符,以防止拼写错误;
不使用与硬件或操作系统关系很大的语句,而使用建议的标准语句,以提高软件的可移植性和可重用性;
除非为了满足特殊需求,避免使用嵌入式汇编;
精心地构造、划分子模块,并按“接口”部分及“内核”部分合理地组织子模块,以提高“内核”部分的可移植性和可重用性;
精心构造算法,并对其性能、效率进行测试;
时刻注意表达式是否会上溢、下溢;
使用变量时要注意其边界值的情况;
留心程序机器码大小(如指令空间大小、数据空间大小、堆栈空间大小等)是否超出系统有关限制;
为用户提供良好的接口界面,使用户能较充分地了解系统内部运行状态及有关系统出错情况;
系统应具有一定的容错能力,对一些错误事件(如用户误操作等)能进行自动补救;
对一些具有危险性的操作代码(如写硬盘、删数据等)要仔细考虑,防止对数据、硬件等的安全构成危害,以提高系统的安全性;
资源文件(多语言版本支持),如果资源是对语言敏感的,应让该资源与源代码文件脱离,具体方法有下面几种:使用单独的资源文件、DLL文件或其
它单独的描述文件(如数据库格式);
代码编辑、编译、审查:
打开编译器的所有告警开关对程序进行编译;
在产品软件(项目组)中,要统一编译开关选项;
通过代码走读及审查方式对代码进行检查;
编写代码时要注意随时保存,并定期备份,防止由于断电、硬盘损坏等原因造成代码丢失;
同产品软件(项目组)内,最好使用相同的编辑器,并使用相同的设置选项;
要小心地使用编辑器提供的块拷贝功能编程;
合理地设计软件系统目录,方便开发人员使用;
某些语句经编译后产生告警,但如果你认为它是正确的,那么应通过某种手段去掉告警信息;
使用代码检查工具(如C语言用PC-Lint, LogiSCOPE等)对源程序检查;
代码测试、维护:
单元测试要求至少达到语句覆盖;
单元测试开始要跟踪每一条语句,并观察数据流及变量的变化;
不能进行单步跟踪的代码,要采用日志输出等形式,跟踪数据流和变量的变化;
清理、整理或优化后的代码要经过审查及测试;
代码版本升级要经过严格测试;
使用工具软件对代码版本进行维护;
正式版本上软件的任何修改都应有详细的文档记录;
发现错误立即修改,并且要记录下来;
关键的代码在汇编级跟踪;
仔细设计并分析测试用例,使测试用例覆盖尽可能多的情况,以提高测试用例的效率;
尽可能模拟出程序的各种出错情况,对出错处理代码进行充分的测试;
仔细测试代码处理数据、变量的边界情况;
保留测试信息,以便分析、总结经验及进行更充分的测试;
不应通过“试”来解决问题,应寻找问题的根本原因;
对自动消失的错误进行分析,搞清楚错误是如何消失的;
修改错误不仅要治表,更要治本;
测试时应设法使很少发生的事件经常发生;
明确模块或函数处理哪些事件,并使它们经常发生;
坚持在编码阶段就对代码进行彻底的单元测试,不要等以后的测试工作来发现问题;
去除代码运行的随机性(如去掉无用的数据、代码及尽可能防止并注意函数中的“内部寄存器”等),让函数运行的结果可预测,并使出现的错误可再现;
写给自己的C++编程规范
最新推荐文章于 2024-08-25 06:11:42 发布