C++代码规范

来源:互联网 发布:计算概率的软件 编辑:程序博客网 时间:2024/05/22 00:14

命名规定

1. 通用命名规则(General Naming Rules)

函数命名、变量命名、文件命名应具有描述性,不要过度缩写,类型和变量应该是名词,函数名可以用“命令性”动词。

如何命名:

尽可能给出描述性名称,不要节约空间,让别人很快理解你的代码更重要,好的命名选择:

2. 文件命名(File Names)

文件名要全部小写,可以包含下划线(_)或短线(-),按项目约定来。
可接受的文件命名:
my_useful_class.cc
my-useful-class.cc
myusefulclass.cc

3. 类型命名(Type Names)
类型命名每个单词以大写字母开头,不包含下划线:MyExcitingClass、MyExcitingEnum。

4. 变量命名(Variable Names)
变量名一律小写,单词间以下划线相连,类的成员变量以下划线结尾,如:

string table_name; // OK - uses underscore.
string tablename; // OK - all lowercase.

类数据成员:
结构体的数据成员可以和普通变量一样,不用像类那样接下划线:
struct UrlTableProperties {
string name;
int num_entries;
}

5. 常量命名(Constant Names)
在名称前加k:kDaysInAWeek。

6. 函数命名(Function Names)
普通函数(regular functions,译者注,这里与访问函数等特殊函数相对)大小写混合,
存取函数(accessors and mutators)则要求与变量名匹配:MyExcitingFunction()、
MyExcitingMethod()、my_exciting_member_variable()、
set_my_exciting_member_variable()。

7. 命名空间(Namespace Names)
命名空间的名称是全小写的,其命名基于项目名称和目录结构:google_awesome_project。

8. 枚举命名(Enumerator Names)
枚举值应全部大写,单词间以下划线相连:MY_EXCITING_ENUM_VALUE。
枚举名称属于类型,因此大小写混合:UrlTableErrors。
enum UrlTableErrors {
OK = 0,
ERROR_OUT_OF_MEMORY,

ERROR_MALFORMED_INPUT,
};

1. 总体规则:不要随意缩写,如果说ChangeLocalValue写作ChgLocVal还有情可原的话,把ModifyPlayerName写作MdfPlyNm就太过分了,除函数名可适当为动词外,其他命名尽量使用清晰易懂的名词;
2. 宏、枚举等使用全部大写+下划线;
3. 变量(含类、结构体成员变量)、文件、命名空间、存取函数等使用全部小写+下划线 ,类成员变量以下划线结尾,全局变量以g_开头;
4. 普通函数、类型(含类与结构体、枚举类型)、常量等使用大小写混合,不含下划线;
5. 参考现有或相近命名约定。

注释

注释虽然写起来很痛苦,但对保证代码可读性至为重要,下面的规则描述了应该注释什么、注释在哪儿。当然也要记住,注释的确很重要,但最好的代码本身就是文档(selfdocumenting),类型和变量命名意义明确要比通过注释解释模糊的命名好得多。

1. 注释风格(Comment Style)
使用//或/* */,统一就好。
//或/* */都可以,//只是用的更加广泛,在如何注释和注释风格上确保统一。
2. 文件注释(File Comments)
在每一个文件开头加入版权公告,然后是文件内容描述。
法律公告和作者信息:
每一文件包含以下项,依次是:
1) 版权(copyright statement):如Copyright 2008 Google Inc.;
2) 许可版本(license boilerplate):为项目选择合适的许可证版本,如Apache 2.0、BSD、LGPL、GPL;
3) 作者(author line):标识文件的原始作者。
如果你对其他人创建的文件做了重大修改,将你的信息添加到作者信息里,这样当其他人对该文件有疑问时可以知道该联系谁。

3. 类注释(Class Comments)
每个类的定义要附着描述类的功能和用法的注释。

// Iterates over the contents of a GargantuanTable. Sample usage:// GargantuanTable_Iterator* iter = table->NewIterator();// for (iter->Seek("foo"); !iter->done(); iter->Next()) {// process(iter->key(), iter->value());// }// delete iter;class GargantuanTable_Iterator {...};
4. 函数注释(Function Comments)
函数声明处注释描述函数功能,定义处描述函数实现。
函数声明:

