8.6. 实现注释

总述

对于代码中巧妙的, 晦涩的, 有趣的, 重要的地方加以注释.

说明

代码前注释

巧妙或复杂的代码段前要加注释. 比如:

  1. // Divide result by two, taking into account that x
  2. // contains the carry from the add.
  3. for (int i = 0; i < result->size(); i++) {
  4. x = (x << 8) + (*result)[i];
  5. (*result)[i] = x >> 1;
  6. x &= 1;
  7. }

行注释

比较隐晦的地方要在行尾加入注释. 在行尾空两格进行注释. 比如:

  1. // If we have enough memory, mmap the data portion too.
  2. mmap_budget = max<int64>(0, mmap_budget - index_->length());
  3. if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock))
  4. return; // Error already logged.

注意, 这里用了两段注释分别描述这段代码的作用, 和提示函数返回时错误已经被记入日志.

如果你需要连续进行多行注释, 可以使之对齐获得更好的可读性:

  1. DoSomething(); // Comment here so the comments line up.
  2. DoSomethingElseThatIsLonger(); // Two spaces between the code and the comment.
  3. { // One space before comment when opening a new scope is allowed,
  4. // thus the comment lines up with the following comments and code.
  5. DoSomethingElse(); // Two spaces before line comments normally.
  6. }
  7. std::vector<string> list{
  8. // Comments in braced lists describe the next element...
  9. "First item",
  10. // .. and should be aligned appropriately.
  11. "Second item"};
  12. DoSomething(); /* For trailing block comments, one space is fine. */

函数参数注释

如果函数参数的意义不明显, 考虑用下面的方式进行弥补:

  • 如果参数是一个字面常量, 并且这一常量在多处函数调用中被使用, 用以推断它们一致, 你应当用一个常量名让这一约定变得更明显, 并且保证这一约定不会被打破.
  • 考虑更改函数的签名, 让某个 bool 类型的参数变为 enum 类型, 这样可以让这个参数的值表达其意义.
  • 如果某个函数有多个配置选项, 你可以考虑定义一个类或结构体以保存所有的选项, 并传入类或结构体的实例. 这样的方法有许多优点, 例如这样的选项可以在调用处用变量名引用, 这样就能清晰地表明其意义. 同时也减少了函数参数的数量, 使得函数调用更易读也易写. 除此之外, 以这样的方式, 如果你使用其他的选项, 就无需对调用点进行更改.
  • 用具名变量代替大段而复杂的嵌套表达式.
  • 万不得已时, 才考虑在调用点用注释阐明参数的意义.
    比如下面的示例的对比:
  1. // What are these arguments?
  2. const DecimalNumber product = CalculateProduct(values, 7, false, nullptr);

  1. ProductOptions options;
  2. options.set_precision_decimals(7);
  3. options.set_use_cache(ProductOptions::kDontUseCache);
  4. const DecimalNumber product =
  5. CalculateProduct(values, options, /*completion_callback=*/nullptr);

哪个更清晰一目了然.

不允许的行为

不要描述显而易见的现象, 永远不要 用自然语言翻译代码作为注释, 除非即使对深入理解 C++ 的读者来说代码的行为都是不明显的. 要假设读代码的人 C++ 水平比你高, 即便他/她可能不知道你的用意:

你所提供的注释应当解释代码 为什么 要这么做和代码的目的, 或者最好是让代码自文档化.

比较这样的注释:

  1. // Find the element in the vector. <-- 差: 这太明显了!
  2. auto iter = std::find(v.begin(), v.end(), element);
  3. if (iter != v.end()) {
  4. Process(element);
  5. }

和这样的注释:

  1. // Process "element" unless it was already processed.
  2. auto iter = std::find(v.begin(), v.end(), element);
  3. if (iter != v.end()) {
  4. Process(element);
  5. }

自文档化的代码根本就不需要注释. 上面例子中的注释对下面的代码来说就是毫无必要的:

  1. if (!IsAlreadyProcessed(element)) {
  2. Process(element);
  3. }