您的位置:首页 > 编程语言 > C语言/C++

[翻译] Effective C++, 3rd Edition, Item 22: 将 data members(数据成员)声明为 private

2005-08-01 23:06 417 查看
[b]Item 22: 将 data members(数据成员)声明为 private[/b]

作者:Scott Meyers

译者:fatalerror99 (iTePub's Nirvana)

发布:http://blog.csdn.net/fatalerror99/

好了,先公布一下计划。首先,我们将看看 data members(数据成员)为什么不应该是 public(公有)的。然后,我们将看到所有反对 public data members(公有数据成员)的理由同样适用于 protected(被保护)的。这就导出了 data members(数据成员)应该是 private(私有)的结论,至此,我们就结束了。

那么,public data members(公有数据成员),为什么不呢?

我们从先从语法一致性开始(参见 Item 18)。如果 data members(数据成员)不是 public(公有的),客户访问一个 object 的唯一方法就是通过 member functions(成员函数)。如果在 public interface(公有接口)中的每件东西都是一个函数,客户就不必挠着脑袋试图想起当他们要访问一个 class 的成员时是否需要使用圆括号。他们只要使用就可以了,因为每件东西都是一个函数。一生坚持这一方针,能节省很多挠头的时间。

但是也许你不认为一致性的理由是强制性的。使用函数可以让你更加精确地控制 data members(数据成员)的可存取性的事实又怎么样呢?如果你让一个 data member(数据成员)为 public(公有)的,每一个人都可以 read-write access(读写访问)它,但是如果你使用函数去得到和设置它的值,你就能实现 no access(禁止访问),read-only access(只读访问)和 read-write access(读写访问)。更进一步,如果你需要,你甚至可以实现 write-only access(只写访问):

class AccessLevels {
public:
...
int getReadOnly() const { return readOnly; }

void setReadWrite(int value) { readWrite = value; }
int getReadWrite() const { return readWrite; }

void setWriteOnly(int value) { writeOnly = value; }

private:
int noAccess; // no access to this int

int readOnly; // read-only access to this int

int readWrite; // read-write access to this int

int writeOnly; // write-only access to this int
};

这种条分缕析的 access control(访问控制)很重要,因为很多 data members(数据成员)should(应该)被隐藏。每一个 data member(数据成员)都需要一个 getter 和 setter 的情况是很罕见的。

还不相信吗?那么该拿出一门重炮了:encapsulation(封装)。如果你通过一个函数实现对一个 data member(数据成员)的访问,你可以在以后用一个计算来替换这个 data member(数据成员),使用你的 class 的人不会有任何察觉。

例如,假设你为一个自动设备写一个应用程序用于监视通过的汽车的速度。每通过一辆汽车,它的速度就被算出,并把那个值加入到迄今为止收集到的所有速度数据的集合中:

class SpeedDataCollection {

...
public:
void addValue(int speed); // add a new data value

double averageSoFar() const; // return average speed

...
};

现在考虑 member function(成员函数)averageSoFar 的实现:实现它的办法之一是在 class 中用一个 data member(数据成员)来实时变化迄今为止收集到的所有速度数据的平均值。无论何时 averageSoFar 被调用,它只是返回那个 data member(数据成员)的值。另一个不同的方法是在每次调用 averageSoFar 时重新计算它的值,通过检验集合中每一个数据值它能做成这些事情。

第一种方法(保持一个实时变化的平均值)使每一个 SpeedDataCollection object 都比较大,因为你必须为持有实时变化的平均值,累计的和以及数据点的数量的 data members(数据成员)分配空间。然而,averageSoFar 可以现得非常高效,它仅仅是一个返回实时变化的平均值的 inline 函数(参见 Item 30)。反过来,无论何时被请求都要计算平均值使得 averageSoFar 的运行比较慢,但是每一个 SpeedDataCollection object 都比较小。

谁能说哪一个最好?在内存非常紧张的机器(例如,一个嵌入式路边设备)上,以及在一个很少需要平均值的应用程序中,每次都计算平均值可能是较好的解决方案。在一个频繁需要平均值的应用程序中,速度是基本的要求,而且内存不成问题,保持一个实时变化的平均值更为可取。这里的重点在于通过一个 member function(成员函数)访问平均值(也就是说,通过将它封装),你能互换这两个不同的实现(也包括其他你可能想到的),对于客户,最多也就是必须重新编译。(你可以用在后面的 Item 31 中记述的技术来消除这个麻烦。)

将 data members(数据成员)隐藏在 functional interfaces(函数化接口)之后能为各种实现提供机动性。例如,它可以在 data members(数据成员)被读或者写的时候很简单地通报其它 objects,可以验证 class invariants(类的不变量)以及函数的前置和后置条件,可以在多线程环境中执行同步任务,等等。从类似 Delphi 和 C# 的语言来到 C++ 的程序员会认同这种类似那些语言中的 "properties"(“属性”)的等价物的功能,虽然需要附加一对额外的圆括号。

关于 encapsulation(封装)这一点来说可能比它最初显现出来的更加重要。如果你对你的客户隐藏你的 data members(数据成员)(也就是说,封装它们),你就能确保 class invariants(类的不变量)总能被维持,因为只有 member functions(成员函数)能影响它们。此外,你预留了以后改变你的实现决策的权力。如果你不隐藏这样的决策,你将很快发现,即使你拥有一个 class 的源代码,你改变任何一个 public 的东西的能力也是极端有限的,因为有太多的客户代码将被破坏。public 意味着未被封装,而实际上可以说,未被封装意味着不可改变,尤其是被广泛使用的 classes。但是仍然被广泛使用的 classes 大多数都是使用了封装的,因为它们可以最大限度地受益于用一种更好的实现替换现有实现的能力。

反对 protected data members(被保护数据成员)的理由是类似的。实际上,它是一样的,虽然起先看起来似乎不那么像。很显然,关于语法一致性和条分缕析的访问控制的论证就像用于 public 一样可以应用于 protected data,但是关于 encapsulation(封装)又如何呢?难道 protected data members(被保护数据成员)不比 public(公有)的更具有封装性吗?实话实说,令人惊讶的答案是它们不。

Item 23 阐释了如果某物发生了变化,某物的 encapsulation(封装)与可能被破坏的代码数量成反比。于是,如果一个 data member(数据成员)发生了变化,例如,如果它被从 class 中移除(可能是为了支持计算,就像在上面的 averageSoFar 中),这个 data member(数据成员)的 encapsulatedness(封装性)与可能被破坏的代码数量成反比。

假设我们有一个 public data member(公有数据成员),随后我们排除了它。有多少代码会被破坏呢?所有使用了它的客户代码,其数量通常 unknowably large(大得难以置信)。因此 public data members(公有数据成员)就是完全未被封装的。但是,假设我们有一个 protected data member(被保护数据成员),随后我们排除了它。现在有多少代码会被破坏呢?所有使用了它的 derived classes(派生类),典型情况下,代码的数量还是 unknowably large(大得难以置信)。因此 protected data member(被保护数据成员)就像 public(公有)的一样没有被封装,因为在这两种情况下,如果 data members(数据成员)发生变化,被破坏的客户代码的数量都 unknowably large(大得难以置信)。这是不符合直觉的,但是富有经验的库实现者会告诉你,这是千真万确的。一旦你声明一个 data member(数据成员)为 public 或 protected,而且客户开始使用它,就很难再改变与这个 data member(数据成员)有关的任何事情。有太多的代码不得不被重写,重测试,重文档化,或重编译。从 encapsulation(封装)的观点来看,实际只有两个访问层次:private(提供了 encapsulation(封装))与所有其它(没有提供)。

Things to Remember

声明 data members(数据成员)为 private。它为客户提供了访问数据的语法上的一致,提供条分缕析的访问控制,允许不变量被强制,而且为 class 的作者提供了实现上的机动性。

protected 并不比 public 的封装性强。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