GoogleCpp风格指南 7)注释

7 注释 Comments

注释虽然写起来很痛苦, 但对保证代码可读性至关重要; 下面的规则描述了如何注释以及在哪注释; 当然也要记住: 注释固然很重要, 但最好的代码本身应该是文档化self-documenting; 有意义的类型名和变量名, 要远胜过要用注释解释的含糊不清的名字;

你写的注释是给代码读者看的: 下一个需要理解你代码的人, 慷慨些吧, 下一个人可能就是你;

7.1 注释风格 Comment Style

Tip 使用 // 或 /* */, 统一就好;

// 或 /* */都可以, 但 // 更常用; 要在如何注释及注释风格上确保统一;

7.2 文件注释 File Comments

Tip 在每一个文件开头加入版权公告boilerplate, 然后是文件内容描述;

法律公告和作者信息: Legal Notice and Author Line

每个文件都应该包含以下项, 依次是:

- 版权声明 (比如, Copyright 2008 Google Inc.)

- 许可证, 为项目选择合适的许可证版本 (比如, Apache 2.0, BSD, LGPL, GPL);

- 作者: 标识文件的原始作者;

如果你对原始作者的文件做了重大修改, 将你的信息添加到作者信息里; 这样当其他人对该文件有疑问时可以知道该联系谁;

文件内容 File Contents

紧接着版权许可和作者信息之后, 每个文件都要用注释描述文件内容;

通常 .h文件要对所声明的类的功能和用法作简单说明; .cc文件通常包含了更多的实现细节或算法技巧讨论; 如果你感觉这些实现细节或算法技巧讨论对于理解 .h文件有帮助, 可以将该注释挪到 .h并在 .cc中指出文档在 .h文件中;

不要简单的在 .h和 .cc间复制注释, 这种做法偏离了注释的实际意义; [有时候像UI中的子类继承, 注释大概也就等于拷贝一下内容]

7.3 类注释 Class Comments

Tip 每个类的定义都要附带accompanying一份注释, 描述类的功能和用法;


1

2

3

4

5

6

7

8

9

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

    ...

};

如果你觉得已经在文件顶部详细描述了该类, 想直接简单得来上一句"完整描述见文件顶部See comment at top of file for a complete description"也不要紧, 但务必确保有这类注释;

如果类有任何同步前提, 文档说明之; 如果该类的实例可被多线程访问, 要特别注意文档说明多线程环境下相关的规则和不变量的使用 invariant surrounding multithreaded use;

7.4 函数注释 Function Comments

Tip 函数声明处注释描述函数功能, 定义处描述函数实现;

函数声明: Function Declarations

注释位于声明之前preceding, 对函数功能及其用法进行描述; 注释使用叙述式descriptive("Opens the file")而非指令式imperative("Open the file"); 注释只是为了描述函数, 而不是命令函数做什么; 通常, 注释不会描述函数如何工作, 那是函数定义部分的事情;

函数声明处注释的内容:

- 函数的输入输出; inputs and outputs

- 对类成员函数而言: 函数调用期间对象是否需要保持引用参数, 是否会释放这些参数;

[Add]

- 如果函数分配了内存, 调用者必须负责释放;

<<<

- 参数是否可以为空指针NULL;

- 是否存在函数使用上的性能隐患;

- 如果函数是可重入的re-entrant, 其同步前提是什么? [http://en.wikipedia.org/wiki/Reentrancy_(computing) ]

举例:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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

但也要避免罗嗦, 或做显而易见的说明; 下面的注释就没有必要加上 "return false otherwise", 因为已经暗含其中了:


1

2

// Returns true if the table cannot hold any
more entries.

bool IsTableFull();

构造/析构函数加注释时, 切记读代码的人要知道构造/析构函数是做什么的, 所以"destroy this object"这样的注释是没有意义的; 注明构造函数对参数做了什么(例如, 是否取得指针所有权)以及析构函数清理了什么; 如果都是些无关紧要的trivial内容, 直接省掉注释, 析构函数前没有注释是很正常的;

函数定义: Function Definitions

每个函数定义时要用注释说明函数功能和实现要点tricky; 比如说你用的编程技巧, 实现的大致步骤, 或解释如此实现的理由, 解释为什么使用了函数而不是成员变量; 为什么前半部分要加锁而后半部分不需要;

不要从 .h文件或其他地方的函数声明处直接复制注释; 简要重述recapitulate  briefly函数功能是可以的, 但注释重点要放在如何实现上;

7.5 变量注释 Variable Comments

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

类数据成员: Class Data Members

每个类数据成员(也叫实例变量或成员变量)都应该用注释说明用途; 如果变量可以接受 空指针NULL或 -1等警戒sentinel值, 须加以说明, 比如:


1

2

3

4

5

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_;

全局变量: Global Variables

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


1

2

// The total number of tests cases that we
run through in this regression test.

const int kNumTestCases
= 6;

7.6 实现注释 Implementation Comments

Tip 对于代码中巧妙的tricky, 晦涩的non-obvious, 有趣的interesting, 重要的important地方加以注释;

代码前注释: Explanatory Comments

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


1

2

3

4

5

6

7

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

}

