1.变量定义
1.1.隐式声明
针对支持隐式声明的语言,我们的策略:
- 关闭隐式声明
- 声明全部的变量
- 遵循某种命名规则
- 检查变量名
2.变量初始化
2.1.初始化错误问题
-
从未对变量赋值。它的值知识程序启动时变量所处内存区域的值。
-
变量值已经过期。变量在某个地方曾经被赋值,但该值已经不再有效。
-
变量的一部分被赋值,而另一部分没有。
2.2.解决办法
- 在声明变量的时候初始化
- 在靠近变量第一次使用的位置初始化它
- 理想情况下,在靠近第一次使用比那两的位置声明和定义该变量
- 在可能的情况下使用 funal 或者 const,确保变量初始化后不被修改
- 特别注意计数器和累加器
- 在类的构造函数里初始化该类的数据成员
- 检查是否需要重新初始化
- 一次性初始化具名常量;用可执行代码来初始化变量
- 使用编译器设置来自动初始化所有变量
- 利用编译器的警告信息
- 检查输入参数的合法性
- 使用内存访问检查工具来检查错误的指针
- 在程序开始时初始化工作内存
可以用某种在程序运行前预先填充内存的工具来把程序的工作内存填充为一个可以预料的值。对于某些检测目而言,0 是个很好的填充值。因为它会确保那些尚未初始化的指针指向内存低端,很容易就能检测出误用未初始化的指针的情况。
3.作用域
作用域或者可见性(visibility)指的是变量在程序内的可见和可引用的范围。
3.1 使变量引用局部化
把变量的阴影点集中起来的主要好处是提高程序的可读性。
3.2 尽可能缩短变量的“存活”时间
与“变量局部化”类似,缩短变量“存活”时间的好处在于当你真正想要修改一个变量的那些位置之间的区域时,该变量被错误或者无意修改的可能性就降低了。
3.3 减少作用域的一般原则
-
在循环开始之前再去初始化该循环里使用的变量,而不是在该循环所属的子程序的开始处初始化这些变量
-
直到变量即将被使用时再为其赋值
-
把相关语句放到一起
-
把相关语句组提取成单独的子程序
-
开始时采用最严格的可见性,然后根据需要扩展变量的作用域(非常重要)
——减少变量作用域的方法之一就是尽量使变量局部化。与扩充一个作用域小的变量的作用域相比,缩减一个已经有很大作用域的变量的作用域是非常困难的。——换句话说,把全局变量转变为类成员变量要比把类成员变量转变成全局变量困难得多。把一个 protected 数据成员转变成 private 数据成员的难度也比你变化要大。这样一来,当对变量的作用域犹豫不决时,你应该倾向于选择该变量所能具有的最小的作用域:首选将变量局限与某个特定的循环,然后是局限于某个子程序,其次成为某个类的 private 变量,protected 变量,再其次对包可见,最后在不得已的情况下再把它作为全局变量。
3.4 有关缩小变量作用域的说明
程序员采用哪种缩小变量作用域的方法,取决于他如何看待“方便性”和“智力上的可管理性”。有的程序员把很多变量定义为全局的,因为全局变量访问起来非常方便,而且无需再去考虑与参数列表和类作用域有关的规则。他们更看重能在任意时间里访问变量所带来的便捷,而不是由此而带来的风险。
其他程序员更愿意尽可能地使用变量局部化,因为这样有助于提高智力上的可管理性。你能够隐藏的信息越多,在同一时间所需要考虑的信息就越少。你需要考虑的信息越少,则忘记某一项信息而犯错误的几率也就越小。
“方便性”和“智力可管理性”两种理念之间的区别,归根结底来源于侧重写程序还是读程序之间的区别。使作用域最大化可能真的会让程序写起来比较容易,但相对于子程序功能划分明确的程序,一个允许任何子程序在任何时间使用任何变量的程序是更难理解的。对于这样的程序,你不能只去理解一个子程序,你还必须要理解其他所有使用了相同全局数据的子程序才行。这种程序无论阅读、调试还是修改起来都很困难。
由此可见,你应该把每个变量定义成只对需要看到它的、最小范围的代码段可见。如果你能把变量的作用域限定在一个单独的循环或者子程序,那是再好不过的了。如果你无法把作用域限制在一个子程序里,那么就把可见性限定到某个类内部的那些子程序。如果你无法把变量的作用域限定在对该变量承担最主要的责任的那个类里面,那么久创建一些访问器子程序来让其他类共享该变变量的数据。这样你就会发现自己极少需要使用赤裸裸的全局数据。
4.持续性
“持续性”是对一项数据的生命期的另一种描述。持续性具有多种形态:
-
特定代码段或者子程序里。 例如循环或者子程序中的变量。
-
你要你允许,它就会持续下去。 例如使用 new 创建的动态内存。
-
程序的生命期。 大多数语言的全局变量都属于这一类,C++ 和 Java 中的 static 变量也是如此。
-
永远持续。 例如存储在数据库或者配置文件中的数据。
针对持续性相关的问题,有如下几点建议:
-
在程序中加入调试代码或者断言来检查那些关键变量的合理取值。如果变量取值变得不合理,就发出警告信息通知你去寻找是否有不准确的初始化。
-
准备抛弃变量时给它们附上“不合理的数值”,例如,你可以在删除一个指针后把它设置为 null。
-
编写代码时要假设数据并没有持续性。
-
养成在使用所有数据之前声明和初始化的习惯。
5.绑定时间
6.数据类型和控制结构之间的关系(序列型数据、选择型数据、迭代型数据)
7.为变量指定单一用途
-
每个变量只用于单一用途
-
避免让代码具有隐含含义
——变量 pageCount 的取值可能表示已打印纸张的数量,当它等于 -1表明有错误发生。这种滥用在技术领域里被称为“混合耦合”。这样的变量用于两种以上的用途,也就意味着其类型对于其中的任何一项任务而言都是错误的。 pageCount 在正常情况下表示纸张的数目;这种情况下它是一个整数。然而当 pageCount 等于 -1 时,它表明有错误发生了;整数类型客串了布尔类型。
- 确保使用了所有已声明的变量