C++ 注释风格建议

0.前言

有个笑话,一位从不写注释的程序员在编写一段复杂的代码时,自豪地认为这段代码只有自己和上帝知道它是干嘛的,等过了一段时间再回顾时,发现没有注释,感叹到这段代码现在只有上帝知道它是干嘛的。

可见,注释是项目中不可或缺的部分,不仅是为了帮助团队其它开发人员快速理解代码,也是帮助自己快速恢复对代码功能的了解。所以,我们对注释不要过于吝啬,该书写时就书写。

虽然注释书写起来有点麻烦,但是为了提供程序本身额外的信息,如函数功能描述、类动能描述、算法描述、程序开发作者、开发时间、开发背景、第三方资料来源路径等,提高代码可读性和可维护性,我们为注释付出的麻烦是值得的。当然也要记住,注释固然重要,但最好的代码应当本身就是文档,要达到“代码即注释”的效果,有意义的类型名和变量名,要远胜于使用注释来解释含糊不清的名字。

1.注释风格(Comment Style)

C++ 有两种注释形式,使用 C 风格/**/或 C++ 风格//都可以,项目中统一就好。毕竟是 C++ 项目,建议还是使用 C++ 风格的//

当然,//对代码块注释不太方便,可以适当采用/**/来快速便捷地注释代码块。

2.文件注释(File Comments)

采用 Doxygen 注释风格,这样每一行注释都具有自我描述性。在每一个文件开头一般需要加入以下内容:
(1)文件内容,简要描述文件功能和内容。
(2)版权信息,如 Copyright 2018 Google Inc;
(3)许可证,如果是开源项目加上合适的许可证,如 Apache 2.0、BSD、LGPL、GPL;
(4)创建信息,标识文件创建作者与日期;
(5)版本信息;
(6)版本变更信息等。

一个合适的文件注释示例如下:

//
// @brief: 开源 JSON 解析库
// @copyright: Copyright 2018 Google Inc
// @license: GPL
// @birth: created by Dablelv on 2018-08-02
// @version: V1.1.1
// @revision: last revised by lvlv on 2018-09-02
//

3.类注释(Class Comments)

每个类的定义都要附带一份注释,描述类的功能和用法,除非它的功能相当明显。类注释应当为读者理解如何使用与何时使用类提供足够的信息,同时应当提醒读者在正确使用此类时应当考虑的因素。如果该类的实例可被多线程访问,要特别注意文档说明多线程环境下相关的规则和常量使用。如果你想用一小段代码演示这个类的基本用法或通常用法,放在类注释里也非常合适。如果类的声明和定义分开了(例如分别放在了 .h 和 .cc 文件中),此时,描述类用法的注释应当和接口定义放在一起(.h文件中),描述类的操作和实现的注释应当和实现放在一起(.cc文件中)。

一个简单的注释示例如下:

// Iterates over the contents of a GargantuanTable
class GargantuanTableIterator {
	...
};

4.函数注释(Function Comments)

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

4.1 函数声明

基本上每个函数声明处前都应当加上注释,描述函数的功能和用途。只有在函数的功能简单而明显时才能省略这些注,例如,简单的取值和设值函数。注释使用叙述式 (Opens the file) 而非指令式 (Open the file),注释只是为了描述函数,而不是命令函数做什么。通常,函数声明的注释不会描述函数如何工作,那是函数定义部分的事情。

函数声明处注释的一般内容:
(1)函数功能简介;
(2)函数参数说明;
(3)函数返回值说明;
(4)函数创建信息,包括创建背景、时间和作者等信息。

举例如下:

// @brief: 获取容器迭代器
// @param: void
// @ret: 返回容器迭代器
// @birth: created by Dablelv on 20180802
Iterator* getIterator() const;

但也要避免啰啰嗦嗦和对显而易见的内容进行说明,比如下面的注释就没有必要加上,因为返回值类型结合函数名已经清晰地表达出函数返回值的含义。

// @ret: 如果表已满则返回true
bool IsTableFull();

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

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

4.2 函数定义

如果函数的实现过程中用到了很巧妙的方式,那么在函数定义处应当加上解释性的注释。例如,你所使用的编程技巧,实现的大致步骤,或解释如此实现的理由。举例如下:

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

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

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 after two spaces
...

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

4.3 函数调用

函数调用时,如果函数实参意义不明显,考虑用下面的方式进行弥补:
(1)如果参数是一个字面常量,并且这一常量在多处函数调用中被使用,你应当用一个统一的常量名来标识该常量;
(2)考虑更改函数的签名,让某个 bool 类型的参数变为 enum 类型,这样可以用参数名称表达其意义;
(3)如果某个函数有多个参数,你可以考虑定义一个类或结构体以保存所有参数,并传入类或结构体的实例。这样的方法有许多优点,一减少了函数参数数量,使得函数调用易读易写;二使用引用或指针传入构造类型对象,减少对象拷贝,提高运行效率;三如果新增参数,函数原型无需改变,之需要将新增的参数加入类或结构体中;
(4)传参时,用具名变量代替大段而复杂的嵌套表达式。比如:

