用于传递具有继承层次结构的事件的C ++系统

时间:2012-02-05 18:40:05

标签: c++ events inheritance

我目前正在为C ++中的游戏编写事件传递系统,我认为如果事件以逻辑方式相互继承,那将会很有用。

这意味着我可以例如引发类型为NukeExplosion的事件,该事件派生自Explosion(可能来自空基类Event)并且它将被传递给所有类型为NukeExplosion的事件的听众,以及更通用的Explosion

到目前为止,我能够提出两种可能的解决方案:

  • 针对同一事件类型的每组侦听器对事件执行dynamic_cast。如果成功,我可以将事件传递给集合中的所有侦听器。
  • 为每个事件类型添加一段代码,再次引发事件,但使用更通用的类型。然后,使用typeid运算符的结果和侦听器映射将事件传递给侦听器。

我真的不喜欢第二个选项,因为它容易出错,并且要求我在每个事件类中编写几乎相同的代码。

第一个选项的问题是它可能需要做很多dynamic_cast,我想避免这种情况。

那么,有没有其他方法我没有考虑到accont,或者是我能做的第一个选择?或者我应该完全放弃事件的继承?

2 个答案:

答案 0 :(得分:1)

我几乎就是这个问题来到这里的。问题基本上是,即使handle (ExplosionEvent *e)的动态类型为{{1},C ++也不会让像e这样的函数接受带有静态类型Event *的参数e。 }。这将是一个很好的功能,但我不太确定还有什么必须改变语言。

访客模式是我能想到的最干净的解决方案。缺点是它很冗长,而且可能不比ExplosionEvent *便宜。

Main.hpp:

dynamic_cast<>

Event.hpp:

#include <iostream>

class Event;
class Handler;

#include "Event.hpp"
#include "Handler.hpp"

Event.cpp:

#ifndef EVENT_H
#define EVENT_H

class Event 
{
public:
  virtual void accept (Handler *handler) { }
};

class ExplosionEvent : public Event 
{
  void accept (Handler *handler);
};

#endif // !EVENT_H

Handler.hpp:

#include "Main.hpp"

void 
ExplosionEvent::accept (Handler *handler) 
{ 
  handler->handleExplosion (this);
}

Handler.cpp:

#ifndef HANDLER_H
#define HANDLER_H

class Handler
{
public:
  void handle (Event *event) { event->accept (this); }
  virtual void handleExplosion (ExplosionEvent *explosionEvent) { }
};

class ExplosionHandler : public Handler
{
  void handleExplosion (ExplosionEvent *explosionEvent);
};

#endif // !HANDLER_H

Main.cpp的:

#include "Main.hpp"

void
ExplosionHandler::handleExplosion (ExplosionEvent *explosionEvent)
{
  std::cout << "BOOM!" << std::endl;
}

编译并运行:

#include "Main.hpp"

int
main (int argc, char *args)
{
  Event *event = new ExplosionEvent;
  Handler *handler = new ExplosionHandler;
  handler->handle (event);
}

答案 1 :(得分:0)

为每个侦听器执行动态转换对于大量侦听器来说会非常昂贵,因此您可能不得不为侦听器实现一个typeid映射。

我的听众会有一个HandleEvent,它会占用Event个对象。此方法将(使用static_cast)基本事件转换为它期望的事件类型(它需要信任事件注册机制的事件调度程序)。

我还会在Event类中实现一个方法,该方法会返回一个新的基本事件(如果有效),因为您可能不想发送基类Event。这可以用宏来完成,但我觉得它也可以用模板方法完成,虽然我还没能使它工作。然后,调度程序将在调用事件处理程序之前获取该基本事件,然后调用处理程序以获取基本事件。