《高质量-C++-C-编程指南v-1-0》-总结(上)

前言

微软公司一位资深软件工程师出的面试题。他让我写函数 strcpy 的代码。
这么一个小不点的函数,他从三个方面考查:
(1)编程风格;
(2)出错处理;
(3)算法复杂度分析(用于提高性能)。

试问有多少软件开发人员对正确性、健壮性、可靠性、效率、易用性、可读性(可理解性)、可扩展性、可复用性、兼容性、可移植性等质量属性了如指掌?并且能在实践中运用自如?。“高质量”可不是干活小心点就能实现的!

我打算定义编程老手和编程高手,请您别见笑。
定义 1:能长期稳定地编写出高质量程序的程序员称为编程老手。
定义 2:能长期稳定地编写出高难度、高质量程序的程序员称为编程高手

第 1 章 文件结构

为了了解作用以及如何更好地使用.

版权和版本的声明
版权和版本的声明位于头文件和定义文件的开头(参见示例 1-1),主要内容有:
(1)版权信息。
(2)文件名称,标识符,摘要。
(3)当前版本号,作者/修改者,完成日期。
(4)版本历史信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* Copyright (c) 2001,上海贝尔有限公司网络应用事业部
* All rights reserved.
*
* 文件名称: filename.h
* 文件标识: 见配置管理计划书
* 摘 要: 简要描述本文件的内容
*
* 当前版本: 1.1
* 作 者: 输入作者(或修改者)名字
* 完成日期: 2001年7月20日
*
* 取代版本:1.0
* 原作者 : 输入原作者(或修改者)名字
* 完成日期: 2001年5月10日
*/

头文件的作用
早期的编程语言如 Basic、Fortran 没有头文件的概念,C++/C 语言的初学者虽然会
用使用头文件,但常常不明其理。这里对头文件的作用略作解释:
(1)通过头文件来调用库功能。在很多场合,源代码不便(或不准)向用户公布,只要
向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功
能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。
(2)头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中
的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的
负担。

1.5 目录结构
如果一个软件的头文件数目比较多(如超过十个),通常应将头文件和定义文件分别
保存于不同的目录,以便于维护。
例如可将头文件保存于 include 目录,将定义文件保存于 source 目录(可以是多级
目录)。
如果某些头文件是私有的,它不会被用户的程序直接引用,则没有必要公开其“声
明”。为了加强信息隐藏,这些私有的头文件可以和定义文件存放于同一个目录。

科学规划目录结构, 加强信息隐藏, 方便个人维护代码 和 信息安全.

第 2 章 程序的版式

版式虽然不会影响程序的功能,但会影响可读性。程序的版式追求清晰、美观,是程序风格的重要构成因素。
可以把程序的版式比喻为“书法”。好的“书法”可让人对程序一目了然,看得兴致
勃勃。

空行起着分隔程序段落的作用。空行得体(不过多也不过少)将使程序的布局更加
清晰。空行不会浪费内存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。
所以不要舍不得用空行。

  • 在每个类声明之后、每个函数定义结束之后都要加空行。
  • 在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。

一行代码只做一件事情,如只定义一个变量,或只写一条语句。这样
的代码容易阅读,并且方便于写注释。

尽可能在定义变量的同时初始化该变量(就近原则)
如果变量的引用处和其定义处相隔比较远,变量的初始化很容易被忘记。如果引用了未被初始化的变量,可能会导致程序错误。本建议可以减少隐患。

长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以
便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。

1
2
3
4
5
6
7
8
9
10
11
/* like this */
if ((very_longer_variable1 >= very_longer_variable12)
&& (very_longer_variable3 <= very_longer_variable14)
&& (very_longer_variable5 <= very_longer_variable16))
{
dosomething();
}

/* like this */
virtual CMatrix CMultiplyMatrix (CMatrix leftMatrix,
CMatrix rightMatrix);

修饰符的位置
修饰符 * 应该靠近数据类型还是该靠近变量名,是个有争议的活题。
若将修饰符 * 靠近数据类型,例如:int* x; 从语义上讲此写法比较直观,即 x
是 int 类型的指针。
上述写法的弊端是容易引起误解,例如:int* x, y; 此处 y 容易被误解为指针变
量。虽然将 x 和 y 分行定义可以避免误解,但并不是人人都愿意这样做。

【规则 2-7-6 】注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不可放在下方。
【规则 2-7-8 】当代码比较长,特别是有多重嵌套时,应当在一些段落的结束处加注释,便于阅读。

类的版式
类可以将数据和函数封装在一起,其中函数表示了类的行为(或称服务)。类提供关键字 public、protected 和 private,分别用于声明哪些数据和函数是公有的、受保护的或者是私有的。这样可以达到信息隐藏的目的,即让类仅仅公开必须要让外界知道的内容,
而隐藏其它一切内容。我们不可以滥用类的封装功能,不要把它当成火锅,什么东西都往里扔。
类的版式主要有两种方式:
(1)将 private 类型的数据写在前面,而将 public 类型的函数写在后面。
采用这种版式的程序员主张类的设计“以数据为中心”,重点关注类的内部结构。
(2)将 public 类型的函数写在前面,而将 private 类型的数据写在后面。
采用这种版式的程序员主张类的设计“以行为为中心”,重点关注的是类应该提供什么样的接口(或服务)。

我建议读者采用“以行为为中心”的书写方式,即首先考虑类应该提供什么样的函
数。这是很多人的经验——“这样做不仅让自己在设计类时思路清晰,而且方便别人阅读。因为用户最关心的是接口,谁愿意先看到一堆私有数据成员!”
image.png

第 3 章 命名规则

据考察,没有一种命名规则可以让所有的程序员赞同,程序设计教科书一般都不指定命名规则。命名规则对软件产品而言并不是“成败悠关”的事,我们不要化太多精力试图发明世界上最好的命名规则,而应当制定一种令大多数项目成员满意的命名规则,并在项目中贯彻实施。

【规则 3-1-1 】标识符应当直观且可以拼读,可望文知意,不必进行“解码”。
标识符最好采用英文单词或其组合,便于记忆和阅读。切忌使用汉语拼音来命名。
程序中的英文单词一般不会太复杂,用词应当准确。例如不要把 CurrentValue 写成 NowValue。

【规则 3-1-2 】标识符的长度应当符合 “min-length && max-information”原则。
几十年前老 ANSI C 规定名字不准超过 6 个字符,现今的 C++/C 不再有此限制。一般来说,长名字能更好地表达含义,所以函数名、变量名、类名长达十几个字符不足为怪。那么名字是否越长约好?不见得! 例如变量名 maxval 就比 maxValueUntilOverflow好用。单字符的名字也是有用的,常见的如 i, j, k, m, n, x, y, z 等,它们通常可用作函数内的局部变量。

我猜之所以有 i, 有 j, 没有 l 是因为字母小写的 l 和数字 1 非常接近.

【规则 3-1-7 】全局函数的名字应当使用“动词”或者“动词+名词”(动宾词组)。
类的成员函数应当只使用“动词”,被省略掉的名词就是对象本身。
例如:
DrawBox(); // 全局函数
box->Draw(); // 类的成员函数

【建议 3-1-1 】尽量避免名字中出现数字编号,如 Value1,Value2 等,除非逻辑上的确需要编号。这是为了防止程序员偷懒,不肯为命名动脑筋而导致产生无意义的名字(因为用数字编号最省事)。