关于《effective C++》条款39:明智而审慎地使用private继承 中的的一个问题

发布于 2022-09-04 15:44:06 字数 593 浏览 36 评论 0

我们有这样一个定时器

class Timer{
public:
    explict Timer(int tickFrequency);
    virtual void onTick() const;
    ...
};

  然后比较了继承和复合。
  继承的代码:

class Widget:private Timer{
private:
    virtual void onTick() const;
    ...
};

  然后书上说:

首先,你或许会想设计Widget使它得以拥有derived classes,但同时你可能会想阻止derived classes重新定义onTick。如果Widget继承自Timer,上面的想法就不可能实现,即使是private继承也不可能。

  首先,这里说的是重新定义。重定义函数要求基类中的对应函数是非虚的,而给出的代码是虚的。如果说的是重写,我们不想基类中的对应函数被重写就不要讲它声明为virtual不就可以了吗,书上到底想要说的是一个什么问题???

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

伴梦长久 2022-09-11 15:44:06

This is just what we’re looking for. A Timer object can be configured to tick with whatever frequency we need, and on each tick, it calls a virtual function. We can redefine that virtual function so that it examines the current state of the Widget world. Perfect!

根据前文对Timer的描述,可以得知onTick必须是虚函数,因为Timer的派生类需要通过重写onTick来改变这个函数的行为(比方说定义新的Timer时)。redefine是重写的意思。

现在Widget需要一个Timer,可能需要重写Timer的onTick函数来满足自己的需求,同时从Widget派生出来的类要无法改变onTick的行为。

让Widget“拥有”一个onTick有两种方案:继承或聚合。

如果采用Widget继承Timer,onTick可被重写;但如此的话,Widget的派生类也都可以重写onTick,因为Widget继承了Timer,且onTick在Timer里被声明为虚函数。(声明在非直接基类里的虚函数也可以被重写,派生类无法取消在基类中声明的虚函数,一组虚函数只需要在基类声明为virtual即可)

而聚合则能避免继承所带来的这个问题:Widget聚合一个Timer的派生类,根据需求在派生类里重写onTick。如书中给出的例子(WidgetTimer中的onTick声明时的virtual是多余的):

class Widget {
 private:
  class WidgetTimer: public Timer {
   public:
     virtual void onTick() const;
     ...
  };
  WidgetTimer timer;
  ...
};

非相关:

虽然无法取消虚函数,但是可以利用域的嵌套关系隐藏基类的虚函数,比方说定义一个同名的可调用对象。这样一些特定的调用会直接找到这个对象,而不是虚成员函数。但依然无法阻止重写

#include <cassert>

struct Timer {
  virtual void onTick() {}
};

struct Widget : Timer {
  struct Callable {
    void operator()() {}
  } onTick;
  
  void bar() {
    this->onTick();
    onTick();
  }
};

struct Derived : Widget {
  void onTick() {
    assert(false);
  }
};

int main() {
  Derived obj;
  static_cast<Widget &>(obj).onTick();
  static_cast<Widget &>(obj).bar();
  
  static_cast<Timer &>(obj).onTick(); // assert failed
  return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文