void print(const string& accountCompanyName) {
	cout<<accountCompanyName<<endl;
}

// 版本 1
print(((stUserID.dwType==stUserID_t::USER_TYPE_WX||stUserID.dwType==stUserID_t::USER_TYPE_QQ)?true:false)?"Tencent":"Others");

// 版本 2
//帐号所属公司名称
string accountCompanyName=((stUserID.dwType==stUserID_t::USER_TYPE_WX||stUserID.dwType==stUserID_t::USER_TYPE_QQ)?true:false)?"Tencent":"Others";
print(accountCompanyName);

很显然,版本 2 使用具名变量清晰地表达了实参的含义,而不是直接使用又臭又长的表达式作为实参传递给函数。

(5)万不得已时,才考虑在调用点用注释阐明参数的意义。比如下面的示例:

// 版本 1
// What are these arguments?
const DecimalNumber product = CalculateProduct(values, 7, false, nullptr);

// 版本 2
ProductOptions options;
options.set_precision_decimals(7);
options.set_use_cache(ProductOptions::kDontUseCache);
const DecimalNumber product = CalculateProduct(values, options, /*completion_callback=*/nullptr);

很显然,版本 2 对函数调用传入的实参解释得更加清晰明了。

5.变量注释(Variable Comments)

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

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

5.2 全局变量

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

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

6.标点、拼写和语法(Punctuation, Spelling and Grammar)

注释时,注意标点、拼写和语法。写的好的注释比差的要易读的多。注释的通常写法是包含正确大小写和结尾句号的完整叙述性语句。大多数情况下,完整的句子比句子片段可读性更高。短一点的注释,比如代码行尾注释,可以随意点,但依然要注意风格的一致性。清晰易读的代码还是很重要的,正确的标点, 拼写和语法对此会有很大帮助。

7.TODO注释(TODO Comments)

如果项目中存在功能代码有待修改和编写地方,建议使用TODO注释进行简略说明。TODO注释的作用类似于书签,便于开发者快速找到需要继续开发的位置和有待完成的功能,起到提醒标记的作用。

TODO 注释要使用全大写的字符串 TODO,在随后的圆括号里写上负责人的名字、邮件地址、bug ID或其它与这一 TODO 相关的身份标识,主要目的是让添加注释的人或负责人可根据规范的 TODO 格式进行查找。添加 TODO 注释一般都是写上自己的名字,但并不意味着一定要自己来修正,除非自己是该 TODO 所描述的问题负责人。简单示例:

// TODO(kl@gmail.com): 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.”)。

8.弃用注释(DEPRECATED Comments)

可以写上包含全大写的 DEPRECATED 的注释,以标记某接口为弃用状态。

弃用注释应当包涵简短而清晰的指引,以帮助其他人修复其调用点。在 C++ 中,你可以将一个弃用函数改造成一个内联函数,这一函数将调用新的接口。在 DEPRECATED 一词后,在括号中留下负责人的名字、邮箱地址以及其他身份标识。注释可以放在接口声明前,或者同一行。简单示例如下:

// DEPRECATED(dablelv):new interface changed to bool IsTableFull(const Table& t)
bool IsTableFull();

9.注意事项

(1)多行注释不要嵌套。
在使用C风格注释符/**/进行块注释时,不能在注释内部再次使用/**/,否则会引发编译错误。示例如下:

/*
	This is a false demo
	/*
		This is nested code
	*/
	This becomes a normal statement
*/

上面的注释中,第一个/*会跟第一个*/匹配,导致“This becomes a normal statement”被编译器当做一个正常的语句来编译,从而导致编译错误。

(2)程序中不能没有注释。
良好的编程习惯和规范能够帮助编码者尽可能的做到“代码即注释”,但往往由于项目的庞大和程序功能的高复杂性,代码结构和功能会变得异常的复杂,为了便于程序员之间的交流合作,提高程序的可读性和可维护性,代码注释必不可少。

(3)勿过度使用注释。
注释在项目开发中虽然必不可能,但过犹不及,注释并不是越多越好,切勿给显而易见的代码功能添加注释,画蛇添足,添加不需要的注释。比如下面的注释就没有必要。

// Find the element in the vector.
auto iter = std::find(v.begin(), v.end(), element);
if (iter != v.end()) {
	Process(element);
}

多余的注释没有存在的价值,不可过度使用注释还有一个更重要的原因,注释同样需要维护,如果程序员在变更代码功能后,忽略了对注释的及时维护,那么会严重降低代码的可读性。比如

a=b;	// assign b to a

// 代码变更后未变更注释
c=b;	// assign b to a 

上面多余的注释在变更代码后未及时的修改,会给阅读者造成困扰。程序员的第一反应并不会怀疑注释是错误的,而是会分析注释的“真正意图”。或许c是a的一个引用,于是c=b;就完成了b对a的赋值,然而事实并非如此。

