8.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(); // 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.
- }
- std::vector<string> list{
- // Comments in braced lists describe the next element...
- "First item",
- // .. and should be aligned appropriately.
- "Second item"};
- DoSomething(); /* For trailing block comments, one space is fine. */
函数参数注释
如果函数参数的意义不明显, 考虑用下面的方式进行弥补:
- 如果参数是一个字面常量, 并且这一常量在多处函数调用中被使用, 用以推断它们一致, 你应当用一个常量名让这一约定变得更明显, 并且保证这一约定不会被打破.
- 考虑更改函数的签名, 让某个
bool
类型的参数变为enum
类型, 这样可以让这个参数的值表达其意义. - 如果某个函数有多个配置选项, 你可以考虑定义一个类或结构体以保存所有的选项, 并传入类或结构体的实例. 这样的方法有许多优点, 例如这样的选项可以在调用处用变量名引用, 这样就能清晰地表明其意义. 同时也减少了函数参数的数量, 使得函数调用更易读也易写. 除此之外, 以这样的方式, 如果你使用其他的选项, 就无需对调用点进行更改.
- 用具名变量代替大段而复杂的嵌套表达式.
- 万不得已时, 才考虑在调用点用注释阐明参数的意义.
比如下面的示例的对比:
- // What are these arguments?
- const DecimalNumber product = CalculateProduct(values, 7, false, nullptr);
和
- ProductOptions options;
- options.set_precision_decimals(7);
- options.set_use_cache(ProductOptions::kDontUseCache);
- const DecimalNumber product =
- CalculateProduct(values, options, /*completion_callback=*/nullptr);
哪个更清晰一目了然.
不允许的行为
不要描述显而易见的现象, 永远不要 用自然语言翻译代码作为注释, 除非即使对深入理解 C++ 的读者来说代码的行为都是不明显的. 要假设读代码的人 C++ 水平比你高, 即便他/她可能不知道你的用意:
你所提供的注释应当解释代码 为什么 要这么做和代码的目的, 或者最好是让代码自文档化.
比较这样的注释:
- // Find the element in the vector. <-- 差: 这太明显了!
- auto iter = std::find(v.begin(), v.end(), element);
- if (iter != v.end()) {
- Process(element);
- }
和这样的注释:
- // Process "element" unless it was already processed.
- auto iter = std::find(v.begin(), v.end(), element);
- if (iter != v.end()) {
- Process(element);
- }
自文档化的代码根本就不需要注释. 上面例子中的注释对下面的代码来说就是毫无必要的:
- if (!IsAlreadyProcessed(element)) {
- Process(element);
- }