行注释: Line Comments

比较隐晦的地方要在行尾加入注释; 在行尾end-of-line空两格进行注释; 比如:


1

2

3

4

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

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

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


1

2

3

4

5

6

7

8

9

10

11

12

13

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.

}

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

NULL, true/false, 1, 2, 3...

向函数传入 空指针NULL, 布尔值boolean或整数literal integer值时, 要注释说明含义, 或使用常量让代码望文知意; 例如, 对比:

WARNING:


1

2

3

4

bool success
= CalculateSomething(interesting_value,

                                  10,

                                  false,

                                  NULL);  //
What are these arguments??

Good:


1

2

3

4

bool success
= CalculateSomething(interesting_value,

                                  10,     //
Default base value.

                                  false,  //
Not the first time we‘re calling this.

                                  NULL);  //
No callback.

或使用常量, 描述性self-describin变量:


1

2

3

4

5

6

7

const int kDefaultBaseValue
= 10;

const bool kFirstTimeCalling
false;

Callback *null_callback = NULL;

bool success
= CalculateSomething(interesting_value,

                                  kDefaultBaseValue,

                                  kFirstTimeCalling,

                                  null_callback);

不允许 Don‘ts

Note 永远不要用自然语言翻译代码作为注释, 要假设读代码的人C++水平比你高, 即便他可能不知道你的用意:

WARNING:


1

2

3

// 现在, 检查 b 数组并确保 i 是否存在,

// 下一个元素是 i+1.

...        //
天哪. 令人崩溃的注释.

7.7 标点, 拼写和语法 Punctuation, Spelling and Grammar

Tip 注意标点, 拼写和语法: 写的好的注释比差的要易读得多;

注释的通常写法是包含正确大小写和结尾句号的完整语句; 短一点的注释(如代码行尾注释)可以随意点, 依然要注意风格的一致性; 完整的语句可读性更好, 也可以说明该注释是完整的, 而不是一些不成熟的想法;

虽然被别人指出该用分号时却用了逗号多少有些frustrating沮丧, 但清晰易读的代码还是很重要的; 正确的clarity标点, 拼写和语法对此会有所帮助;

7.8 TODO Comments

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

TODO注释要使用全大写的字符串TODO, 在随后的圆括号里面写上你的名字, 邮件地址, 或其它身份标识; 冒号是可选的; 主要目的是让添加注释的人(也是可以请求提供更多细节的人)可根据规范的TODO格式进行查找, 添加TODO注释并不意味着commitment你要自己来修正;


1

2

// TODO([email protected]): Use a "*" here for concatenation
operator.

// TODO(Zeke) change this to use relations.

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

[Add]

废除注释 Deprecation Comments

使用DEPRECATED注释标记废除的接口点;

你可以用DEPRECATED注释来标记一个接口以及废弃; DEPRECATED可以写在接口之前, 或者放在同一行;

在DEPRECATED注释后可以用括号跟上你的姓名, email或其他身份标识;

废弃注释必须包含简洁的指导direction, 告诉使用者在那些调用点callsite进行更新; 如果确定要停止废弃设施facility的使用, 你必须修复所有调用点, 或者找人帮你做;

新代码不应该包含废弃的接口点; 使用新的接口代替; 如果你不能理解注释中的指导, 要找到相关人员寻求帮助, 然后使用新接口点;

<<<

译者笔记

1) 关于注释风格, 很多C++的coders更喜欢行注释; C coders或许对块注释依然情有独钟, 或者在文件头大段大段的注释时使用块注释; [块注释有时候会给IDE的快捷注释/反注释带来点小麻烦]

2) 文件注释可以炫耀你的成就, 也是为了捅了篓子别人可以找到你;

3) 注释要言简意赅, 不要拖沓冗余, 复杂的东西简单化和简单的戏复杂化都是要被鄙视的; [有问题的]

4) 对于Chinese coders来说, 用英文注释还是中文注释是个问题, 但不管怎样, 注释是为了让别人看懂, 不是为了炫耀编程之外你的母语或外语水平; [English Only好麽, 中文还要带来Unicode问题]

5) 注释不要太乱, 适当缩进才会让人乐意看; 但也没必要规定注释从第几列开始; UNIX/LINUX下还可以约定是使用tab还是space; [不论什么平台, 自从有了code review工具, 这已经是明文规定了好么]

6) TODO很不错, 有时候, 注释确实是为了标记一些未完成的或完成不尽如人意的地方; 一搜索就知道还有哪些要做的, 日志都省了; [TODO不要滥用, 明确知道是临时方案, 或需要在下一个US实现, 这样的情况适用TODO]

