search

Home  >  Q&A  >  body text

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

我们有这样一个定时器

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不就可以了吗,书上到底想要说的是一个什么问题???

PHP中文网PHP中文网2803 days ago550

reply all(1)I'll reply

  • 阿神

    阿神2017-04-17 15:34:45

    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!

    According to the previous description of Timer, we can know that onTick must be a virtual function, because the derived class of Timer needs to override onTick to change the behavior of this function (for example, when defining a new Timer). redefine means rewriting.

    Now that Widget needs a Timer, you may need to rewrite the Timer's onTick function to meet your own needs. At the same time, classes derived from Widget must not change the onTick behavior.

    There are two options for letting a Widget "own" an onTick: inheritance or aggregation.

    If Widget inherits Timer, onTick can be overridden; but in this case, Widget's derived classes can also override onTick, because Widget inherits Timer, and onTick is declared as a virtual function in Timer. (Virtual functions declared in indirect base classes can also be overridden. Derived classes cannot cancel virtual functions declared in base classes. A set of virtual functions only needs to be declared as virtual in the base class. )

    Aggregation can avoid this problem caused by inheritance: Widget aggregates a derived class of Timer and overrides onTick in the derived class as required. As the example given in the book (virtual when declaring onTick in WidgetTimer is redundant):

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

    Not relevant:

    Although virtual functions cannot be canceled, you can use the nested relationship of fields to hide the virtual functions of the base class, for example, define a callable object with the same name. In this way, some specific calls will find the object directly instead of the virtual member function. But stillcannot prevent rewriting.

    #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;
    }
    

    reply
    0
  • Cancelreply