注释虽然写起来很痛苦,但对保证代码可读性至关重要。下面的规则描述了如何注释以及在哪儿注释。当然也要记住:注释固然很重要,但最好的代码本身应该是自文档化的。有意义的类型名和变量名,要远胜过要用注释解释的含糊不清的名字。
你写的注释是给下一个需要理解你的代码的人看的。慷慨些吧,下一个人可能就是你!
使用//或者/**/,统一就好。
//或者/**/都可以,但是//更常用。要在如何注释以及注释风格上确保统一。
在每一个文件开头加入版权公告,然后是文件内容描述。
法律公告和作者信息:
每个文件都应该包含以下项,依次是: 版权声明(比如,Copyright 2008 Google Inc.)许可证。为项目选择合适的许可证版本(比如Apache 2.0, BSD, LGPL, GPL)作者:标识文件的原始作者如果你对原始作者的文件做了重大修改,将你的信息添加到作者信息里。这样当其他人对该文件有疑问时可以知道该联系谁。文件内容:
紧接着版权许可和作者信息之后,每个文件都要用注释描述文件内容。通常.h文件要对声明的类的功能和用法作简单说明。.cc文件通常包含了更多的实现细节或算法技巧讨论,如果你感觉这些实现细节或者算法技巧讨论对于理解.h文件有帮助,可以将该注释挪到.h中,并且在.cc中指出文档在.h中。不要简单的在.h和.cc间复制注释。这种偏离了注释的实际意义。每个类的定义都要附带一份注释,描述类的功能和用法。
// 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 { ...};如果你觉得已经在文件顶部详细描述了该类,像直接简单的上来一句“完整描述见文件顶部”也不打紧,但务必确保有这类注释。
如果类有任何同步前提,文档说明之。如果该类的实例可被多线程访问,要特别注意文档说明多线程环境下相关的规则和常量使用。
函数声明处注释描述函数的功能;定义处描述函数实现。
函数声明:
注释位于声明之前,对函数功能及用法进行描述。注释使用叙述式(”Opens the file”)而非指令式(”Open the file”);注释只是为了描述函数,而不是命令函数做什么。通常,注释不会描述函数如何工作。那是函数定义部分的事情。函数声明处注释的内容: 函数的输入输出。对类成员函数而言,函数调用期间对象是否需要保持引用参数,是否会释放这些参数。如果函数分配了空间,需要由调用者释放。参数是否可以被NULL。是否存在函数使用上的性能隐患。如果函数是可重入的,其同步的前提是什么。// 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;上面给出了一个具体示例。但是也要避免啰啰嗦嗦,或做些显而易见的说明。注释构造/析构函数时,切记读代码的人知道构造/析构函数是干啥的,所以“destroys this object”这样的注释是没有意义的。注明构造函数对参数做了什么(例如,是否取得指针所有权)以及析构函数清理了什么。如果都是些无关紧要的内容,直接省掉注释。析构函数前没有注释是很正常的。函数定义:
每个函数定义要用注释说明函数功能和实现要点。比如说说你用的编程技巧,实现的大致步骤,或者解释如此实现的理由。为什么前半部分要加锁而后半部分不需要等等之类的。不要从.h
文件或其他地方的函数声明处直接复制注释。简要重述函数功能是可以的,但注释重点要放在如何实现上。通常变量名本身足以很好地说明变量用途。但在某些情况下,也需要额外的注释说明。
类数据成员:
每个类数据成员(也叫实例变量或者成员变量)都应该用注释说明用途。如果变量可以接受NULL
或者-1
等警戒值,需要加以说明。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;对于代码中巧妙的,晦涩的,有趣的,重要的地方加以注释。
代码前注释:
巧妙或者复杂的代码前要加以注释,比如:// 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.{ // One space before comment when opening a new scope is allowed, // thus the comment lines up with the following comments and code. DoSomethingElse(); // Two spaces before line comments normally.}向函数传入NULL,布尔值或者整数时,要注释说明含义,或使用常量让代码望文知义。例如对比:// First version:bool success = CalculateSomething(interesting_value, 10, false, NULL); // What are these arguments??// Second version:bool success = CalculateSomething(interesting_value, 10, // Default base value. false, // Not the first time we're calling this.// Third version:const int kDefaultBaseValue = 10;const bool kFirstTimeCalling = false;Callback *null_callback = NULL;bool success = CalculateSomething(interesting_value, kDefaultBaseValue, kFirstTimeCalling, null_callback);不允许: - 注意永远不要用自然语言翻译代码作为注释。要假设读代码的人C++水平比你高,即使他/她可能不知道你的用意:
// Bad styles:// 现在, 检查 b 数组并确保 i 是否存在,// 下一个元素是 i+1.... // 天哪. 令人崩溃的注释.注意标点,拼写和语法;写的好的注释比差的要易读的多。
注释的通常写法是包含正确的大小写和结尾句号的完整语句。短一点的注释(如代码行尾注释)可以随意点,但依然要注意风格的一致性。完整的语句可读性更好,也可以说明该注释是完整的,而不是一些不成熟的想法。
虽然被别人指出该用分号时却用了逗号多少有些尴尬,但清晰易读的代码还是很重要的。正确的标点,拼写和语法对此会有帮助。
对那些临时的,短期的解决方案,或已经够好但仍不完美的代码使用TODO注释。
TODO
注释要使用全大写的字符串TODO
,在随后的圆括弧里面写上你的大名,邮件地址,或其它身份标识。冒号是可选的。主要目的是让添加注释的人(也是可以请求提供更多细节的人)可以根据规范的TODO
格式进行查找。添加注释并不意味着你要自己来修正。
如果TODO
是为了在“将来某一天做某事”,可以附上一个非常明确的时间“Fix by November 2005”或者一个明确的事项(例如“Remove this code when all clients can handle xml responses.”)。
通过弃用注释(DEPRECATED comments)以标记某接口点(interface points)已弃用。
您可以写上包含全大写的DEPRECATED的注释,以标记某接口为弃用状态。注释可以放在接口声明前,或者同一行。
在DEPRECATED一词后,留下您的名字,邮箱地址以及括弧补充。
仅仅标记接口为DEPRECATED并不会让大家不约而同的弃用,您还得亲自主动修正调用点(callsites),或者是找个帮手。
修正好的代码应该不会再设计弃用接口点了,着实改用新接口点。如果您不知从何下手,可以找标记弃用注释的当事人一起商量。
新闻热点
疑难解答
图片精选