Effective C++——条款22(第4章)

条款22:    将成员变量声明为 private

Declare data members private.
    
    首先看看为什么变量不应该是 public,然后再看看所有返回 public 成员变量的论点同样适用于 protected 成员变量.最后导出一个结论:成员变量应该是 private.

    从语法一致性开始(详见 条款18). 如果成员变量不是 public,客户唯一能够访问对象的办法就是通过成员函数.如果 public 接口内的每样东西都是函数,客户就不需要在打算访问 class 成员时迷惑地试着记住是否该使用小括号(圆括号).他们只要做就是了,因为每样东西都是函数.
     使用函数可以让客户对成员变量的处理有更精确的控制.如果令成员变量为 public,每个人都可以读写它,但如果以函数取得或设定其值,就可以实现出"不准访问","只读访问",以及"读写访问".甚至可以实现"只写访问",如果要想要的话"
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;            // 对此int无任何访问动作
    int readOnly;            // 对此int做只读访问
    int readWrite;            // 对此int做读写访问
    int writeOnly;            // 对此int做只写访问
};
    如此细微地划分访问控制颇有必要,因为许多成员变量应该被隐藏起来.此外, 如果通过函数访问成员变量,日后可改以某个计算替换这个成员变量,而 class 客户一点也不会知道 class 的内部实现已经起了变化.
    举个例子,假设正在写一个自动测速程序,当汽车通过,其速度便被计算并填入一个速度收集器内:
class SpeedDataCollection {
public:
    ...
    void addValue(int speed);
    double averageSoFar() const;
    ...
};
    现在考虑成员函数averageSoFar, 做法一是在 class 内设计一个成员变量,记录至今以来所有速度的平均值.当averageSoFar被调用,只需返回那个成员变量就好. 另一个做法是令averageSoFar每次调用时重新计算平均值,此函数有权力调取收集器内的每一笔速度值.
    上述第一种做法(随时保持平均值)会使每一个SpeedDataCollection对象变大,因为必须为用来存放目前平均值,累积总量,数据点数的每一个成员变量分配空间.然而averageSoFar却可因此而十分高效;它可以只是一个返回目前平均值的 inline 函数.相反地,"被询问才计算平均值"会使得averageSoFar执行较慢,但每一个SpeedDataCollection对象比较小.
     哪一个比较好?在一部 内存不够充足的机器上,或是在一个并不常常需要平均值的应用程序中,"每次需要时才计算"或许是比较好的解法.但 在一个频繁需要平均值的应用程序中,如果反应速度非常重要,内存不是重点,这时候"随时维持一个当前平均值"往往更好一点.
    将成员变量隐藏在函数接口的背后,可以为"所有可能的实现"提供弹性.例如这可使得成员变量被读或被写时轻松通知其他对象,可以验证 class 的约束条件以及函数的前提和事后状态,可在多线程环境中执行同步控制...等等.
    封装非常重要, 如果对客户隐藏成员变量(也就是封装它们),可以确保 class 的约束条件总是会获得维护,因为只有成员函数可以影响它们. 如果不隐藏它们呢,很快就会发现,即使拥有 class 原始码,改变任何 public 事物的能力还是极端受到束缚,因为那会破坏太多客户码.public 意味不封装,而几乎可以说,不封装意味不可改变,特别是对被广泛使用的 class 而言. 被广泛使用的 class 是最需要封装的一个族群,因为它们最能够从"该采用一个较佳实现版本"中获益.
    protected 成员变量的论点十分类似,protected 成员变量的封装性是不是高过 public 成员变量?答案令人惊讶:并非如此.
    条款23指出, 某些东西的封装性与"当其内容改变时可能造成的代码破坏量"成反比.因此,成员变量的封装性与"成员变量的内容改变时所破坏的代码数量"成反比.所谓改变,也许是从 class 中移除它.
    假设有 一个 public 成员变量,最终取消了它.多少代码可能会被破坏呢?呃, 所有使用它的客户代码都会被破坏,而那是一个不可知的大量.因此 public 成员变量完全没有封装性.
    假设有 一个 protected 成员变量,最终取消了它.多少代码可能会被破坏呢?呃, 所有使用它的derived class 都会被破坏,那往往也是个不可知的大量.因此,protected 成员变量就像 public 成员变量一样缺乏封装性.
    一旦将一个成员变量声明为 public 或 protected 而客户开始使用它,就很难改变那个成员变量所涉及的一切.太多代码需要重写,重新测试,重新编写文档,重新编译. 从封装的角度观看,起始只有两种访问权限:private(提供封装)和其它(不提供封装).
     注意:
    切记将成员变量声明为 private.这可赋予客户访问数据的一致性,可细微划分访问控制,允许约束条件获得保证,并提供 class 作者以充分的实现弹性.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值