我正在写一个Window类,该类传播不同类型的事件,列在

enum Event {WINDOW_ClOSE=0x1, WINDOW_REDRAW=0x2, MOUSE_MOVE=0x4, ...};


到已通过窗口注册以进行通知的对象。对于每种类型的事件,我都有一个抽象类,任何对象都必须扩展该抽象类以允许进行通知。为了对MOUSE_MOVE事件做出反应,我的对象将从MouseMoveListener继承,该对象具有process_mouse_move_event()方法,该方法由Window调用。可以通过扩展多个这些类来合并侦听许多事件,这些类均继承自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并为每种类型的事件使用单独的Window::register来避免这种情况,但是我认为这会不必要地炸毁我的界面(尤其是因为register以外的方法可能会随同样的问题)。所以我想我正在寻找答案要么说:


“您可以继续这样做,因为...”或
“无论如何都要引入单独的Window::register方法,因为...”或者当然
“您做错了所有事情,应该……”。


编辑:

从Igors注释的链接中:如果EventListener中至少有一个虚拟成员(例如,虚拟析构函数),则上述操作仅适用于该类,因此该类在技术上并不完全为空。

编辑2:

我过早接受n.m.的解决方案,将其作为“我做错了所有事情”之一。但是,它是第二种类型。即使我可以多态调用EventListener->register(Window&)Window也需要实现一个高度冗余的接口(就已声明的方法而言),该接口允许EventListeners注册进行选择性通知。这等效于上述我的替代解决方案,只是没有充分的理由附加了EventListener类。总之,规范的答案似乎是:

不要做dynamic_cast +空基类只是为了避免声明许多类似的函数,否则在以后维护代码时会伤害您。编写许多功能。

编辑3:

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

最佳答案

object->registerWindow (this, EventTypes);


当然,您需要为所有registerWindow继承人实现EventListener。让他们检查与他们相关的事件类型。

更新

如果这意味着您需要重新设计代码,则需要重新设计代码。为什么会这样呢?因为dynamic_cast不是执行接通类型的正确方法。这不是正确的方法,因为每次在层次结构中添加类时,都需要遍历并可能更新旧代码中的所有动态转换开关。这变得非常混乱,并且很快无法维护,这正是发明虚拟功能的原因。

如果您使用虚拟功能进行接通类型,则每次更改层次结构时,您都不需要执行任何操作。虚拟呼叫机制将负责您的更改。

关于c++ - 在此示例中使用空基类是合理的吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19411053/

10-10 03:55