本文是我在阅读他人C++代码时,总结的一些关于代码注释的要点。
注释的核心目的,是提高代码的可读性,给读者解释那些隐藏在代码背后的设计逻辑,暴露代码无法直接反映的缘由和目的。
一、对显而易见的过程不用注释。
1、完全废话。
以下代码中的注释,都是废话。写这些没用的注释,一般会是初学者,因为老师在授课的时候大多是这样注释的。要知道,我们在实际生产中,这些注释是没用的。因此,我们注释的时候,避免一些基础知识的注释,例如:这是xx类的构造函数,避免显而易见的过程,例如以下的if语句。再看.h文件的开头注释,第1,2,3,5行的内容没有任何意义,难道读者连这份文件名是什么都不知道?难道读者分不清这是头文件还是cpp文件?这份文件的创建时间对阅读代码没有任何作用,况且如果是代码维护,git记录的时间更精准。
//主函数入口
int main()
{
string str = "hello";//声明一个string类型变量并初始化。
A a = A(str); //调用A类构造函数,构造a对象。
...
return 0;
}
//这是A类的构造函数
A::A(string str){
//如果字符串str不为空,我们就打印str变量
if(!str.empty()) {
cout << str;
}
//否则打印”hello world”。
else {
cout << "hello world";
}
}
某.h文件开头注释
//这是数据库操作类的头文件
//文件名:db_operator
//时间:2020年1月18日
//作者:wangbadan
//在这个文件里,定义数据库操作的接口。
//所有的数据库操作接口都在这里。
2、有些注释看似有用,实则无用的注释。
以下代码中,对于例1,注释的逻辑过程读者从代码中是看得出的。看不出的是,运行模式是什么?为什么不是K和B,就初始化为A,这些模式之间到底存在什么样的逻辑关系?这些才是读者真正想作者注释出来的。
对于例2,从函数名deleteCrashTable中,读者可以看出操作是删除数据库表,虽然这个注释是有点用,但留给读者更大的疑惑是,为什么数据库表会损坏,损坏的定义是什么。
对于例3,无论在做初始化也好,在update也好,代码它总有一个过程。相对于解释代码本身在做什么,读者更喜欢看到一些代码为什么在做这些。比如例3中,如果A,B,C这个顺序很重要的话,你可以说一下为什么先初始化A,再初始化B,最后才初始化C。而不是说这是在初始化A模块,这是在初始化B模块,这是在初始化C模块。
例1:
//如果运行模式既不是K模式,也不是B模式,那么初始化为A模式。
if(m_mode.compare(K_MODE)!= 0 &&
m_mode.compare(B_MODE) != 0) {
m_mode = A_MODE;
}
例2:
void func(){
...
//删除损坏的数据库表。
deleteCrashTable();
...
}
例3:
void init() {
//初始化A模块
initA();
//初始化B模块
initB();
//初始化C模块
initC();
}
3、强烈建议使用中文注释。我相信阅读你的代码的人母语都是汉语。一方面代码注释核心目的是提高代码可读性,不是在彰显作者的才华,另一方面,大多数作者没有能用英语说清楚一个逻辑过程的才华。
4、注释不要出现中文乱码。犯这种错误的,是代码文件格式没有安排好。建议在新建代码文件的时候,就设定文件的编码格式为utf-8,或者在上传到代码仓库的时候,好好检查一遍上传的改动内容。不要出现如下尴尬情形。
/*
* ?ж???????????
* @param [out] isExist ???????1?????0??????
* return 0???????-1 ???
*/
5、注释要及时更新,不然适得其反,误导读者。
如下例子,返回值已经不再是true和false了,可注释没有更新,可能会导致调用接口时,逻辑刚好相反。建议在代码走读之前,自己先检查一遍。
/*判断字符串[str],中是否包含字符[c]
* 返回值:true:包含,false:不包含。
*/
int astrsub(){
}
6、偏僻的知识点要注释。
前面我们说,对于基础知识不用注释,但对于一些不常用的语法规则,不常用的库函数,我们可以注释,并且要注释。而对于如何定义一个知识点是否偏僻而不是基础知识,这就要自己对自己团队水平做个简单的评估了。
7、注释要有统一的格式规范。
如果你统一不了别人的代码风格,那就把自己的模块的风格统一好。
不要让注释包围了你的代码,建议一段一段的注释。如以下反例,大家不要模仿。
//注释注释注释注释注释注释注释注释注释注释注释注释注释注释注释注释
void adjust(vector<int> &arr, int len, int index)
{
//注释注释注释注释注释注释注释注释注释注释注释注释
int left = 2*index + 1; //注释注释注释注释注释注释注释注释
int right = 2*index + 2;// 注释注释注释注释注释注释注释注释注释注释注释注释
//注释注释注释注释注释注释注释注释注释注释注释注释
int maxIdx = index;//注释注释注释注释注释注释注释注释
if(left<len && arr[left] > arr[maxIdx]) maxIdx = left;
if(right<len && arr[right] > arr[maxIdx]) maxIdx = right;
//注释注释注释注释注释注释注释注释
//注释注释注释注释注释注释注释注释
if(maxIdx != index)
{
//注释注释注释注释注释注释注释注释
//注释注释注释注释注释注释注释注释
swap(arr[maxIdx], arr[index]);
adjust(arr, len, maxIdx);
}
}
统一的格式总是枯燥无味的,但不要嫌麻烦,注释规范中规定的都要做到。如下例子,第二个函数的参数没有打注释,就给读者看第二个函数的时候,带来了困难。困难你会说,第一个函数参数不是注释了吗?读者看第一个函数不就可以了。不,在有些情形你错了,当这两个函数在不同文件里,或者是隔了好几个函数调用,甚至,调用者把参数的名字改了,阅读就困难了。
//k:skb原始数据包
void func1(pkg k){
func2(k);
}
void func2(pkg j){
some_info a;
a.m_1 = j.src;
a.m_2 = j.dst;
a.m_3 = j.offet;
}
函数func2没用按照编码规范,对参数进行注释。
8、注释里不要写一些与代码无关的话。例如下面:
例1:
/*
* 啊!新年快乐,这是最后一个函数了。
*/
int astrsub(){
}
例2:某.h文件开头
//业精于勤荒于嬉,行成于思毁于随。
例3:
//模板的使用真是操蛋了。
template<typename T1,typename T2>
void swap(T1& t1, T2& t2) {
}
9、注释里不要出现git的关键标志
>>>>>>>
=======
<<<<<<<
注释里出现这些关键符号,都会影响git做代码合并。