自动摘要: 1 程序风格 【11】 程序块要采用缩进风格编写,缩进的空格数为4个。函数开始、结构的定义及循环、判断等语句中的代码都要采用缩进风格,case语句下的情况处理语句也要遵从语句缩进要求。预 ……..
1 程序风格
【1-1】 程序块要采用缩进风格编写,缩进的空格数为4个。函数开始、结构的定义及循环、判断等语句中的代码都要采用缩进风格,case语句下的情况处理语句也要遵从语句缩进要求。预处理指令不要缩进,从行首开始。即使预处理指令位于缩进代码块中,指令也应从行首开始.【1-2】 相对独立的程序块之间、变量说明之后必须加空行。如:类、函数定义之后。【1-3】 较长的语句(>80字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读。循环、判断等语句中若有较长的表达式或语句,则要进行适当的划分,长表达式要在低优先级操作符处划分新行,操作符放在新行之首。若函数中的形参与实参较长时,也要进行适当的划分。【1-4】 【强制】不允许把多个短语句写在一行中,即一行只写一条语句。if、for、do、while、case、switch、default等语句自占一行,且if、for、do、while等语句的执行语句部分无论多少都要加括号{}。程序块的分界符(如{、})可以使用下面两种风格之一;**// 风格1**
1 | if(1) |
// 风格2
1 | if(1){ |
【1-5】 修饰符的位置:将 * 紧靠变量名,将 & 紧靠类型名。如:
1 | int *p, a; |
【1-6】 尽量不使用非 ASCII 字符, 使用时必须使用 UTF-8 编码。尽量不将字符串常量耦合到代码中,应该编写资源文件。【1-7】 【强制】避免使用不易理解的数字,应用有意义的标识来替代(#define 或 const)。涉及实际状态或者含有实际意义的常量,不应直接使用数字,必须用有意义的enum来代替。示例:如下的程序可读性差。
1 | if (Trunk[index].trunk_state == 0) |
应改为如下形式。
1 | enum |
【1-9】 不要使用难懂的技巧性很高的语句,除非很有必要时。说明:高技巧语句不等于高效率的程序,实际上程序的效率关键在于算法。示例:如下表达式,考虑不周就可能出问题,也较难理解。
1 | * stat_poi ++ += 1; |
应分别改为如下。
1 | *stat_poi += 1; |
2 注释
注释基本概念在每个代码项中都可以有两类描述, 这两类描述将在文档中格式化在一起: 一种就是brief描述, 另一种就是detailed。两种都是可选的,但不能同时没有。简述(brief)就是在一行内简单的描述。而详细描述(detailed description)则提供更长, 更详细的文档。一般有如下几处需要使用注释:1) 文件头(.h和.cpp):主要用于声明版权,描述本文件实现的功能,及文件版本信息等;2) 【强制】类的定义:主要用于描述该类的功能,同时也可包含使用方法或者注意事项的简要描述;3) 【强制】类的成员变量声明:在类的成员变量上方,对该成员变量进行简要地描述;4) 【强制】类的成员函数声明:在类定义的成员函数上方,对该成员函数功能进行简要描述,并简要描述参数、返回值的含义,描述使用本函数需要注意的问题;5) 函数的实现:在函数的实现代码之上,详细描述函数的功能、本函数使用其他类或函数的说明等;6) 【强制】enum定义:描述每个枚举项的含义;常用注释指令可以在注释中加入指令,控制输出文档的格式,使用指令时需要在前面加上\符号。
\file | 文件的批注说明; |
---|---|
\author | 作者的信息; |
\brief | 用于class 或function的简易说明;eg:\brief 本函数负责打印错误信息串 |
\param | 主要用于函数说明中,后面接参数的名字,然后再接关于该参数的说明; |
\return | 描述该函数的返回值情况;eg: \return 本函数返回执行结果,若成功则返回TRUE,否则返回FLASE |
\retval | 描述返回值类型eg: \retval NULL 空字符串。 \retval !NULL 非空字符串。 |
\note | 注解 |
\attention | 注意 |
\warning | 警告信息 |
\enum | 引用了某个枚举,Doxygen会在该枚举处产生一个链接eg:\enum CTest::MyEnum |
\var | 引用了某个变量,Doxygen会在该枚举处产生一个链接**eg: **\var CTest::fileKey_ |
\class | 引用某个类,格式:\class |
\exception | 可能产生的异常描述eg: \exception 本函数执行可能会产生超出范围的异常 |
基本注释要求一般注释的描述由简述开始,经过特殊分隔方式后,后面紧跟详述的内容,C++风格可以使用的分隔方法有以下两种:使用 \brief 参数标识,空行作为简述和详述的间隔标识
1 | /// \brief Brief description. |
使用以空行(或者小数点加空格)作为简述与详述的分割
1 | /// Brief description |
以小数点加空格作为分隔如下:
1 | /// Brief description |
【2-1】 【强制】一个代码块(类、函数、结构等)的概述采用单行的“///”加一个空格开头的注释,并写在该代码块声明的前面;
1 | /// \brief 本类的功能:打印错误信息 |
【2-2】 一个代码块的详述采用至少两行的”///”加一个空格开头的注释,若不足两行第二行的开头也要写出来,并且放在代码块定义的前面;如果某代码没有声明只有定义或者相反,则在定义或者声明前面写上单行的概述+一个空行+多行的详述;【2-3】 枚举值列表的各项、结构域的各项等采用在本行最后添加”///<”加一个空格开头的注释;
1 | /// 颜色的枚举定义 |
【2-4】 对变量的定义采用在变量上面加单行”///”加一个空格开头的注释(相当于是给改变量一个概述);
1 | /// 成员变量描述 |
【2-5】 函数的参数用”/// \param”+一个空格开头的行描述在函数的详述里面; 函数的返回值用”/// \return”+一个空格开头的行描述在函数的详述里面;函数之间的参考用”/// \see”+一个空格开头的行描述在函数的详述里面;
1 | /// 下面是一个含有两个参数的函数的注释说明(简述) |
【2-6】 文件头的版权以及文件描述的注释参见例代码。
1 | /////////////////////////////////////////////////////////////////// |
【2-7】 全局变量要有较详细的注释,包括对其功能、取值范围、哪些函数存取它以及存取时注意事项等的说明。【2-8】 对变量的定义和分支语句(条件分支、循环语句等)必须编写注释。说明:这些语句往往是程序实现某一特定功能的关键,对于维护人员来说,良好的注释帮助更好的理解程序,有时甚至优于看设计文档。【2-9】 对那些临时的,短期的解决方案,或已经够好但仍不完美的代码使用 TODO 注释。
3 标识符命名
【3-1】 标识符应当直观且可以拼读,可望文知意,不必进行“解码”;【3-2】 标识符的长度应当符合“min-length && max-information”原则;【3-3】 通用命名规则:函数命名、变量命名、文件命名应具备描述性;不要过度缩写.类型和变量应该是名词, 函数名可以用 “命令性” 动词.尽可能给出描述性的名称. 不要节约行空间, 让别人很快理解你的代码更重要.好的命名风格:
1 | int num_errors; // Good. |
糟糕的命名使用含糊的缩写或随意的字符:
1 | int n; // Bad - meaningless. |
类型和变量名一般为名词: 如 fileOpener, num_errors.函数名通常是指令性的 (确切的说它们应该是命令), 如 openFile(), set_num_errors().【3-4】 【强制】文件命名:文件名要全部是英文,可包含下划线_。可接受的文件命名:my_useful_class.cppmy-useful-class.cppmyUsefulClass.cpp【3-5】 【强制】类型命名:类型名称的每个单词首字母均大写, 不包含下划线。如:ExcitingClass,ExcitingEnum。所有类型命名(类、结构体、typedef、枚举)均使用相同约定。 例如:
1 | // classes and structs |
【3-6】 【强制】变量命名:变量名可以使用两种方式,类或结构体的成员变量以下划线结尾。风格1:全部使用小写,单词之间用下划线连接。 如:
1 | my_exciting_local_variable // 普通变量 |
**风格2: **首字母小写,其他字母以单词为间隔,每个单词首字母大写。如:
1 | myExcitingLocalVariable |
全局变量命名: 对全局变量尽量少用, 可用 g 作为前缀, 以便更好的区分局部变量。【3-7】 常量命名:在名称前加 k: kDaysInAWeek.所有编译时常量,无论是局部的,全局的还是类中的, 和其他变量稍微区别一下. k 后接大写字母开头的单词:
1 | const int kDaysInAWeek = 7; |
【3-8】 【强制】函数命名:首字母小写,常规函数使用大小写混合, 取值和设值函数则要求与变量名匹配:excitingFunction()excitingMethod()get_exciting_member_variable() 或 exciting_member_variable()set_exciting_member_variable()addTableEntry()deleteUrl()【3-9】 【强制】枚举命名:枚举的命名应当和 宏 一致: ENUM_NAME.【3-10】 除了编译开关/头文件等特殊应用,应避免使用_EXAMPLE_TEST_之类以下划线开始和结尾的定义。
4 函数
【4-1】 【强制】参数的书写要完整,不要贪图省事只写参数的类型而省略参数名字。例如:
1 | void setValue(int nWidth, int nHeight); // 良好的风格 |
【4-2】 【强制】如果参数是指针,且仅作输入用,则应在类型前加const,以防止该指针在函数体内被意外修改。
1 | void stringCopy(char *strDestination,const char *strSource); |
【4-3】 【强制】如果输入参数以值传递的方式传递对象,则宜改用“const &”方式来传递,这样可以省去临时对象的构造和析构过程,从而提高效率;【4-4】 对所调用函数的返回值要仔细、全面地处理。说明:函数的每种返回值的意义要清晰、明了、准确,防止使用者误用、理解错误返回值。【4-5】 一个函数仅完成一件功能;为简单功能编写函数。说明:虽然为仅用一两行就可完成的功能去编函数好象没有必要,但用函数可使功能明确化,增加程序可读性,亦可方便维护、测试。示例:如下语句的功能不很明显。
1 | value = ( a > b ) ? a : b ; |
改为如下就很清晰了。
1 | int max (int a, int b) |
【4-6】 检查函数所有参数输入的有效性。If / assert【4-7】 检查函数所有非参数输入的有效性,如数据文件、公共变量等。说明:函数的输入主要有两种:一种是参数输入;另一种是全局变量、数据文件的输入,即非参数输入。函数在使用输入之前,应进行必要的检查。【4-8】 改进模块中函数的结构,降低函数间的耦合度,并提高函数的独立性以及代码可读性、效率和可维护性。优化函数结构时,要遵守以下原则:1)不能影响模块功能的实现。2)仔细考查模块或函数出错处理及模块的性能要求并进行完善。3)通过分解或合并函数来改进软件结构。4)考查函数的规模,过大的要进行分解。5)降低函数间接口的复杂度。6)不同层次的函数调用要有较合理的扇入、扇出。7)函数功能应可预测。8)提高函数内聚。(单一功能的函数内聚最高)说明:对初步划分后的函数结构应进行改进、优化,使之更为合理。【4-9】 尽可能在定义变量的同时初始化该变量(就近原则)。严禁使用未经初始化的变量作为右值。类或结构体必须编写构造函数,并在构造函数中对相应的成员进行初始化。
5 可测性
【5-1】 使用断言来发现软件问题,提高代码可测性。说明:断言是对某种假设条件进行检查(可理解为若条件成立则无动作,否则应报告),它可以快速发现并定位软件问题,同时对系统错误进行自动报警。断言可以对在系统中隐藏很深,用其它手段极难发现的问题进行定位,从而缩短软件问题定位时间,提高系统的可测性。实际应用时,可根据具体情况灵活地设计断言。示例:下面是C语言中的一个断言,用宏来设计的。(其中NULL为0L)
1 |
|
【5-2】 用断言来检查程序正常运行时不应发生但在调测时有可能发生的非法情况。【5-3】 用断言确认函数的参数。示例:假设某函数参数中有一个指针,那么使用指针前可对它检查,如下。
1 | int exam_fun( unsigned char *str ) |
6 程序效率
【6-1】 在保证软件系统的正确性、稳定性、可读性及可测性的前提下,提高代码效率。说明:不能一味地追求代码效率,而对软件的正确性、稳定性、可读性及可测性造成影响。【6-2】 尽量减少循环嵌套层次。【6-3】 【强制】避免循环体内含判断语句,应将循环语句置于判断语句的代码块之中。说明:目的是减少判断次数。循环体中的判断语句是否可以移到循环体外,要视程序的具体情况而言,一般情况,与循环变量无关的判断语句可以移到循环体外,而有关的则不可以。示例:如下代码效率稍低。
1 | for (ind = 0; ind < MAX_RECT_NUMBER; ind++) |
因为判断语句与循环变量无关,故可如下改进,以减少判断次数。
1 | if (data_type == RECT_AREA) |
7 质量保证
【7-1】 代码质量保证优先原则1)正确性,指程序要实现设计要求的功能。2)稳定性、安全性,指程序稳定、可靠、安全。3)可测试性,指程序要具有良好的可测试性。4)规范/可读性,指程序书写风格、命名规则等要符合规范。5)全局效率,指软件系统的整体效率。6)局部效率,指某个模块/子模块/函数的本身效率。7)个人表达方式/个人方便性,指个人编程习惯。【7-2】 防止引用已经释放的内存空间。【7-3】 【强制】函数中分配的内存,在函数退出之前要释放。【7-4】 函数中申请的(为打开文件而使用的)文件句柄,在函数退出之前要关闭。【7-5】 防止内存操作越界。说明:内存操作主要是指对数组、指针、内存地址等的操作。内存操作越界是软件系统主要错误之一,后果往往非常严重,所以当我们进行这些操作时一定要仔细小心。【7-6】 严禁随意更改其它模块或系统的有关设置和配置。说明:编程时,不能随心所欲地更改不属于自己模块的有关设置如常量、数组的大小等。【7-7】 不能随意改变与其它模块的接口。【7-8】 时刻注意易混淆的操作符。形式相近的操作符最容易引起误用,如=与==、 |与||、 &与&&等,若拼写错了,编译器不一定能够检查出来。【7-9】 系统应具有一定的容错能力,对一些错误事件(如用户误操作等)能进行自动补救。【7-10】 使用第三方提供的软件开发工具包或控件时,要注意以下几点:1)充分了解应用接口、使用环境及使用时注意事项。2)不能过分相信其正确性。3)除非必要,不要使用不熟悉的第三方工具包与控件。【7-11】 资源文件(多语言版本支持),如果资源是对语言敏感的,应让该资源与源代码文件脱离,如:使用单独的资源文件。
8 代码编辑、编译、审查
【8-1】 编写代码时要注意随时保存,并定期备份,防止由于断电、硬盘损坏等原因造成代码丢失。【8-2】 同产品软件(项目组)内,最好使用相同的编辑器,并使用相同的设置选项。【8-3】 要小心地使用编辑器提供的复制粘贴功能编程。【8-4】 合理地设计软件系统目录,方便开发人员使用。说明:方便、合理的软件系统目录,可提高工作效率。【8-5】 某些语句经编译后产生警告,但如果你认为它是正确的,那么应通过某种手段去掉警告信息。说明:在VC中,可用“#pragma warning”来关掉或打开某些警告。#pragma warning(disable:N1) // 不显示N号警告信息#pragma warning(once:N) // N号警告信息仅报告一次#pragma warning(error:N) // 把N号警告信息作为一个错误【8-6】 使用代码检查工具(如:PC-Lint)对源程序检查。【8-7】 使用软件工具(如:LogiSCOPE)进行代码审查。【8-8】 打开编译器的所有警告开关对程序进行编译。【8-9】 在产品软件(项目组)中,要统一编译开关选项。【8-10】 通过代码走读及审查方式对代码进行检查。说明:代码走读主要是对程序的编程风格如注释、命名等以及编程时易出错的内容进行检查,可由开发人员自己或开发人员交叉的方式进行;代码审查主要是对程序实现的功能及程序的稳定性、安全性、可靠性等进行检查及评审,可通过自审、交叉审核或指定部门抽查等方式进行。【8-11】 测试部测试产品之前,应对代码进行抽查及评审。
9 代码测试、维护
【9-1】 单元测试要求至少达到语句覆盖。【9-2】 清理、整理或优化后的代码要经过审查及测试。【9-3】 代码版本升级要经过严格测试。【9-4】 软件的任何修改都应有详细的文档记录。【9-5】 发现错误立即修改,并且要记录下来。【9-6】 仔细设计并分析测试用例,使测试用例覆盖尽可能多的情况,以提高测试用例的效率。【9-7】 尽可能模拟出程序的各种出错情况,对出错处理代码进行充分的测试。【9-8】 仔细测试代码处理数据、变量的边界情况。【9-9】 保留测试信息,以便分析、总结经验及进行更充分的测试。【9-10】 不应通过“试”来解决问题,应寻找问题的根本原因。【9-11】 坚持在编码阶段就对代码进行彻底的单元测试,不要等以后的测试工作来发现问题。
10 宏
【10-1】 【强制】用宏定义表达式时,应使用完备的括号。示例:如下定义的宏都存在一定的风险。#define RECTANGLE_AREA( a, b ) a * b#define RECTANGLE_AREA( a, b ) (a * b)#define RECTANGLE_AREA( a, b ) (a) * (b)正确的定义应为:#define RECTANGLE_AREA( a, b ) ((a) * (b))【10-2】 【强制】将宏所定义的多条表达式放在大括号中。示例:下面的语句只有宏的第一条表达式被执行。为了说明问题,for语句的书写稍不符规范。
1 |
|
正确的用法应为:
1 |
|
【10-3】 【强制】使用宏时,不允许参数发生变化。示例:如下用法可能导致错误。
1 |
|
11 头文件
【11-1】** 【强制】为了防止头文件被重复引用,应当用ifndef/define/endif结构产生预处理块;“#ifndef”或者“#define”后以TAB键代替SPACE键做空格;如果头文件名称是由多个单词组成,则各单词间以下划线“_”连接,例如有头文件名称为“filesystem.h”,则定义如下:“#ifndef _FILE_SYSTEM_H_”;【11-2】 头文件中只存放“声明”而不存放“定义”;【11-3】 **头文件中应包含所有定义文件所定义的函数声明,若一个头文件对应多个定义文件,则不同定义文件内实现的函数要分开声明,并作注释以解释所声明的函数从属于那一个定义文件;【11-4】 在C++ 语法中,类的成员函数可以在声明的同时被定义,并且自动成为内联函数。这虽然会带来书写上的方便,但却造成了风格不一致,弊大于利。建议将成员函数的定义与声明分开,不论该函数体有多么小。 头文件的结构如下:
1 |
|