Declare data members private
先来看看 public 成员变量,为什么不采用它呢?
- 如果成员变量不是 public,客户唯一能够访问对象的办法就是通过成员函数。
- 使用函数处理变量有更精确的控制,毕竟不是每个成员变量都需要一个 getter 函数和 setter 函数。
- 封装,便于日后更改维护代码。
举个栗子,假设写一个自动测速程序,当汽车通过时,其速度便被计算并填入一个速度收集器里:
class SpeedDataCollection{
...
public:
void addValue(double speed); // 添加一笔新数据
double averageSoFar() const; // 返回平均速度
...
};
现在考虑成员函数 averageSoFar 。做法之一是设计一个成员变量,记录至今以来所有速度的平均值。当 averageSoFar 被调用时,只需返回那个成员变量就好。另一种做法是令 averageSoFar 每次被调用时重新计算平均值,此函数有权利调取收集器内的每一笔速度值。
上述第一种做法(随时保持平均值)会使每一个 SpeedDataCollection 对象变大,因为必须为用来存放目前平均值、累积总量、数据点数的每一个成员变量分配空间。然而 averageSoFar 却可以因此而十分高效;它可以只是一个返回目前平均值的 inline 函数。相反地, “ 被询问才计算平均值 ” 会使得 averageSoFar 执行较慢,但每一个 SpeedDataCollection 对象比较小。
☆ 只考虑速度不考虑内存的话就用第一种做法,要是再一部内存吃紧的机器,比如嵌入式机器上则采用第二种做法较好。
☆ 重点是,由于通过成员函数来访问平均值(也就是封装了它),你就得以替换不同的实现方式(以及其他你可能想到的东西),客户最多只需重新编译。
- public 意味不封装,而几乎可以说,不封装意味不可改变,特别是对广泛使用的 classes 而言。被广泛使用的 classes 是最需要封装的一个族群,因为它们最能够从 “ 改采用一个较佳实现版本 ” 中获益。
- 成员变量的封装性与 “ 成员变量的内容改变时所破坏的代码数量 ” 成反比。
假设我们有一个 public 成员变量,而我们最终取消了它,所有使用它的客户码都会被破坏,而那是一个不可知的大量。
假设我们有一个 protected 成员变量,而我们最终取消了它,所有使用它的derived classes 都会被破坏,那往往也是一个不可知的大量。
- 从封装的角度来看,其实只有两种访问权限:private(提供封装)和其他(不提供封装)。
请记住:
- 切记将成员变量声明为 private。这可赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并提供 class 作者以充分的实现弹性。
- protected 并不比 public 更具封装性。