我们所编写的C++程序代码,主要有两个目的:一是供机器执行,二是供程序员阅读。而代码的质量,往往体现在第二点,也就是说,可读性好是优秀代码的主要指标。
如果你到现在还认为代码的可读性不是很重要,那你就大错特错了。优秀的代码不仅是代码外观整洁、清晰,更富有条理性,而且可是程序更容易被理解和维护,他在很大程度上决定了软件工程的开销。有一点是可以肯定的,没有一种编程方法、风格是最好的,最完美的。但是有些编程方式的确比其他方式对代码的优化更有利和有效,产生更大的影响。
提高的代码的可读性手段
- 清晰地表达意图。
- 一个方法只做一件事情,同一个方法体内,保持相同的抽象层次。
- 不要重复自己(避免手动的复制与粘贴代码)。
- 减少“语法噪音”。
- 命名时取有意义的名字,避免不规范的缩写。
- 编写良好的注释。
提供代码可读性的方式有很多。不同的人有不同的看法。但不论如何,还是有一定的规律性的。例如编写良好的注释、规范的变量命名等等。本议题余下部分将详细的讨论提高代码可读性中,被大家认可的准则。
良好的注释,可以起到事半功倍的效果。中国有这么一句老话:不在多,贵在精。他同样适用于注释。代码注释,不是让你长篇大论的写那么一堆。而在于简明扼要,就像表达程序的中心大意。当然编写出可自注释的代码比编写注释更高一筹了。
注释技巧:逐层注释,为每个代码块添加注释,并在每一层使用统一的注释方法和风格。使用分段注释,如果有多个代码块,而每个代码块实现单一任务,可在每个代码块前添加一个注释来向读者说明这段代码的功能。
例如:
// 验证所有数据记录,是否正确
foreach (Record record in records)
{
if (rec.checkStatus() == Status.OK)
{
. . .
}
}
// 开始记录转换
Context ctx = new ApplicationContext();
ctx.BeginTransaction();
不要画蛇添枝,自作聪明。例如,避免编写出下述注释:无用的注释会浪费你的时间,并将转移读者对该代码细节的理解。
// 如果 a 等于 5。
if (a == 5)
{
// 设置counter等于0。
counter = 0;
}
代码注释时,重点关注代码的要点,同时使用一致的风格。
使用特有的注释注释标签,例如MFC中通常用的注释标签就是TOD0
在写代码时就添加注释,这时在你脑海里的是清晰完整的思路。例如:
public void ProcessOrder()
{
// Make sure the products are available
// Check that the customer is valid
// Send the order to the store
// Generate bill
}
规范化的变量命名,尽量使用有意义的单词来做变量名,首先,用拼音来做变量名是非常不好的习惯,因为在汉字中同音字实在太多了,而且又分音调,像ck这种变量名估计当时记得,过段时间估计就会忘记了。“匈牙利”命名法是一个不错的选择,所以我建议大家选择“匈牙利”命名法。
匈牙利命名法:匈牙利命名法,由匈牙利人发明。匈牙利命名法关键是:标识符的名字以一个或者多个小写字母开头作为前缀;前缀之后的是首字母大写的一个单词或多个单词组合,该单词要指明变量的用途。
一般情况下匈牙利命名法命名方式是:<scope>_<prefix>_<qualifier>。scope前缀如表13-1所示,prefix前缀如表13-2所示。
前 缀 | 类 型 | 例 子 |
---|---|---|
g | 全局变量域 | g_Servers |
m | 成员变量 | m_pDoc |
l | 局部作用域 | l_strName少用 |
前 缀 | 类 型 | 内存规格描述 | 例 子 |
---|---|---|---|
ch | char | 8-bit character | chGrade |
ch | TCHAR | 16-bit character if _UNICODE is defined | chName |
b | BOOL | Boolean value | bEnabled |
n | int | Integer (size dependent on operating system) | nLength |
n | UINT | Unsigned value (size dependent on operating system) | nLength |
w | WORD | 16-bit unsigned value | wPos |
l | LONG | 32-bit signed integer | lOffset |
dw | DWORD | 32-bit unsigned integer | dwRange |
p | * | Ambient memory model pointer | pDoc |
lp | FAR* | Far pointer | lpDoc |
lpsz | LPSTR | 32-bit pointer to character string | lpszName |
lpsz | LPCSTR | 32-bit pointer to constant character string | lpszName |
lpsz | LPCTSTR | 32-bit pointer to constant character string if _UNICODE is defined | lpszName |
h | handle | Handle to Windows object | hWnd |
lpfn | (*fn)() | callbackFar pointer to CALLBACK function | lpfnAbort |
st | struct | Struct | stPerson |
保持接口功能的单一性。不要编写面面俱到的函数接口,保持函数功能的单一性,按照这种原则实现的代码具有自注释功能。
降低函数的圈复杂度,一般圈复杂度高的函数,函数中if,嵌套等逻辑变化较多。不利于函数的调试和问题的定义,请参考议题51。很深的嵌套总会让人望而生畏,一个个if else怎么看都像陷阱,大部分同学可能没有趟过这样的深坑,为了保持和团队规范的一致性:一个接口函数体只能在最后有一个return出口的规范,确实导致许多新人编写出高嵌套的代码,其实完全可以通过Group的方式进行化解。可以通过多return出口将这个世界打平。
提要代码可读性的方法还有很多,这讨论的仅是抛砖引玉的作用,限于篇幅就在一一列举了。有兴趣的同学可以专门搜集一下。
请谨记
- 良好的注释、规范的变量命名,可显著的提高程序的可读性。可读性好的代码更容易被理解和维护。
- 可读性好的代码,可显著降低程序的开发成本。这种成功包括程序员的沟通和代码的维护。