谨慎使用私有继承

2008-02-23 05:28:49来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

 在《C 箴言:确保公开继承模拟“is-a”》一文中论述了 C 将 public inheritance(公有继承)视为一个 is-a 关系。当给定一个 hierarchy(继承体系),其中有一个 class Student 从一个 class Person 公有继承,当为一个函数调用的成功而有必要时,需要将 Students 隐式转型为 Persons,他通过向编译器展示来做到这一点。用 private inheritance(私有继承)代替 public inheritance(公有继承)把这个例子的一部分重做一下是值得的:

class Person { ... };
class Student: private Person { ... }; // inheritance is now private

void eat(const Person& p); // anyone can eat

void study(const Student& s); // only students study

Person p; // p is a Person
Student s; // s is a Student

eat(p); // fine, p is a Person

eat(s); // error! a Student isn't a Person

  很明显,private inheritance(私有继承)不意味着 is-a。那么他意味着什么呢?

  “喂!”您说:“在我们得到他的含义之前,我们先看看他的行为。private inheritance(私有继承)有怎样的行为呢?”好吧,支配 private inheritance(私有继承)的第一个规则您只能从动作中看到:和 public inheritance(公有继承)对照,假如 classes(类)之间的 inheritance relationship(继承关系)是 private(私有)的,编译器通常不会将一个 derived class object(派生类对象)(诸如 Student)转型为一个 base class object(基类对象)(诸如 Person)。这就是为什么为 object(对象)s 调用 eat 会失败。第二个规则是从一个 private base class(私有基类)继承的 members(成员)会成为 derived class(派生类)的 private members(私有成员),即使他们在 base class(基类)中是 protected(保护)的或 public(公有)的。

  行为但是如此。这就给我们带来了含义。private inheritance(私有继承)意味着 is-implemented-in-terms-of(是根据……实现的)。假如您使 class(类)D 从 class(类)B 私有继承,您这样做是因为您对于利用在 class(类)B 中才可用的某些特性感兴趣,而不是因为在 types(类型)B 和 types(类型)D 的 objects(对象)之间有什么概念上的关系。同样地,private inheritance(私有继承)纯粹是一种实现技术。(这也就是为什么您从一个 private base class(私有基类)继承的每一件东西都在您的 class(类)中变成 private(私有)的原因:他全部都是实现的细节。)利用《接口继承和实现继承》中提出的条款,private inheritance(私有继承)意味着只有 implementation(实现)应该被继承;interface(接口)应该被忽略。

  假如 D 从 B 私有继承,他就意味着 D objects are implemented in terms of B objects(D 对象是根据 B 对象实现的),没有更多了。private inheritance(私有继承)在 software design(软件设计)期间没有任何意义,只在 software implementation(软件实现)期间才有。 private inheritance(私有继承)意味着 is-implemented-in-terms-of(是根据……实现的)的事实有一点混乱,正如《通过composition模拟“has-a”》一文中所指出的 composition(复合)也有同样的含义。您怎么预先在他们之间做出选择呢?答案很简单:只要您能就用 composition(复合),只有在绝对必要的时候才用 private inheritance(私有继承)。什么时候是绝对必要呢?主要是当 protected members(保护成员)和/或 virtual functions(虚拟函数)掺和进来的时候,另外更有一种和空间相关的极端情况会使天平向 private inheritance(私有继承)倾斜。我们稍后再来操心这种极端情况。

  毕竟,他只是一种极端情况。 假设我们工作在一个包含 Widgets 的应用程式上,而且我们认为我们需要更好地理解 Widgets 是怎样被使用的。例如,我们不但要知道 Widget member functions(成员函数)被调用的频度,还要知道 call ratios(调用率)随着时间的流逝如何变化。带有清楚的执行阶段的程式在不同的执行阶段能够有不同的行为侧重。例如,一个编译器在解析阶段对函数的使用和优化和代码生成阶段就有很大的不同。

  我们决定修改 Widget class 以持续跟踪每一个 member function(成员函数)被调用了多少次。在运行时,我们能够周期性地检查这一信息,和每一个 Widget 的这个值相伴的可能更有我们觉得有用的其他数据。为了进行这项工作,我们需要设立某种类型的 timer(计时器),以便在到达收集用法统计的时间时我们能够知道。

  尽可能复用已有代码,而不是写新的代码,我在我的工具包中翻箱倒柜,而且满意地找到下面这个 class(类):

class Timer {
public:
explicit Timer(int tickFrequency);
virtual void onTick() const; // automatically called for each tick
...
};

  这正是我们要找的:一个我们能够根据我们的需要设定 tick 频率的 Timer object,而在每次 tick 时,他调用一个 virtual function(虚拟函数)。我们能够重定义这个 virtual function(虚拟函数)以便让他检查 Widget 所在的当前状态。很完美!

  为了给 Widget 重定义 Timer 中的一个 virtual function(虚拟函数),Widget 必须从 Timer 继承。但是 public inheritance(公有继承)在这种情况下不合适。Widget is-a(是个)Timer 不成立。Widget 的客户不应该能够在一个 Widget 上调用 onTick,因为在概念上那不是的 Widget 的 interface(接口)的一部分。允许这样的函数调用将使客户更容易误用 Widget 的 interface(接口),这是个对《使接口易于正确使用难错误使用》中的关于“使接口易于正确使用,而难以错误使用”的建议的明显违背。public inheritance(公有继承)在这里不是正确的选项。

  因此我们就 inherit privately(秘密地继承):

class Widget: private Timer {
private:
virtual void onTick() const; // look at Widget usage data, etc.
...
};

  通过 private inheritance(私有继承)的能力,Timer 的 public(公有)onTick 函数在 Widget 中变成 private(私有)的,而且在我们重新声明他的时候,也把他保留在那里。重复一次,将 onTick 放入 public interface(公有接口)将误导客户认为他们能够调用他,而这违背了我在《使接口易于正确使用难错误使用》。

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇: 创建和使用库:静态、共享和动态

下一篇: C 中extern