---TBC---YCR

时间: 2024-10-31 02:27:22

GoogleCpp风格指南 7)注释的相关文章

GoogleCpp风格指南 5) 其他特性_part1

5 其他C++特性 Other C++ Features 5.1 引用参数 Reference Arguments Tip 所有按引用传递的参数必须加上 const; 定义: 在C语言中, 如果函数需要修改变量的值, 参数必须为指针, 如 int foo(int* pval); 在C++中, 函数还可以声明引用参数 int foo(int& val); 优点: 定义引用参数防止出现 (*pval)++ 这样丑陋的代码; 像拷贝构造函数这样的应用也是必需的, 而且更明确, Note 不接受NULL

GoogleCpp风格指南 1)头文件 2)作用域

Google开源项目风格指南 v3.133 原作: Benjy Weinberger, Craig Silverstein, Gergory Eitzmann, Mark Mentovai, Tashana Landray 翻译: YuleFox, brantyoung 修改: YCR 0 扉页 0.1 译者前言 Google经常发布一些开源项目, 因此发布这份编程风格, 使所有提交代码的人能获知Google的编程风格; 规则的作用是避免混乱, 但规则本身要权威, 有说服力, 并且是理性的, 大

GoogleCpp风格指南 5) 其他特性_part3

[Add] Lambda expressions 在合适的时候使用lambda表达式; 不要使用默认的lambda captures, 使用显式的captures; [http://en.cppreference.com/w/cpp/language/lambda ] 定义: lambda表达式是一个创建匿名函数对象anonymous function objects.的简洁concise方式; 在将函数作为参数传递的时候很有用; 1 2 3 std::sort(v.begin(), v.end

GoogleCpp风格指南 4)Google奇技

4 来自Google的奇技 Google-Specific Magic Google用了很多自己的实现技巧/工具使 C++代码更加健壮, 我们使用C++的方式可能和你在其他地方见到的有所不同; [Removed] 4.1 智能指针 Tip 如果确实需要使用智能指针的话, scoped_ptr完全可以胜任; 你应该只在非常特定的情况下使用 std::tr1::shared_ptr, 例如STL容器中的对象; 任何情况下都不要使用 auto_ptr; "智能"指针看上去是指针, 其实是附加

GoogleCpp风格指南 8)格式 _part1

8 格式 Formatting 代码风格和格式确实比较随意, 但一个项目中所有人遵循同一风格是非常容易的; 个体未必同意下述每一处格式规则, 但整个项目服从统一的编程风格是很重要的, 只有这样才能让所有人很轻松地阅读和理解代码; 我们写了一个 settings file for emacs [http://google-styleguide.googlecode.com/svn/trunk/google-c-style.el] 帮助你正确的格式化代码; 8.1 行长度 Line Length T

GoogleCpp风格指南 3)类

3 类 Classes 类是C++中代码的基本单元; 显然, 它们被广泛使用; 本节列举了写一个类时的主要注意事项; 3.1 构造函数的职责 Doing Work in Constructors Tip 构造函数中只进行那些没什么意义的(trivial 译注: 简单初始化对于程序执行没有实际的逻辑意义, 因为成员变量"有意义"的值大多不再构造函数中确定)初始化, 可能的话, 使用 Init()方法集中初始化有意义的(non-trival)数据; [Add] 在ctor中防止做复杂的初始

GoogleCpp风格指南 8)格式 _part2

8.9 布尔表达式 Boolean Expressions Tip 如果一个布尔表达式超过标准行宽standard line length; 断行方式要统一一下; 下例中, 逻辑与(&&)操作符总位于行尾: 1 2 3 4 5 if (this_one_thing > this_other_thing &&     a_third_thing == a_fourth_thing &&     yet_another & last_one) {

GoogleCpp风格指南 5) 其他特性_part2

5.10 前置自增和自减 Preincrement and Predecrement Tip 对于迭代器iterator和其他模板对象template object使用前缀形式(++i)的自增, 自减运算符; 定义: 对于变量在自增(++i 或 i++)或自减(--i 或 i--)后, 表达式的值没有被用到的情况下, 需要确定到底是使用前置还是后置的自增(自减); 优点: 不考虑返回值的话, 前置pre自增(++i)通常要比后置post自增(i++)效率更高; 因为后置自增(自减)需要对表达式的

GoogleCpp风格指南 6)命名约定

6 命名约定 Naming 最重要的一致性规则是命名管理govern naming; 命名风格快速获取名字代表是什么: 类型? 变量, 函数, 常量, 宏, ...甚至不需要去查找类型声明; 大脑中的模式匹配引擎pattern-matching engine可以非常可靠的处理这些命名规则; 命名规则具有一定随意性, 但相比按个人喜好命名, 一致性更重要, 所以不管你怎么想, 规则总归是规则; 6.1 通用命名规则 General Naming Rules Tip 函数命名, 变量命名, 文件命名