Google C++编程风格指南(六):代码注释
C++ #编程风格指南2012-11-09 11:08
- 注释
注释虽然写起来很痛苦,但对保证代码可读性至为重要,下面的规则描述了应该注释什么、注释在哪儿。当然也要记住,注释的确很重要,但最好的代码本身就是文档(self-documenting),类型和变量命名意义明确要比通过注释解释模糊的命名好得多。
注释是为别人(下一个需要理解你的代码的人)而写的,认真点吧,那下一个人可能就是你!
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):标识文件的原始作者。
如果你对其他人创建的文件做了重大修改,将你的信息添加到作者信息里,这样当其他人对该文件有疑问时可以知道该联系谁。
文件内容:
每一个文件版权许可及作者信息后,都要对文件内容进行注释说明。
通常,.h文件要对所声明的类的功能和用法作简单说明,.cc文件包含了更多的实现细节或算法讨论,如果你感觉这些实现细节或算法讨论对于阅读有帮助,可以把.cc中的注释放到.h中,并在.cc中指出文档在.h中。
不要单纯在.h和.cc间复制注释,复制的注释偏离了实际意义。
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 { ... };
如果你觉得已经在文件顶部详细描述了该类,想直接简单的来上一句“完整描述见文件顶部”的话,还是多少在类中加点注释吧。
如果类有任何同步前提(synchronization assumptions),文档说明之。如果该类的实例可被多线程访问,使用时务必注意文档说明。
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;
但不要有无谓冗余或显而易见的注释,下面的注释就没有必要加上“returns false otherwise”,因为已经暗含其中了:
// Returns true if the table cannot hold any more entries. http://yige.org bool IsTableFull();
注释构造/析构函数时,记住,读代码的人知道构造/析构函数是什么,所以“destroys this object”这样的注释是没有意义的。说明构造函数对参数做了什么(例如,是否是指针的所有者)以及析构函数清理了什么,如果都是无关紧要的内容,直接省掉注释,析构函数前没有注释是很正常的。
函数定义:
每个函数定义时要以注释说明函数功能和实现要点,如使用的漂亮代码、实现的简要步骤、如此实现的理由、为什么前半部分要加锁而后半部分不需要。
不要从.h文件或其他地方的函数声明处直接复制注释,简要说明函数功能是可以的,但重点要放在如何实现上。
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 regression test. 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; }
行注释:
比较隐晦的地方要在行尾加入注释,可以在代码之后空两格加行尾注释,如:
// If we have enough memory, mmap the data portion too. mmap_budget = max<int64>(0, mmap_budget - index_->length()); if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock)) return; // Error already logged.
注意,有两块注释描述这段代码,当函数返回时注释提及错误已经被记入日志。
前后相邻几行都有注释,可以适当调整使之可读性更好:
... DoSomething(); // Comment here so the comments line up. DoSomethingElseThatIsLonger(); // Comment here so there are two spaces between // the code and the comment. ...
NULL、true/false、1、2、3……:
向函数传入、布尔值或整数时,要注释说明含义,或使用常量让代码望文知意,比较一下:
bool success = CalculateSomething(interesting_value, 10, false, NULL); // What are these arguments??
和:
bool success = CalculateSomething(interesting_value, 10, // Default base value. false, // Not the first time we're calling this. NULL); // No callback.
使用常量或描述性变量:
const int kDefaultBaseValue = 10; const bool kFirstTimeCalling = false; Callback *null_callback = NULL; bool success = CalculateSomething(interesting_value, kDefaultBaseValue, kFirstTimeCalling, null_callback);
不要:
注意永远不要用自然语言翻译代码作为注释,要假设读你代码的人C++比你强:D:
// Now go through the b array and make sure that if i occurs, // the next element is i+1. ... // Geez. What a useless comment.
7. 标点、拼写和语法(Punctuation, Spelling and Grammar)
留意标点、拼写和语法,写的好的注释比差的要易读的多。
注释一般是包含适当大写和句点(.)的完整的句子,短一点的注释(如代码行尾的注释)可以随意点,依然要注意风格的一致性。完整的句子可读性更好,也可以说明该注释是完整的而不是一点不成熟的想法。
虽然被别人指出该用分号(semicolon)的时候用了逗号(comma)有点尴尬。清晰易读的代码还是很重要的,适当的标点、拼写和语法对此会有所帮助。
8. TODO注释(TODO Comments)
对那些临时的、短期的解决方案,或已经够好但并不完美的代码使用TODO注释。
这样的注释要使用全大写的字符串TODO,后面括号(parentheses)里加上你的大名、邮件地址等,还可以加上冒号(colon):目的是可以根据统一的TODO格式进行查找:
// TODO(kl@gmail.com): Use a "*" here for concatenation operator. // TODO(Zeke) change this to use relations.
上是为了在“将来某一天做某事”,可以加上一个特定的时间("Fix by November 2005")或事件("Remove this code when all clients can handle XML responses.")。
相关文章
- Google C++编程风格指南(五):命名约定 2012/11/09
- Google C++编程风格指南(四):智能指针和其他C++特性 2012/11/09
- Google C++编程风格指南(三):C++ 类 2012/11/09
- Google C++编程风格指南(二):作用域 2012/11/09
- Google C++编程风格指南(一):头文件 2012/11/09
- C++获取操作的精确时间 2012/11/09
- 用信号处理函数处理ctrl+C和段错误 2012/11/09
- Qt中HMAC hash 算法 2012/11/09
- C++ 中KMP字符串匹配算法 2012/11/09
- c/c++编译笔记 2012/11/07