PS: 为本文标题增加空格 & 内容表格格式修复
前言:
本文翻译自C++ Programming Style Guidelines原文地址
参考资料:Google机翻
本文仅供参考,如有错误希望指出。如有侵权,会立即删除,决不用于商业用途。
本文为ID: 萌面大道 http://maimieng.com/ 翻译,授权网易云课堂C++程序设计入门(上)的崔毅东老师作为课件使用,大家可以免费下载,禁止用于商业用途,如需转载,需联系本人并保留此版权声明。(CC版权声明在文末)
祝大家学有所成!
——by 萌面大道
1 介绍
这份文档列出了C++开发社区中C++代码编写的基本指南。
这些建议是基于多个来源,个人经验,实际需求及一些给定的建议[1]-[4]收集来的而确定的标准。
引入一份新的指南而不只是引用以上的资料原因有很多。最主要是因为那些资料范围太过宽泛,并且更多的具体规则(尤其是命名规范)需要建立。同时,这份指南拥有注释,可以让它比大多数现存指南更简单地使用于检查项目中的代码。此外,编程指导通常以莫名其妙的方式和编程语言的技术问题混在一起。不过虽然这份文档不包含任何C++技术建议,但聚焦于主要的编程风格。其他可以参考C++编程实践指南。
集成开发环境(IDE)能够通过可视化接口,高亮代码,自动补全等提高代码可读性。程序员应该拒绝依赖这些特性。源代码应该被我们更多地注意而不是IDE对于代码的照顾,并且应当使代码独立于任何IDE但获得最大化的可读性。
1.1 指南布局
这些指南以不同主题分组,并且每个建议都被标明序号,在审查代码时可以更方便地参考。
(译者注:最后一条是第94,不过在这其中有几条并不存在,源网站也没有。)
以下是指南的布局:
| n. 指南简述 |
| 恰当的例子(以下此处不译) |
| 原因, 来源 和 附加信息。 |
第三行很重要。代码标准和指南有可能引发争论,因此为它们标注来源是很重要的。
1.2 指南的重要性
在指南部分里,“必须”,“推荐”,“建议”均有特殊含义。“必须”是一定要遵守的,“推荐”是强烈建议,“建议”是一般指引。(译者注:为了标明此,有些语言进行了重组。)
2 通用指南
| 1. 任何违反此指南但提高代码可读性都是允许的。 |
|
| 该指南的主要目的是提高代码可读性,从而使得一般数量级的代码更好的理解和维护。此指南不可能涵盖所有的特殊情况,这需要程序员的随机应变、灵活运用。 |
| 2. 如果您和它持强烈不同的意见,您完全不必遵守此指南。 |
|
| 制作这份指南,并不是用一个特定的代码风格去强加于每个人。有经验的程序员虽然通常采用类似这种规范,但有一份这样的指南,且让每个人都去熟悉它,通常可以使人们开始思考代码风格并评判在这个领域他们自己的习惯。 |
| 另一方面,没有经验的程序员或者编程新手想要进入这一行当,通常遵循一个代码风格指南会让这段路途走得更加惬意。 |
3 命名规范
3.1 一般命名规定
| 3. 变量和常量类型的命名每个单词的首字母必须大写。 |
| Line, SavingsAccount |
| C++开发社区惯例。 |
| 4. 变量的命名首字母必须小写(其他单词首字母大写)。 |
| line, savingsAccount |
| C++开发社区惯例。使得变量便于区别于类型,并且有效地解决了命名冲突,类似:Line line;。 |
| 5. 常量的命名(包括枚举值)必须用下划线分隔单词并全部大写。 |
| MAX_ITERATIONS, COLOR_RED, PI |
| C++开发社区惯例。通常,常量应尽量避免使用。大多数情况下,作为函数执行所得值是更好的选择: |
| int getMaxIterations() // NOT: MAX_ITERATIONS = 25 |
| { |
| return 25; |
| } |
| 这种形式既能增加代码可读性,又能确保了对于类的值具有统一的接口。 |
| 6. 函数或方法的命名必须是动词词组且首字母小写(其他单词首字母大写)。 |
| getName(), computeTotalWidth() |
| C++开发社区惯例。此命名规范类似于变量的命名,但C++函数通过不同形式区别于变量名。 |
| 7. 命名空间推荐以全部小写命名。 |
| model::analyzer, io::iomanager, common::math::geometry |
| C++开发社区惯例。 |
| 8. 命名模版类型推荐使用单一的大写字母。 |
| template … |
| template<class C, class D> … |
| C++开发社区惯例。这使得模版名称突出于所有其他名称。 |
| 9. 作为名称使用时,缩略词不得使用全部大写[4]。 |
| exportHtmlSource(); // NOT: exportHTMLSource(); |
| openDvdPlayer(); // NOT: openDVDPlayer(); |
| 使用全部大写命名基本名称将会和上述的命名规范冲突。像dVD,hTML等如此命名的变量可读性显然不高。另外还有一个问题在上述例子中已经说明;当名称和另外一个有联系,代码可读性会严重下降;缩略词并不那么像它本身那样独特。 |
| 10. 全局变量推荐使用::操作符。 |
| ::mainWindow.open(), ::applicationContext.getName() |
| 通常,应避免使用全局变量。可以考虑使用单件模式对象来代替。 |
| 11. 类的私有变量命名推荐以下划线为结尾。 |
| class SomeClass { |
| private: |
| int length_ ; |
| } |
| 除了变量的名称和类型,作用域是其最重要的特性。使用下划线指示类的作用域可以很简单的区分局部变量和类的成员变量。这点很重要,因为类的成员变量比函数变量具有更大的作用域,因此更值得被程序员注意。此方法的另一个作用就是它很好的解决了为了给函数或构造器设值寻找合理的变量名: |
| void setDepth (int depth) |
| { |
| depth_ = depth; |
| } |
| 有一个问题就是使用前缀还是后缀下划线。两者都被广泛的使用,但是更加建议使用后缀,因为这样可以维持更好的可读性。 |
| 需要注意的是,变量作用域的争议问题已经持续很久了,虽然好像现在这个指南已经获得了赞同,并且这将作为一条惯例在专业的开发者社区变得越来越普遍。 |
| 12. 通用的变量推荐和其类型一致。 |
| void setTopic(Topictopic) // NOT: void setTopic(Topic value) |
| // NOT: void setTopic(Topic* aTopic) |
| // NOT: void setTopic(Topic* t) |
| void connect(Databasedatabase) // NOT: void connect(Database db) |
| // NOT: void connect (Database* oracleDB) |
| 通过减少术语和名称来减少代码复杂程度。此外,使得仅给出名字推断出类型变得容易。如果有一些原因似乎不能强烈地指出其类型名,就说明这是一种错误的选择。非通用的变量有一个特殊的角色。这些变量可以经常结合类型命名: |
| Point startingPoint, centerPoint; |
| Name loginName; |
| 13. 所有名称推荐使用英文。 |
| fileName; // NOT: filNavn |
| 在国际化开发中,英文是首选语言。 |
| 14. 变量作用域越长,推荐使用更长的名称,作用域越短,建议使用较短的名称[1]。 |
|
| 暂时存储或索引的临时变量名字最好较短。程序员阅读这些变量应该能推断出它的值不会在几行代码之外使用。常见的整型临时变量有:i, j, k, m, n,字符型:c, d。 |
| 15. 对象的名字是暗示的且要避免出现在函数名称中。 |
| line.getLength(); // NOT: line.getLineLength(); |
| 后者在类的声明中看起来很自然,但被在例子中被证明在使用中这是多余的。 |
3.2 特定命名规定
| 17. 术语get/set必须使用在直接存取属性的地方。 |
| employee.getName(); |
| employee.setName(name); |
| matrix.getElement(2, 4); |
| matrix.setElement(2, 4, value); |
| C++开发社区惯例。在Java语言中,这个规定几乎已成为一种标准。 |
| 18. 术语compute建议使用在需要计算的方法(函数)中。 |
| valueSet->computeAverage(); |
| matrix->computeInverse() |
| 给阅读代码者直接线索,这可能会是一个潜在的超时操作,若重复使用时,可能要考虑缓存的结果。一致的使用此规范可以增加代码可读性。 |
| 19. 术语find建议使用在需要查找的方法(函数)中。 |
| vertex.findNearestVertex(); |
| matrix.findMinElement(); |
| 给阅读代码者直接线索,这会是一个查找的方法(函数),只涉及简单的操作。一致的使用此规范可以增加代码可读性。 |
| 20. 术语initialize建议使用在建立对象或概念。 |
| printer.initializeFontSet(); |
| 美式initialize优于英式initialise。最好避免使用缩略词init。 |
| 21. 代表GUI(图形化)组件的变量推荐使用组件类型作为后缀。 |
| mainWindow, propertiesDialog, widthScale, loginText,leftScrollbar, mainForm, fileMenu, minLabel, exitButton, yesToggle etc. |
| 从名称给出变量类型和对象资源的线索,可以提高代码可读性。 |
| 22. 复数形式在表示对象集合时推荐使用。 |
| vector points; |
| int values[]; |
| 从名称给出变量类型和其中元素要进行的操作的线索,可以提高代码可读性。 |
| 23. 若存在多个对象推荐加上前缀n。 |
| nPoints, nLines |
| 这种标号方式来自于数学,是一种已建立为标明多个对象的惯例。 |
| 24. 独立的(实体)序号推荐加上后缀No。 |
| tableNo, employeeNo |
| 这种标号方式来自于数学,一种已建立为标明独立的(实体)序号的惯例。 |
| 一种更为优雅的方式是使用前缀i:iTable, iEmployee. 这可以有效的命名迭代程序。 |
| 25. 迭代程序(器)推荐命名为i,j,k等。 |
| for (int i = 0; i < nTables); i++) { |
| : |
| } |
| for (vector::iterator i = list.begin(); i != list.end(); i++) { |
| Element element = *i; |
| … |
| } |
| 这种标号方式来自于数学,一种已建立为标明迭代程序(器)的惯例。 |
| 变量名j,k等,推荐只在嵌套的循环中使用。 |
| 26. 布尔变量和方法(函数)推荐加上前缀is。 |
| isSet, isVisible, isFinished, isFound, isOpen |
| C++开发社区和Java部分强制惯例。 |
| 使用后缀解决了一个常见的问题,即选择不好的布尔变量名称,类似:status,flag。而isStatus或isFlag又不太合适,因此程序员将被迫选择更多有含义的名字。 |
| 这里有一些在某些条件下更好的前缀方案供选择,他们是has,can和should前缀: |
| bool hasLicense(); |
| bool canEvaluate(); |
| bool shouldSort(); |
| 27. 互补的名称必须用在互补的操作[1]。 |
| get/set, add/remove, create/destroy, start/stop, insert/delete, |
| increment/decrement, old/new, begin/end, first/last, up/down, min/max, |
| next/previous, old/new, open/close, show/hide, suspend/resume, etc. |
| 通过对称性减少代码复杂程度 |
| 28. 名称中最好避免出现缩略词。 |
| computeAverage(); // NOT: compAvg(); |
| 有两种类型的单词需要注意。第一种是词典中列出的普通词,这些不得简写,千万不要这样写: |
| cmd 代替 command |
| cp 代替 copy |
| pt 代替 point |
| comp 代替 compute |
| init 代替 initialize |
| 等 |
| 另一种是具有特殊含义的词组,通过他们的缩略词可以更自然的为人们所知,这些词组要保持缩略形式,千万不要这样写: |
| HypertextMarkupLanguage 代替 html |
| CentralProcessingUnit 代替 cpu |
| PriceEarningRatio 代替 pe |
| 等 |
| 29. 最好避免特殊地命名指针。 |
| Lineline; // NOT: Line pLine; |
| // NOT: LIne* linePtr; |
| C/C++环境有许多指针类型的变量,所以很多规定几乎无法遵守。此外,程序员应当忽略C++的对象通常斜体的特殊惯例。只有当对象的实际类型具有特殊意义,名称才应强调类型。 |
| 30. 最好避免出现否定的布尔变量名。 |
| bool isError; // NOT: isNoError |
| bool isFound; // NOT: isNotFound |
| 当一个名称出现双重否定时,会出现问题。因为不能从!isNotFound直接看出其含义。 |
| 31. 枚举常量建议以共同的类型名作为前缀。 |
| enum Color { |
| COLOR_RED, |
| COLOR_GREEN, |
| COLOR_BLUE |
| }; |
| 在声明处给出额外的信息,可以看出哪些常量是共通的,以及这些常量所代表的意义。一种代替方案是总是通过共同的类型名联系这些常量:Color::RED, Airline::AIR_FRANCE等。 |
| 注意,枚举名应当是典型的单数Color {…}。一个复数的名称像Colors {…},在声明时并没有什么区别,但这样的用法看起来比较笨拙。 |
| 32. 例外的类推荐加上后缀Exception。 |
| class AccessException |
| { |
| : |
| } |
| 例外的类其实不是程序主要设计的一部分,通过命名让它们相对于其他类更加突出。 |
| 33. 函数(具有返回值的方法)推荐命名为它们执行后的返回或者规程(void型方法)。 |
|
| 增加代码可读性。使得可以很清晰的看出其功能,尤其是其不支持什么功能。这将再一次使代码简化避免副作用的产生。 |
4 文件
4.1 源文件
| 34. C++头文件推荐以.h(推荐)或.hpp作为扩展名。源文件应该为.c++(推荐),.c,.cc或.cpp。 |
| MyClass.c++, MyClass.h |
| 以上均为C++标准支持的文件后缀名。 |
| 35. 类推荐在头文件中声明,并在类名和文件名匹配的源文件中定义。 |
| MyClass.h, MyClass.c++ |
| 易于找到给定类的相关文件。一个明显的例外,即模版类声明和定义必须在同一个.h文件内。 |
| 36. 推荐把所有的定义包含在源文件中。 |
| class MyClass |
| { |
| public: |
| int getValue () {return value_ ;} // NO! |
| … |
| private: |
| int value_; |
| } |
| 头文件应当声明一个接口,源文件应执行它。当寻找,执行时,程序员应总是清楚它可以在源文件中找到。 |
| 37. 文件目录必须在80列内。 |
|
| 80列是编辑器,终端,显示端和调试器中共同的宽度,并且文件将在很多人中共享,所以应当保持这些系统规定参数。在程序员之间传递代码时,避免无意义的断行,能够提升代码可读性。 |
| 38. 分页符和Tab等特殊符号必须避免在程序中出现。 |
|
| 这些字符用在多人编程,多平台时将必定导致编辑器,显示端,终端模拟器或者调试器出现问题。 |
| 39. 分行显示的代码必须使其显而易见[1]。 |
| totalSum = a + b + c + |
| d + e; |
|
| function (param1, param2, |
| param3); |
|
| setText (“Long line split” |
| “into two parts.”); |
|
| for (int tableNo = 0; tableNo < nTables; |
| tableNo += tableStep) { |
| … |
| } |
| 当一段代码超过80列的极限时需要分行。很难给出严格的规定什么情况需要分行,但是上面的例子可以作为参考。通常: |
| 逗号后分行 |
| 操作符后分行 |
| 对准上一行的表达式的开始的新的一行 |
4.2 包含文件和包含语句
| 40. 头文件必须放在一个保护装置中。 |
| #ifndef COM_COMPANY_MODULE_CLASSNAME_H |
| #define COM_COMPANY_MODULE_CLASSNAME_H |
| : |
| #endif // COM_COMPANY_MODULE_CLASSNAME_H |
| 这个结构是为了避免产生编译错误。 |
| 这个命名规定统一了源代码中头文件的位置,避免了命名冲突。 |
| 41. Include语句推荐将其按分类排级,并按等级从低到高排序。在两个Include语句类之间空行分隔。 |
| #include |
| #include |
|
| #include <qt/qbutton.h> |
| #include <qt/qtextfield.h> |
|
| #include “com/company/ui/PropertiesDialog.h” |
| #include “com/company/ui/MainWindow.h” |
| 除了展示给阅读代码者清晰的包含文件结构,同样可以直接看出涉及哪些模块。 |
| Include文件目录不能使用绝对路径。编译器指令应改为用于为包含的文件指示根目录。 |
| 42. include语句必须只放在文件顶端。 |
| 通用规范。避免隐藏(在文件内)的include语句产生编译错误。 |
5 语句
5.1 类型
| 43. 在文件内部使用的量的类型建议在此文件内声明。 |
|
| 确保信息隐藏。 |
| 44. 类的一部分必须分为公共,保护和私有三部分[2][3]。所有部分必须明确。不合适的部分不应混在一起。 |
| 顺序通常为将public部分放在最上,所以当人们可以看到protected/private部分即可不再阅读。 |
| 45. 类型转换必须明确作出。绝不能依赖隐性类型转换。 |
| floatValue = static_cast(intValue); // NOT: floatValue = intValue; |
| 通过这点,程序员可以意识到涉及到的不同类型,并且混合是有意的。 |
5.2 变量
| 46. 变量推荐在声明处初始化。 |
|
| 这确保了变量在任何时候都是有效的。有时不能以一个有效值初始化变量,就像: |
| int x, y, z; |
| getCenter(&x, &y, &z); |
| 这些情况,应当保持未初始化,这要好于初始化假值。 |
| 47. 变量绝不能拥有双重含义。 |
|
| 通过确保变量唯一地代表概念来提升代码可读性。减少副作用导致错误的机会。 |
| 48. 尽量避免使用全局变量。 |
|
| 在C++中,全局变量并不是必需的。同样适用于全局函数或者文件范围的(静态)变量。 |
| 49. 类中变量尽量不要在public中声明。 |
|
| 公有变量将会破环C++的信息隐藏性和封装性。使用私有变量和访问函数代替。这种规则的一种例外是当类本质上是没有行为(类似C的结构体)的一种数据结构。这种情况下,让实例变量变公有是恰当的[2]。 |
| 注意:C++的结构体只是为了和C语言的一致性,并通过减少构建结构体的数量避免增加代码可读性。使用类来代替。 |
| 51. C++的指针和引用的符号推荐靠近类型名,而不是靠近名称。 |
| floatx; // NOT: floatx; |
| int& y; // NOT: int &y ; |
| 变量的指针或者引用属性是类型的性质,而不是命名的。C语言程序员经常在两者中取舍,而C++中遵守这个规则已越来越普遍。 |
| 53. 隐式测试0不推荐用于布尔变量和指针。 |
| if (nLines != 0) // NOT: if (nLines) |
| if (value != 0.0) // NOT: if (value) |
| 依据C++标准,没必要定义int和float型的0和二进制0行为相同。此外,使用明确的测试语句可以使被测试的类型更清晰。建议指针同样不应用0隐式测试也很普遍,即if (line == 0)代替if (line)。后者在C/C++看起来很普遍也可以使用。 |
| 54. 推荐在尽可能小的范围内进行变量声明。 |
|
| 在小范围保持变量的操作,这样可以更简单的控制其作用和副作用。 |
| 55. for()语句内必须只能可包含循环控制语句。 |
| sum = 0; // NOT: for (i = 0, sum = 0; i < 100; i++) |
| for (i = 0; i < 100; i++) sum += value[i]; |
| sum += value[i]; |
| 增强代码的可维护性和可读性。使得控制体和循环包含更加清晰。 |
| 56. 循环变量推荐在循环之前被初始化。 |
| isDone = false; // NOT: bool isDone = false; |
| while (!isDone) { // : |
| : // while (!isDone) { |
| } // : |
| // } |
|
| 57. do-while建议不使用。 |
|
| do-while循环比普通的while循环可读性差,因为其循环控制条件在底部。阅读代码者必须完全看完循环才能明确循环的范围。 |
| 此外,do-while循环非必要。任何do-while循环都可以重写为while或for循环。减少这种结构的使用来提高代码可读性。 |
| 58. 循环体的break和continue建议避免使用。 |
|
| 如果这些语句相较于同样的结构化代码可以提高代码可读性,否则建议不使用。 |
| 60. while(true)这种形式推荐用于无限循环中。 |
| while (true) { |
| : |
| } |
|
| for (;;) { // NO! |
| : |
| } |
|
| while (1) { // NO! |
| : |
| } |
| 依靠对于1进行的测试既没有必要也没有意义。for (;;)可读性极差,并且表面上看不出它实际是个无限循环。 |
5.4 条件语句
| 61. 复杂的条件表达式必须避免。引入临时布尔变量代替[1]。 |
| bool isFinished = (elementNo < 0) |
| bool isRepeatedEntry = elementNo == lastElement; |
| if (isFinished |
| : |
| } |
|
| // NOT: |
| if ((elementNo < 0) |
| elementNo == lastElement) { |
| : |
| } |
| 通过给表达式分配布尔变量,程序可以自动记录(分析)。这样的结构造可以更简单的阅读,改正和维护。 |
| 62. if语句中,象征性的部分推荐放在if()部分,例外放在else()部分。 |
| bool isOk = readFile (fileName); |
| if (isOk) { |
| : |
| } |
| else { |
| : |
| } |
| 确保例外不会使得正常路径难以理解。这对于代码的可读性和性能均很重要。 |
| 63. 条件语句推荐放在单独的一行。 |
| if (isDone) // NOT: if (isDone) doCleanup(); |
| doCleanup(); |
| 这是为了方便调试。当写在一行中,无法清晰的知道真或假。 |
| 64. 条件语句中的执行语句必须避免。 |
| File* fileHandle = open(fileName, “w”); |
| if (!fileHandle) { |
| : |
| } |
|
| // NOT: |
| if (!(fileHandle = open(fileName, “w”))) { |
| : |
| } |
| 条件语句中的执行语句使得代码难以阅读。这需要C/C++的新程序员尤其注意。 |
5.5 杂项
| 65. 代码中推荐尽量避免幻数的使用。除了0和1,其他的推荐考虑声明常量代替。 |
|
| 如果一个数字自身没有一个显而易见的含义,通过引入命名常量代替来增加代码可读性。另一种不同的方法是在量被访问处引入一个方法(函数)。 |
| 66. 浮点数推荐总是带有小数点和至少一位小数。 |
| double total = 0.0; // NOT: double total = 0; |
| double speed = 3.0e8; // NOT: double speed = 3e8; |
|
| double sum; |
| : |
| sum = (a + b) * 10.0; |
| 这强调了整型和浮点型不同的性质。这两种算数模型是完全不同且毫不相容的概念。 |
| 同样,在上述最后一个例子中,他在某处强调了代码中变量(sum)分配的类型,虽然这可能看起来不太明显。 |
| 67. 小于0的浮点数推荐总是在小数点前写上数码0。 |
| double total = 0.5; // NOT: double total = .5; |
| C++的数字和表达式系统借鉴于数学,并且语法应当只要允许就遵守数学惯例。同样,0.5的可读性明显大于.5;也不会使其和5混淆。 |
| 68. 函数必须要明确列出返回值类型。 |
| int getValue() // NOT: getValue() |
| { |
| : |
| } |
| 如果没有明确列出,C++将会默认返回值为int型。程序员不得依赖这个特点,因为这会使其他程序员觉得这种形式难以理解。 |
| 69. goto语句推荐尽量避免使用。 |
|
| goto语句打破了代码的结构。只有在非常少的情况下(例如跳出一个深层的嵌套结构)goto语句建议考虑,并且仅当替代的选择结构被证明有较差的可读性。 |
| 70. 0推荐用来代替NULL。 |
|
| NULL是标准C语言库的部分,但在C++中已被淘汰。 |
6.1 布局
| 71. 基本的缩进推荐为2个空格。 |
| for (i = 0; i < nElements; i++) |
| a[i] = 0; |
| 缩进1对于强调代码的逻辑布局显得太短。 |
| 缩进大于4使得深层的嵌套代码很难阅读,并且增加了必须分行的可能。选择缩进2,3,4之一,2和4更加普遍,且选择2可以降低代码分行的可能。 |
| 72. 代码块的布局推荐如例1(推荐)或者例2,但绝不要如例3[4]。函数和类代码块必须使用例2的布局。 |
| while (!done) { |
| doSomething(); |
| done = moreToDo(); |
| } |
|
| while (!done) |
| { |
| doSomething(); |
| done = moreToDo(); |
| } |
|
| while (!done) |
| { |
| doSomething(); |
| done = moreToDo(); |
| } |
| 例3引入一个特别的缩进,使得强调的代码逻辑结构不如例1和例2清晰。 |
| 73. 类推荐使用以下格式声明: |
| class SomeClass : public BaseClass |
| { |
| public: |
| … |
|
| protected: |
| … |
|
| private: |
| … |
| } |
| 这一定程度上遵守了以上通用的代码块规则。 |
| 74. 方法(函数)推荐使用以下格式定义: |
| void someMethod() |
| { |
| … |
| } |
| 这遵守了以上通用的代码块规则。 |
| 75. if-else语句推荐使用以下格式: |
| if (condition) { |
| statements; |
| } |
|
| if (condition) { |
| statements; |
| } |
| else { |
| statements; |
| } |
|
| if (condition) { |
| statements; |
| } |
| else if (condition) { |
| statements; |
| } |
| else { |
| statements; |
| } |
| 这一定程度上遵守了以上通用的代码块规则。但是,如果一个else从句和紧邻的if或else从句的括号在一行也是可行的,即: |
| if (condition) { |
| statements; |
| } else { |
| statements; |
| } |
| 代码中if-else语句每个部分单独一行是更好的选择。这将使得语句控制变得更加简单,例如当需要移动else部分时。 |
| 76. for语句推荐使用以下格式: |
| for (initialization; condition; update) { |
| statements; |
| } |
| 这遵守了以上通用的代码块规则。 |
| 77. 空的for语句推荐使用以下格式: |
| for (initialization; condition; update) |
| ; |
| 这强调了for语句是空的,并且由于这是故意的所以使得代码阅读者清楚。不过,空循环应当避免。 |
| 78. while语句推荐使用以下格式: |
| while (condition) { |
| statements; |
| } |
| 这遵守了以上通用的代码块规则。 |
| 79. do-while语句推荐使用以下格式: |
| do { |
| statements; |
| } while (condition); |
| 这遵守了以上通用的代码块规则。 |
| 80. switch语句推荐使用以下格式: |
| switch (condition) { |
| case ABC : |
| statements; |
| // Fallthrough |
|
| case DEF : |
| statements; |
| break; |
|
| case XYZ : |
| statements; |
| break; |
|
| default : |
| statements; |
| break; |
| } |
| 注意整个switch语句中每个case关键字相对缩进。这使得整个switch语句清晰突出。此外注意“:”前的额外的空格。当case语句不包含break语句应当有明确的贯穿注释。省略break是个普通错误,如果故意省略必须要让代码清晰。 |
| 81. try-catch语句推荐使用以下格式: |
| try { |
| statements; |
| } |
| catch (Exception& exception) { |
| statements; |
| } |
| 这一定程度上遵守了以上通用的代码块规则。if-else语句关于大括号的规则通样可以运用于try-catch语句。 |
| 82. 单独的if-else,for,或while语句建议不加大括号。 |
| if (condition) |
| statement; |
|
| while (condition) |
| statement; |
|
| for (initialization; condition; update) |
| statement; |
| 通常推荐应当是在任何情况大括号都不应缺少。但是,通常语言构造中大括号是用来归纳多条语句。单条语句使用大括号会显得多余。不过,有一种反对此语法的声音,如果增加语句而忘记加大括号,代码就会出错。但不论怎样,代码绝不应当为了适应可能发生的改变。 |
| 83. 函数的返回值类型建议放在函数名上方左对齐。 |
| void |
| MyClass::myMethod(void) |
| { |
| : |
| } |
| 这使得更容易发现函数名,因为它们都在第一列开始。 |
6.2 空白
| 84. |
| -常规操作符推荐在前后加空格以间隔。 |
| -C++保留字后推荐加空格以间隔。 |
| -逗号后推荐加空格以间隔。 |
| -冒号推荐在前后加空格以间隔 |
| -for循环语句内的分号后推荐加空格以间隔。 |
| a = (b + c) *d; // NOT: a=(b+c)*d |
|
| while (true) // NOT: while(true) |
| { |
| … |
|
| doSomething(a, b, c, d); // NOT: doSomething(a,b,c,d); |
|
| case 100 : // NOT: case 100: |
|
| for (i = 0; i < 10; i++) { // NOT: for(i=0;i<10;i++){ |
| … |
| 让语句的每一部分都独立出来。增加代码可读性。在C++代码中,很难给出一个完整的空格使用建议列表。以上的例子只是给出一个通常的建议。 |
| 85. 方法(函数)名后若相连其他名称,建议以空格隔开。 |
| doSomething (currentFile); |
| 让每个名称独立出来。增加代码可读性。当后面没有名称,空格可以省略(doSomething()),这种情况名称就不用独立。左圆括号后是否加空格这个规范并没有要求,即都是可以的。这个规范通常在右括号前留一个空格: doSomething( currentFile ); 这样做使得每个名称作为不同含义而独立,但是右括号前的空格更像是一种艺术,并且没有那个空格的语句会显得不对称(doSomething( currentFile);)。 |
| 86. 逻辑集合代码块推荐以空行分隔。 |
| Matrix4x4 matrix = new Matrix4x4(); |
|
| double cosAngle = Math.cos(angle); |
| double sinAngle = Math.sin(angle); |
|
| matrix.setElement(1, 1, cosAngle); |
| matrix.setElement(1, 2, sinAngle); |
| matrix.setElement(2, 1, -sinAngle); |
| matrix.setElement(2, 2, cosAngle); |
|
| multiply(matrix); |
| 通过在不同的逻辑集合代码块加入空行增加代码可读性。 |
| 87. 方法(函数)推荐以三个空行分隔。 |
| 通过大空行使得方法(函数)在类中独立。 |
| 88. 变量声明建议左对齐。 |
| AsciiFile* file; |
| int nPoints; |
| float x, y; |
| 增加代码可读性。变量可以通过对齐简单的分别类型。 |
| 89. 当使用对齐可以增加代码可读性就使用对齐。 |
| if (a == lowValue) compueSomething(); |
| else if (a == mediumValue) computeSomethingElse(); |
| else if (a == highValue) computeSomethingElseYet(); |
|
| value = (potential * oilDensity) / constant1 + |
| (depth * waterDensity) / constant2 + |
| (zCoordinateValue * gasDensity) / constant3; |
|
| minPosition = computeDistance(min, x, y, z); |
| averagePosition = computeDistance(average, x, y, z); |
|
| switch (value) { |
| case PHASE_OIL : strcpy(phase, “Oil”); break; |
| case PHASE_WATER : strcpy(phase, “Water”); break; |
| case PHASE_GAS : strcpy(phase, “Gas”); break; |
| } |
| 尽管这点违反了普遍的规范,但是代码中很多地方需要如此来增加代码可读性就是允许的。上述很多情况下不得不要将代码对齐。代码对齐的通用规范很难给出,但是以上的例子就是很通用的线索。 |
6.3 注释
| 9. 无厘头的代码不需要注释,需要重写![1]。 |
|
| 通常,注释应当通过代码命名的选择和明确的逻辑结构提升自身文档化而尽量减少使用。 |
| 91. 所有注释推荐使用英文书写[2]。 |
|
| 在国际化开发环境中,英文是首选语言。 |
| 92. 所有注释均用//,包括多行注释。 |
| // Comment spanning |
| // more than one line. |
| 自从多级C注释不被支持,使用//注释确保注释所有文件部分总是可能的。调试等目的时使用/**/。 |
| 推荐真正注释和//之间保留一个空格,并且注释应当以一个大写字母开头,以句号结束。 |
| 93. 推荐将注释和代码同等位置(缩进)。 |
| while (true) { // NOT: while (true) { |
| // Do something // Do something |
| something(); something(); |
| } } |
| 这是为了避免注释打破程序的逻辑结构。 |
| 94. 类和方法的初始注释推荐依照javaDoc规定。 |
|
| 作为标准的类和方法文档,Java开发社区比C/C++开发社区要更加成熟。这是因为标准自动化Javadoc工具是开发套件的一部分,并且它用这些注释帮助做出高质量超文本文档。类Javadoc工具同样可用于C++。这些相同的标签语法类似于Javadoc。看这里的Doc++或Doxygen实例。 |
7 引用(以下不再翻译)
[1] Code Complete, Steve McConnell - Microsoft Press
[2] Programming in C++, Rules and Recommendations, M Henricson, e. Nyquist, Ellemtel (Swedish telecom)
http://www.doc.ic.ac.uk/lab/cplus/c%2b%2b.rules/
[3] Wildfire C++ Programming Style, Keith Gabryelski, Wildfire Communications Inc.
http://www.wildfire.com/~ag/Engineering/Development/C++Style/
[4] C++ Coding Standard, Todd Hoff
http://www.possibility.com/Cpp/CppCodingStandard.htm
[5] Doxygen documentation system
http://www.stack.nl/~dimitri/doxygen/index.html

[Translate]C++编程代码风格指南 由 萌面大道 创作,采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于http://geosoft.no/development/cppstyle.html上的作品创作。
本许可协议授权之外的使用权限可以从 http://maimieng.com/ 处获得。