【问题标题】:Is protected inheritance transitive?受保护的继承是可传递的吗?
【发布时间】:2010-11-11 21:16:46
【问题描述】:

假设我有一个监听器接口定义为:

class Listener
{
   public:
      virtual void doSomething(void) = 0;
}

还有一个从我的接口公开继承的抽象类:

class AbstractBase: public Listener
{
   public:
      virtual void doSomething(void){ ...do something }
}

我的派生类使用 protected 继承从我的AbstractBase 继承:

class Derived: protected AbstractBase
{
   public:
      Derived(Caller &c){ c.register(this); }
}

我的 Caller 类通知注册事件的侦听器:

class Caller
{
   public:
      void register(Listener *listenerPtr){...add listenerPtr to some container }
      void raiseEvent(){...loop over registered listeners and call listenerPtr->doSomething();}
}

然后我以下列方式使用这些类:

int main(void)
{
   Caller caller;
   Derived derived(caller); // derived registers with caller in its constructor
   caller.raiseEvent();
}

在上面的代码中,我看到raiseEvent() 被允许调用listener.doSomething()。但是,因为我注册了一个 Derived 的实例作为侦听器,所以 doSomething() 不应该受到保护(因此不能从 Caller 调用),因为它通过受保护的继承从 AbstractBase 继承了它的实现?

【问题讨论】:

  • 大概Listener::doSomething()应该是虚拟的吧?

标签: c++ inheritance


【解决方案1】:

访问控制基于静态类型,而不是动态类型。因此,由于您的Caller 使用指向Listener 的指针,它可以使用Listener 类定义的接口。如果它接收到一个指向Derived 的指针,它基本上仍将其视为Listener,因此从它的角度来看,doSomething() 是公共的,无论派生类可能做什么。

顺便说一句,我对使用protected 继承有一些想法。我还没有看到或听到关于它真正含义的很好解释,或者关于你何时或为什么要使用它的合理解释。在 D&E 中,Bjarne 说(§13.9):

受保护的基类首先在 ARM 中进行了描述,并在 2.1 版中提供。回想起来,我认为protected 是一个“好的论据”和时尚战胜了我更好的判断和接受新功能的规则的案例。

【讨论】:

  • 我认为protected 是一个半生不熟的访问级别。它对某些方法(非纯虚拟,构造函数/复制构造函数/赋值运算符/析构函数)很有用,但我发现它对于继承(如这里所指出的)或属性(难以确保不变量)完全没用。
  • @Matthieu:是的,我想我没有引用它,但 Bjarne 也说他对成员 functionsprotected 很好——它正在扩展它继承和成员数据确实是错误的。
【解决方案2】:

将其视为对象的一组接口。它在派生接口中受到保护,而不是在侦听器接口中。因此,任何使用侦听器接口的人都可以访问任何使用派生的人都会将其视为受保护的。基本上它是基于声明类型的编译时检查。

【讨论】:

    【解决方案3】:

    访问限定符在编译时检查,而不是在运行时检查。如果raiseEvent() 有一个对象并且它知道它的类型是Listener,那么它就可以在它上面调用doSomething()——这是每个Listener 的公共接口的一部分。

    如果一个方法将接收到一个指向Derived 对象的指针,那么通过受保护的继承,它将知道该对象继承自 AbstractBase 并因此支持raiseEvent() - 然后调用将是非法。

    【讨论】:

      【解决方案4】:
      class Derived: protected AbstractBase
      {
      public:
          Derived(Caller &c)
          {
                  // Invokes `Caller::Register(Listener*)`. Cast to `Listener*` is
                  // allowed because inside `Derived` member functions, `Listener`
                  // is a visible base class. 
              c.register(this);
          }
      };
      
      class Caller
      {
      public:
          void register(Listener *listenerPtr);
          void raiseEvent()
          {
              // Because type of pointers is `Listener*` and not `Derived*`,
              // and because `Listener::doSomething()` is a *public*, member
              // function, it is accessile in this method.
          }
      };
      

      【讨论】:

        【解决方案5】:

        您将Derived 的实例注册为侦听器这一事实并不重要:Caller::raiseEvent 操纵Listener,其中doSomething 方法是公开的。

        一旦Derived 在其构造函数中的Caller 上将自己注册为Listener,受保护的继承关系就会被“绕过”:他暴露了他是Listener 的事实,因此拥有@987654329 @public 方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-04-01
          • 2016-06-12
          • 2015-01-19
          • 2014-06-09
          • 2018-09-03
          • 2012-09-26
          • 2010-09-14
          • 2014-06-29
          相关资源
          最近更新 更多