因此,注释存在的意义是给代码阅读者提供有价值的信息,而不是盲目添加变成代码的累赘。实际上,开发者应该尽可能地让代码如果本身具有可读性,能够自成文档,达到代码即注释的效果,而让注释成为“锦上添花”的作用。如果程序本身设计存在问题,且不遵守编码规范,那么指望注释来提高程序的可读性只能是天方夜谭。

10.小结

注释是较为人性化约定,每一个程序员都应该养成注释的习惯。
(1)关于注释风格,很多 C++ 的coders 更喜欢行注释,C coders或许对块注释依然情有独钟,或者在文件头大段大段的注释时使用块注释;
(2)注释要言简意赅,不要拖沓冗余,不要书写不必要的注释;
(3)对于 Chinese coders 来说,用英文注释还是用中文注释是一个问题,但不管怎样,注释是为了让别人看懂,而不是炫耀母语或外语的水平;
(4)注释不要太乱,适当的缩进才会让人乐意看,但也没有必要规定注释从第几列开始 ;
(5)TODO 很不错,有时候,注释确实是为了标记一些未完成的或完成的不尽如人意的地方,这样一搜索,就知道还有哪些活要干,日志都省了。

注释时建议留下自己的名字,不仅可以彰显个人成就,也是在出现问题时,快速找到对应的负责人,做一个信任自己代码和富有责任感的 coder 吧。


参考文献

Google C++编程风格指南
陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008.P280-P282.C11.6 正确使用注释

### 回答1: Google C++编程风格指南是Google推出的一份针对C++语言的编程规范和最佳实践指南。它为开发人员提供了一些关于代码结构、命名约定、注释规范、编程技巧等方面的指导,以帮助实现高效、可读性好、维护性强的C++代码。 该指南的目标是提高代码质量和风格的一致性,使团队中的所有开发人员都能遵循相同的编码规范。这样可以促进代码的可读性和可维护性,避免因不规范的代码导致的bug和错误。 Google C++编程风格指南主要围绕以下几个方面进行了详细的规范说明: 1.命名规范:指导了变量、函数、类、命名空间等的命名规则,使命名具有描述性和可读性。 2.代码布局:指导了代码缩进、换行等格式规范,使代码结构清晰、易于阅读。 3.注释规范:指导了注释的写法和内容,使注释可以更好地解释代码的意图和逻辑。 4.错误处理:指导了如何处理错误和异常情况,以及如何做好错误处理的文档。 5.性能优化:指导了一些C++编程的性能优化技巧,使代码在性能上更高效。 除此之外,该指南还包含了一些关于C++标准库、代码组织和文件组织等方面的规范建议,以帮助开发人员更好地编写高质量的C++代码。 总之,Google C++编程风格指南是一份很有价值的编程规范文档,它不仅可以提高团队协作中代码的质量和一致性,也可以提升开发人员编写C++代码的水平和技巧。 ### 回答2: 谷歌C语言风格指南是由谷歌公司提供的一套编码规范,旨在帮助开发人员编写更加规范、可维护和可扩展的C语言代码。这个风格指南可以作为参考,帮助开发人员遵循一些统一的编码规范。 该风格指南是一个PDF文档,可以通过在谷歌搜索中输入"google c style guide pdf"来获取。在搜索结果中可能会找到来自谷歌官方或其他信任来源的链接,点击链接即可下载该PDF文件。 该指南涵盖了一系列主题,包括命名规则、注释风格、缩进和排版、函数和类的设计、错误处理、变量和常量的使用等等。通过遵循这些指南,开发人员可以编写出更规范和易读的代码,提高代码的可重用性和可维护性,减少出错的可能性。 需要注意的是,谷歌C语言风格指南并没有硬性要求每个开发人员都必须遵守,而是提供了一些最佳实践和建议。开发团队或个人可以根据自身需求和偏好进行适当的调整。 总之,谷歌C语言风格指南是一个有价值的资源,可以帮助开发人员编写更加规范和高效的C语言代码。 ### 回答3: 首先,"Google C++ Style Guide" 是一份Google为了统一团队内C++代码编写风格而发布的指南。这个指南非常详细,并覆盖了各种C++编码规范和最佳实践。 这个指南的PDF版本可以通过在Google搜索中输入"google c++ style guide pdf"来找到并下载。用户可以通过点击搜索结果中的链接,进入相关页面,并选择下载相应的PDF版本。 在这个指南中,可以学到很多关于C++编码规范的信息,包括命名规范、注释规范、代码风格、文件和目录结构等等。该指南中的规范是Google工程师们多年编码经验的总结,值得借鉴和学习。 使用这个指南可以帮助开发人员遵循一致的编码风格,从而提高代码的可读性和可维护性。而且,该指南还可以帮助团队在代码审查过程中更好地相互沟通,从而提高团队合作效率。 总之,通过查找并下载"Google C++ Style Guide"的PDF版本,开发人员可以学习并应用其中的C++编码规范和最佳实践,提高代码质量和团队协作效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值