4. 我使用的C/C++编写规范之注释

  闲来无事,把自用的C/C++编程规范整理一下。内容主要来自Google与华为,并参考了一点微软。


  注释虽然写起来很痛苦,但对保证代码可读性至为重要,下面的规则描述了应该注释什么、注释在哪儿。当然也要记住,注释的确很重要,但最好的代码本身就是文档(self-documenting),类型和变量命名意义明确要比通过注释解释模糊的命名好得多。
  注释是为别人(下一个需要理解你的代码的人)而写的,认真点吧,那下一个人可能就是你!
 
1.通用规则
  1)除了文件头使用/ /,其余地方一律使用//;
  2)将注释缩进为被其描述的代码的同一级;
  3)在注释中使用首字母大写(用英语注释时)的完整的句子,以及适当的标点符号和拼写;
  4)一定不要让注释仅仅是重复代码;
  5)注释应放在其代码上方相邻位置或右方,不可放在下面;
  6)避免在注释中使用缩写,除非是业界通用或子系统内标准化的缩写;
  7)避免在一行代码或表达式的中间插入注释;
  8)注释应考虑程序易读及外观排版的因素,使用的语言若是中、英兼有的,建议多使用中文,除非能用非常流利准确的英文表达。对于有外籍员工的,由产品确定注释语言;
  9)文件头、函数头、全局常量变量、类型定义的注释格式采用doxygen格式。
 
2.文件注释
  请参考“文件头”一文。
 
3.类注释
  每个类的定义要附着描述类的功能和用法的注释。

// 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.函数注释
  函数声明处注释描述函数功能,定义处描述函数实现。
  函数声明:
  注释于声明之前,描述函数功能及用法,注释使用描述式("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.
bool IsTableFull();

  注释构造/析构函数时,记住,读代码的人知道构造/析构函数是什么,所以“destroys this object”这样的注释是没有意义的。说明构造函数对参数做了什么(例如,是否是指针的所有者)以及析构函数清理了什么,如果都是无关紧要的内容,直接省掉注释,析构函数前没有注释是很正常的。
  函数定义:
  每个函数定义时要以注释说明函数功能和实现要点,如使用的漂亮代码、实现的简要步骤、如此实现的理由、为什么前半部分要加锁而后半部分不需要。
  不要从.h 文件或其他地方的函数声明处直接复制注释,简要说明函数功能是可以的,但重点要放在如何实现上。
 
5.变量注释
  通常变量名本身足以很好说明变量用途,特定情况下,需要额外注释说明。
  类数据成员:
  每个类数据成员(也叫实例变量或成员变量)应注释说明用途,如果变量可以接受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 ErrorCode when SCCP translate
// Global Title failure, as follows    变量作用、含义
// 0 -SUCCESS    1 -GT Table error
// 2 -GT error Others -no use          变量取值范围
// only function SCCPTranslate() in
// this modual can modify it, and other
// module can visit it through call
// the function GetGTTransErrorCode()  使用方法
BYTE g_GTTranErrorCode;

 
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.
...

  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++比你强:

// 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.标点、拼写和语法
  留意标点、拼写和语法,写的好的注释比差的要易读的多。
  注释一般是包含适当大写和句点(.)的完整的句子,短一点的注释(如代码行尾的注释)可以随意点,依然要注意风格的一致性。完整的句子可读性更好,也可以说明该注释是完整的而不是一点不成熟的想法。
  虽然被别人指出该用分号(semicolon)的时候用了逗号(comma)有点尴尬。清晰易读的代码还是很重要的,适当的标点、拼写和语法对此会有所帮助。
 
8.TODO 注释
  对那些临时的、短期的解决方案,或已经够好但并不完美的代码使用TODO注释。
  这样的注释要使用全大写的字符串TODO,后面括号(parentheses)里加上你的大名、邮件地址等,还可以加上冒号(colon):目的是可以根据统一的TODO格式进行查找:

// TODO([email protected]): 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.")。
  一定不要在已发布的代码示例中使用TODO待办注释。每一个代码示例都必须完整,在代码中不能有未完成的任务。

猜你喜欢

转载自blog.51cto.com/13807182/2130164