代码风格(3)——注释

一、总述

注释虽然写起来很痛苦,但对保证代码可读性至关重要。
当然也要记住:注释固然很重要, 但最好的代码应当本身就是文档。有意义的类型名和变量名,要远胜过要用注释解释的含糊不清的名字。

1.1 注释风格

使用 ///* */ 都可以;但 //更常用

二、文件注释

在每一个文件开头加入版权公告

/**===========================================================================
  Copyright (C) XXXX All rights reserved.

  @file     user_udp.c
  @brief    本文件用于UDP通讯接口
  @author   Leung Man-Wah
  @version  r0.1
  @date     2019/07/04
  @license XXXX
----------------------------------------------------------------------------
  Remark: (备注描述)
----------------------------------------------------------------------------
                                History
----------------------------------------------------------------------------
  <Date>     | <Version> | <Author>       | <Description>
-------------|-----------|----------------|---------------------------------
  2019/07/04 | r0.1      | Leung Man-Wah  | 创建
-------------|-----------|----------------|---------------------------------
             |           |                |
-------------|-----------|----------------|---------------------------------
             |           |                |
-------------|-----------|----------------|---------------------------------
             |           |                |
============================================================================*/

2.1 法律公告和作者信息

每个文件都应该包含许可证引用。为项目选择合适的许可证版本。(比如, Apache 2.0, BSD, LGPL, GPL)

如果你对原始作者的文件做了重大修改,请考虑删除原作者信息。

2.2 文件内容

如果一个 .h 文件声明了多个概念,则文件注释应当对文件的内容做一个大致的说明,同时说明各概念之间的联系。一个一到两行的文件注释就足够了,对于每个概念的详细文档应当放在各个概念中,而不是文件注释中。

三、类注释

每个类的定义都要附带一份注释,描述类的功能和用法,除非它的功能相当明显。

// Iterates over the contents of a GargantuanTable.
// Example:
//    GargantuanTableIterator* iter = table->NewIterator();
//    for (iter->Seek("foo"); !iter->done(); iter->Next()) {
//      process(iter->key(), iter->value());
//    }
//    delete iter;
class GargantuanTableIterator 
{
  ...
};

类注释应当为读者理解如何使用与何时使用类提供足够的信息,同时应当提醒读者在正确使用此类时应当考虑的因素。如果类有任何同步前提,请用文档说明。如果该类的实例可被多线程访问,要特别注意文档说明多线程环境下相关的规则和常量使用。

如果你想用一小段代码演示这个类的基本用法或通常用法,放在类注释里也非常合适。

如果类的声明和定义分开了(例如分别放在了 .h.cc 文件中),此时,描述类用法的注释应当和接口定义放在一起,描述类的操作和实现的注释应当和实现放在一起。

四、函数注释

基本上每个函数定义处前都应当加上注释,描述函数的功能和用途。只有在函数的功能简单而明显时才能省略这些注释(例如,简单的取值和设值函数)。

扫描二维码关注公众号,回复: 8979784 查看本文章

函数声明处注释的内容:

  • 函数的输入输出。
  • 对类成员函数而言:函数调用期间对象是否需要保持引用参数, 是否会释放这些参数。
  • 函数是否分配了必须由调用者释放的空间。
  • 参数是否可以为空指针。
  • 是否存在函数使用上的性能隐患。
  • 如果函数是可重入的,其同步前提是什么?
/**
 @brief UDP发送继电器状态
 @param deviceType -[in] 设备类型
 @param pMacAddr -[in] MAC地址
 @param relayStatus -[in] 继电器状态
 @return 无
*/
void UdpSendRelayStatus(uint8 deviceType, uint8 *pMacAddr, uint8 relayStatus)
{
}

/**
 @brief JSON格式封装设备信息
 @param pSendData -[in&out] 要封装的发送数据
 @return 无
*/
static void jsonPackageDeviceInfo(char *pSendData)
{
}

注释函数重载时,注释的重点应该是函数中被重载的部分,而不是简单的重复被重载的函数的注释。多数情况下,函数重载不需要额外的文档,因此也没有必要加上注释。

注释构造/析构函数时,切记读代码的人知道构造/析构函数的功能,所以 “销毁这一对象” 这样的注释是没有意义的。你应当注明的是注明构造函数对参数做了什么 (例如,是否取得指针所有权) 以及析构函数清理了什么。如果都是些无关紧要的内容,直接省掉注释。析构函数前没有注释是很正常的。

五、变量注释

通常变量名本身足以很好说明变量用途。某些情况下,也需要额外的注释说明。

5.1 类数据成员

每个类数据成员 (也叫实例变量或成员变量) 都应该用注释说明用途。如果有非变量的参数(例如特殊值,数据成员之间的关系,生命周期等)不能够用类型与变量名明确表达,则应当加上注释。然而,如果变量类型与变量名已经足以描述一个变量,那么就不再需要加上注释。

特别地,如果变量可以接受 NULL-1 等警戒值,须加以说明。比如:

private:
 // Used to bounds-check table accesses. -1 means
 // that we don't yet know how many entries the table has.
 int numTotalEntries;

5.2 全局变量

和数据成员一样,所有全局变量也要注释说明含义及用途,以及作为全局变量的原因。比如:

// The total number of tests cases that we run through in this regression test.
int g_numTestCases = 6;

六、实现注释

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

6.1 代码前注释

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

// 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;
}

6.2 行注释

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

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

七、TODO注释

对那些临时的,短期的解决方案,或已经够好但仍不完美的代码使用 TODO 注释。

TODO 注释要使用全大写的字符串 TODO,在随后的圆括号里写上你的名字,邮件地址, bug ID,或其它身份标识和与这一 TODO 相关的 issue。主要目的是让添加注释的人 (也是可以请求提供更多细节的人) 可根据规范的 TODO 格式进行查找。添加 TODO 注释并不意味着你要自己来修正,因此当你加上带有姓名的 TODO 时, 一般都是写上自己的名字。

// TODO([email protected]): Use a "*" here for concatenation operator.
// TODO(Zeke) change this to use relations.
// TODO(bug 12345): remove the "Last visitors" feature

如果加 TODO 是为了在 “将来某一天做某事”,可以附上一个非常明确的时间 “Fix by November 2005”),或者一个明确的事项 (“Remove this code when all clients can handle XML responses.”)。

TODO 很不错,有时候,注释确实是为了标记一些未完成的或完成的不尽如人意的地方,这样一搜索,就知道还有哪些活要干,日志都省了。


• 由 Leung 写于 2019 年 10 月 28 日

• 参考:Google 开源项目风格指南——8. 注释

发布了86 篇原创文章 · 获赞 131 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_36347513/article/details/102790815
今日推荐