注释于声明之前,描述函数功能及用法,注释使用描述式("Opens the file")而非指令式("Open the file");注释只是为了描述函数而不是告诉函数做什么。通常,注释不会描述函数如何实现,那是定义部分的事情。

函数声明处注释的内容:

1) inputs(输入)及outputs(输出);
2) 对类成员函数而言:函数调用期间对象是否需要保持引用参数,是否会释放这些参数;
3) 如果函数分配了空间,需要由调用者释放;
4) 参数是否可以为NULL;
5) 是否存在函数使用的性能隐忧(performance implications);
6) 如果函数是可重入的(re-entrant),其同步前提(synchronization assumptions)
是什么?

举例如下:

// Returns an iterator for this table. It is the client's// responsibility to delete the iterator when it is done with it,// and it must not use the iterator once the GargantuanTable object// on which the iterator was created has been deleted.//// The iterator is initially positioned at the beginning of the table.//// This method is equivalent to:// Iterator* iter = table->NewIterator();// iter->Seek("");// return iter;// If you are going to immediately seek to another place in the// returned iterator, it will be faster to use NewIterator()// and avoid the extra seek.Iterator* GetIterator() const;
5. 变量注释(Variable Comments)
通常变量名本身足以很好说明变量用途,特定情况下,需要额外注释说明。
类数据成员:
每个类数据成员(也叫实例变量或成员变量)应注释说明用途,如果变量可以接受NULL 或-1
等警戒值(sentinel values),须说明之,如:
private:// Keeps track of the total number of entries in the table.// Used to ensure we do not go over the limit. -1 means// that we don't yet know how many entries the table has.int num_total_entries_;
全局变量(常量):
和数据成员相似,所有全局变量(常量)也应注释说明含义及用途,如:
// The total number of tests cases that we run through in this regressiontest.const int kNumTestCases = 6;
6. 实现注释(Implementation Comments)
对于实现代码中巧妙的、晦涩的、有趣的、重要的地方加以注释。
代码前注释:
出彩的或复杂的代码块前要加注释,如:
// Divide result by two, taking into account that x// contains the carry from the add.for (int i = 0; i < result->size(); i++) {x = (x << 8) + (*result)[i];(*result)[i] = x >> 1;x &= 1;}
注释也是比较人性化的约定了:
1. 关于注释风格,很多C++的 coders更喜欢行注释,C coders或许对块注释依然情有独钟,或者在文件头大段大段的注释时使用块注释;
2. 文件注释可以炫耀你的成就,也是为了捅了篓子别人可以找你;
3. 注释要言简意赅,不要拖沓冗余,复杂的东西简单化和简单的东西复杂化都是要被鄙视的;
4. 对于Chinese coders来说,用英文注释还是用中文注释,it is a problem,但不管怎样,注释是为了让别人看懂,难道是为了炫耀编程语言之外的你的母语或外语水平吗;
5. 注释不要太乱,适当的缩进才会让人乐意看,但也没有必要规定注释从第几列开始(我自己写代码的时候总喜欢这样),UNIX/LINUX下还可以约定是使用tab还是space,个人倾向于space;
6. TODO很不错,有时候,注释确实是为了标记一些未完成的或完成的不尽如人意的地方 ,这样一搜索,就知道还有哪些活要干,日志都省了。
格式

1. 行长度(Line Length)
每一行代码字符数不超过80。

2. 非ASCII字符(Non-ASCII Characters)
尽量不使用非ASCII 字符,使用时必须使用UTF-8 格式。

3. 空格还是制表位(Spaces vs. Tabs)
只使用空格,每次缩进2 个空格。
使用空格进行缩进,不要在代码中使用tabs,设定编辑器将tab 转为空格。

4. 函数声明与定义(Function Declarations and Definitions)
返回类型和函数名在同一行,合适的话,参数也放在同一行。
函数看上去像这样:

ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {DoSomething();...}
如果同一行文本较多,容不下所有参数:

ReturnType ClassName::ReallyLongFunctionName(Type par_name1,Type par_name2,Type par_name3) {DoSomething();...}
注意以下几点:
1) 返回值总是和函数名在同一行;
2) 左圆括号(open parenthesis)总是和函数名在同一行;
3) 函数名和左圆括号间没有空格;
4) 圆括号与参数间没有空格;
5) 左大括号(open curly brace)总在最后一个参数同一行的末尾处;
6) 右大括号(close curly brace)总是单独位于函数最后一行;
7) 右圆括号(close parenthesis)和左大括号间总是有一个空格;
8) 函数声明和实现处的所有形参名称必须保持一致;
9) 所有形参应尽可能对齐;
10) 缺省缩进为2 个空格;
11) 独立封装的参数保持4 个空格的缩进。
5. 函数调用(Function Calls)
尽量放在同一行,否则,将实参封装在圆括号中。
函数调用遵循如下形式:
bool retval = DoSomething(argument1, argument2, argument3);
如果同一行放不下,可断为多行,后面每一行都和第一个实参对齐,左圆括号后和右圆括号
前不要留空格:

bool retval = DoSomething(averyveryveryverylongargument1,argument2, argument3);
如果函数名太长,以至于超过行最大长度,可以将所有参数独立成行:
if (...) {......if (...) {DoSomethingThatRequiresALongFunctionName(very_long_argument1, // 4 space indentargument2,argument3,argument4);}
6. 条件语句(Conditionals)
更提倡不在圆括号中添加空格,关键字else 另起一行。
对基本条件语句有两种可以接受的格式,一种在圆括号和条件之间有空格,一种没有。
最常见的是没有空格的格式,那种都可以,还是一致性为主。如果你是在修改一个文件,参考当前已有格式;如果是写新的代码,参考目录下或项目中其他文件的格式,还在徘徊的话,就不要加空格了。
if (condition) { // no spaces inside parentheses... // 2 space indent.} else { // The else goes on the same line as the closing brace....}
7. 循环和开关选择语句(Loops and Switch Statements)
switch 语句可以使用大括号分块;空循环体应使用{}或continue。
switch 语句中的case 块可以使用大括号也可以不用,取决于你的喜好,使用时要依下文所述。
如果有不满足case 枚举条件的值,要总是包含一个default(如果有输入值没有case 去处理,编译器将报警)。如果default 永不会执行,可以简单的使用assert:
switch (var) {case 0: { // 2 space indent... // 4 space indentbreak;}case 1: {...break;}default: {assert(false);}}
空循环体应使用{}或continue,而不是一个简单的分号:
while (condition) {// Repeat test until it returns false.}for (int i = 0; i < kSomeNumber; ++i) {} // Good - empty body.while (condition) continue; // Good - continue indicates no logic.while (condition); // Bad - looks like part of do/while loop.
9. 布尔表达式(Boolean Expressions)
如果一个布尔表达式超过标准行宽(80 字符),如果断行要统一一下。
下例中,逻辑与(&&)操作符总位于行尾:
if (this_one_thing > this_other_thing &&a_third_thing == a_fourth_thing &&yet_another & last_one) {...}
两个逻辑与(&&)操作符都位于行尾,可以考虑额外插入圆括号,合理使用的话对增强可读性是很有帮助的。
首先说明,对于代码格式,因人、因系统各有优缺点,但同一个项目中遵循同一标准还是有必要的:
1. 行宽原则上不超过80列,把22寸的显示屏都占完,怎么也说不过去;
2. 尽量不使用非ASCII字符,如果使用的话,参考UTF-8格式(尤其是UNIX/Linux下,Windows下可以考虑宽字符),尽量不将字符串常量耦合到代码中,比如独立出资源文件,这不仅仅是风格问题了;
3. UNIX/Linux下无条件使用空格,MSVC的话使用Tab也无可厚非;
4. 函数参数、逻辑条件、初始化列表:要么所有参数和函数名放在同一行,要么所有参数并排分行;
5. 除函数定义的左大括号可以置于行首外,包括函数/类/结构体/枚举声明、各种语句的左大括号置于行尾,所有右大括号独立成行;
6. ./->操作符前后不留空格,*/&不要前后都留,一个就可,靠左靠右依各人喜好;
7. 预处理指令/命名空间不使用额外缩进,类/结构体/枚举/函数/语句使用缩进;
8. 初始化用=还是()依个人喜好,统一就好;
9. return不要加();
10. 水平/垂直留白不要滥用,怎么易读怎么来。