C++ 在本例中使用空基类是否合理?

C++ 在本例中使用空基类是否合理?,c++,inheritance,base-class,C++,Inheritance,Base Class,我正在编写一个窗口类,它传播不同类型的事件,在 enum Event {WINDOW_ClOSE=0x1, WINDOW_REDRAW=0x2, MOUSE_MOVE=0x4, ...}; 到已注册为窗口通知的对象。对于每种类型的事件,我都有一个抽象类,任何对象都必须扩展该类才能允许通知。比如说,要对鼠标移动事件做出反应,我的对象将从鼠标移动列表继承,该列表有一个处理鼠标移动事件()方法,该方法由窗口调用。可以通过扩展这些类中的多个来组合侦听多个事件,这些类都继承自EventListener

我正在编写一个
窗口
类,它传播不同类型的事件,在

enum Event {WINDOW_ClOSE=0x1, WINDOW_REDRAW=0x2, MOUSE_MOVE=0x4, ...}; 
到已注册为窗口通知的对象。对于每种类型的事件,我都有一个抽象类,任何对象都必须扩展该类才能允许通知。比如说,要对
鼠标移动
事件做出反应,我的对象将从
鼠标移动列表
继承,该列表有一个
处理鼠标移动事件()
方法,该方法由
窗口
调用。可以通过扩展这些类中的多个来组合侦听多个事件,这些类都继承自
EventListener
基类。要注册一个对象,我会调用

void Window::register(EventListener* object, int EventTypes)
{
    if(EventTypes&WINDOW_CLOSE)
       /* dynamic_cast object to WindowCloseListener*, add to internal list of all
          WindowCloseListeners if the cast works, else raise error */

    if(EventTypes&MOUSE_MOVE)     
       /* dynamic_cast object to MouseMoveListener*, add to internal list of all
          MouseMoveListeners if the cast works, else raise error */

    ...
} 
这很好,但我的抱怨是,
EventListener
完全是空的,我觉得代码很臭。我知道我可以通过完全删除
EventListener
并为每种类型的事件设置一个单独的
窗口::register
来避免这种情况,但我觉得这会不必要地破坏我的界面(特别是因为
register
以外的方法可能会出现同样的问题)。所以我想我在寻找的答案是:

  • “你可以继续这样做,因为……”或者

  • “无论如何,还是要引入单独的
    窗口::register
    方法,因为…”或者当然

  • “你做错了,你应该……”

编辑:

从Igors comment中的链接可以看出:如果
EventListener
中至少有一个虚拟成员(例如一个虚拟析构函数),则我上面所做的工作才有效,因此从技术上讲,该类不是完全空的

编辑2:

我过早地接受了n.m.的解决方案,认为这是一种“我做错了”的解决方案。然而,它是第二种类型。即使我可以多态地调用
EventListener->register(Window&)
Window
也需要实现一个高度冗余的接口(就声明的方法而言),该接口允许
EventListeners
注册选择性通知。这相当于我上面描述的替代解决方案,只是在没有充分理由的情况下额外引入了
EventListener
类。总之,典型的答案似乎是:

不要仅仅为了避免声明许多类似的函数而执行
dynamic\u cast
+空基类,这将在以后维护代码时对您造成伤害。编写许多函数

编辑3:

我找到了一个令我满意的解决方案(使用模板)。它不再使用空基类,也不会出现n.m.指出的维护问题

object->registerWindow (this, EventTypes);
当然,您需要为所有
EventListener
继承人实现
registerWindow
。让他们检查与他们相关的事件类型

更新

如果这意味着您需要重新设计代码,那么您需要重新设计代码。为什么会这样?因为
dynamic\u cast
不是打开类型的正确方法。这不是一种合适的方法,因为每次在层次结构中添加类时,都需要在旧代码中通过动态强制转换来更新所有开关。这会很快变得非常混乱和无法维护,这正是虚拟函数被发明的原因


如果您使用虚拟函数打开类型,每次更改层次结构时,您都必须执行。。。没有什么。虚拟呼叫机制将处理您的更改。

这就是我最后要做的:

template <int EventType> void register_(EventListener<EventType> Listener)
{
    // do stuff with Listener, using more templates
};

等等。这种方法还很好地消除了空基类的需要。

看起来您正在做类似的事情。在这里,作者也引入了一个空基类,只是为了从中获得一些东西。好吧,我真的不知道一个非循环访问者是做什么的,但我会向“继续按你的方式做”致敬,谢谢!好吧,这就是为什么我把“非循环访问者”作为相关文章的可点击链接。当然,你必须知道如何跟踪链接。很抱歉,我之前(已删除)的评论-我仍然不知道非循环访问者到底做了什么,但链接确实很有帮助。你的
事件类型
动态播播
似乎是多余的:你不能简单地
静态播
,因为
事件类型
告诉你类型吗?请注意,
dynamic\u cast
不能保证其运行时有上限……好吧,所以我完全错了。这很好,但您建议完全重新设计我的代码的工作方式(尽管没有太多细节),理由是“这就是他们在OOP 101中所教的”。我将把你的答案翻译成“RTFM”,谢谢。@Roberto:如果你有一个
dynamic\u cast
,并且没有对“虚拟呼叫在这里不起作用,因为…”这一形式的确切解释,那么是的,你做错了,你没有做RTFM,你的设计需要从头开始重建。对不起,我从Igors链接得到,我需要一个基类中的虚拟析构函数。你的答案毫无帮助。当然,你需要在基类中使用一个虚拟析构函数,这几乎是给定的。如果你没有,你会遇到很多麻烦。然而,这与手头的问题是正交的,您的设计仍然需要重新进行。好的,那么请(不要讽刺)您能对我的代码的一般问题更具体一点吗。我的意思是,从某种意义上讲,它不会被破坏,因为它不会编译/做它应该做的事情。
register_mouse_motion_event(...)
register_keyboard_event(...)