假设我将侦听器接口定义为:
class Listener
{
public:
virtual void doSomething(void) = 0;
}
和从我的界面公开继承的抽象类:
class AbstractBase: public Listener
{
public:
virtual void doSomething(void){ ...do something }
}
我的派生类使用受保护的继承继承自AbstractBase
:
class Derived: protected AbstractBase
{
public:
Derived(Caller &c){ c.register(this); }
}
My 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继承它的实现?
答案 0 :(得分:3)
访问控制基于静态类型,而不是动态类型。因此,由于您的Caller
使用指向Listener
的指针,因此它可以使用Listener
类定义的接口。如果它收到指向Derived
的指针,它仍然基本上将其视为Listener
,因此从其观点来看,doSomething()
是公共的,无论派生类可能做什么。
顺便说一句,我对使用protected
继承有了一些想法。我还没有看到或听到关于它的真正含义的合理解释或者对何时或为什么要使用它的合理解释。在D& E中,Bjarne说(§13.9):
受保护的基类首先在ARM中描述,并在版本2.1中提供。回想起来,我认为
protected
是一个案例,其中“好的论据”和时尚克服了我更好的判断和我接受新功能的规则。
答案 1 :(得分:1)
将其视为一组对象的接口。它在派生接口中受到保护,而不是在侦听器接口中受到保护。因此,任何使用侦听器接口的人都可以访问任何使用派生的人将其视为受保护。基本上它是基于声明类型的编译时检查。
答案 2 :(得分:0)
在编译时检查访问限定符,而不是在运行时。如果raiseEvent()
有一个对象且它知道它是Listener
类型的,那么它就可以在其上调用doSomething()
- 这是每个Listener
的一部分公共接口。
如果某个方法会收到指向Derived
对象的指针,那么通过受保护的继承,不会知道该对象是从AbstractBase继承并因此支持raiseEvent()
- 然后这个电话是非法的。
答案 3 :(得分:0)
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.
}
};
答案 4 :(得分:0)
您将Derived
的实例注册为听众并不重要:Caller::raiseEvent
操纵Listener
方法公开的doSomething
。
被保护的继承关系被绕过'只要Derived
在其构造函数中Listener
上将Caller
注册为Listener
,他就会公开他是doSomething
的事实,因此{{1}}公共方法。