【问题标题】:Writing a very simple event class in C++用 C++ 编写一个非常简单的事件类
【发布时间】:2014-01-31 15:31:54
【问题描述】:

我正在尝试用 C++ 编写一个非常简单的事件或消息类。我希望事件保存发生时间和特定于事件类型的一些数据。 我目前拥有的是以下内容

class EventData{ public: virtual ~EventData(void) = 0; }; struct Event{ Event(EventType type, Time time, EventData *data = nullptr): type_(type), time_(time), data_(data) {} ~Event(void){ if(data_) delete data_; } //Disable copying, event memory will be managed by EventManager Event(const Event& other) = delete; const EventType type_; const Time time_; const EventData *data_; };

在我的主循环中,我有这样的东西,

bool running = true; while(running){ const Event* nextEvent = evtManager.getNextEvent(); switch(nextEvent.type_){ case EVT_A: const EventAData* data = static_cast&ltEventAData*&gt(nextEvent.data_); //do stuff break; } ... case EVT_END: running = false; break; } }

那么问题是是否有更有效的方法来做到这一点,即使用模板。另一个问题是有人可能不小心给出了错误的EventTypeEventData 对,在这种情况下static_cast 将失败。

我应该注意,我希望这个Event 类尽可能快,尤其是对time_ 成员变量的访问。

【问题讨论】:

标签: c++ templates events


【解决方案1】:
  1. There is no need to check whether the pointer is null before deleting it.
  2. 您正在尝试执行类型擦除,然后根据您刚刚擦除的类型执行不同的任务。这是低效的。改为查看 boost::variant,它似乎正是您所需要的。
  3. 如果你坚持使用这种方法,你应该使用typeid来区分类型。

【讨论】:

  • 1.谢谢你让我知道。出于某种原因,我总是认为它会引发运行时错误。 2. 是的,我花了一些时间来理解 boost::variant 是如何工作的,但它似乎以一种更高效、更紧凑的方式执行了我正在做的事情。因此,您的建议确实正是我要问的。一个缺点是您必须事先知道所有事件类型,即如果有人想添加新的事件类型,他必须修改核心源。 3.你是说type_info吗?我不明白为什么这比使用散列字符串更好。
  • 我认为您使用“虚拟数据类”的方法从根本上与运行时事件类型创建不兼容。您是使用 type_ 作为判别式的静态转换数据,并且您希望保留创建新 EventType 的能力。您似乎错过了这样一个事实,即您的 EventData 是静态创建的,即使您能够动态创建新类型,这也不适用于您的数据持有者。在 type_info 注释上 - 数据类的 RTTI 由编译器本身应用,不会出错。你不应该担心它的速度,除非你已经测量过了。
  • 抱歉,我从来没有指“运行时”事件类型创建。 boost::variant<...> data_ 只需要对所有事件类型进行硬编码。如果有人想定义他自己的数据类型,他必须将它插入到变体中,这也意味着您必须重新编译使用该变体的所有内容。将类型作为散列字符串,意味着您可以在此编译单元之外定义数据类型。
  • 对,我误会你了。另一方面 - 如果您在变体上使用 typedef 并在整个应用程序中使用它,则更改允许的类型比使用散列字符串和切换转换更安全。 Boost::variant 使用访问者,并且需要为每个变体类型重载 operator(),因此添加新类型将导致您更新所有访问者,从而避免您记住进行切换转换的每个位置。跨度>
【解决方案2】:

您可以使用继承来代替模板。

Event 可以是具有一个成员变量_time 的抽象基类。使用简单的访问器方法(可能是内联的)或使 _time 成为公共成员变量,访问 _time 成员变量将很快。使用访问器方法几乎总是更好。访问速度非常快,并且访问器将允许您在以后需要时灵活地更改时间的内部表示。

您示例中的 EventType 枚举确实描述了 Event 的所有可能子类。如果您要为 EventType 中的每个条目创建抽象基类 Event 的具体子类,那么您真的不需要 EventType 枚举。子类标识事件类型。所有子类的构造函数都可以是唯一的,每一个都采用不同的参数。每个子类都可以为其成员变量提供访问器。这些额外的构造函数参数和访问器可以替换 EventData 类。

最后,您的主循环有两种选择。首先,您可以多态地对待当前在每个 case 语句中完成的处理。 Event 基类可以有一个名为“processEvent()”的纯虚函数,每个子类都可以覆盖它。如果可能的话,你可以用一个简单的函数调用来替换 switch 语句:

nextEvent->processEvent();

其次,如果这不可行,您可以使用 RTTI 将 nextEvent 变量从 Event 指针向下转换为相应子类的指针。然后,您可以调用特定于子类的访问器来执行类似于您当前正在执行的处理。

【讨论】:

  • 这就是我用于我的游戏引擎的;我有一个抽象类,它有一个方法 getType,它将事件的类型作为散列字符串返回。我认为 RTTI 太慢了,这就是我存储类型以便我可以使用 static_cast 的原因。在事件本身中包含 processEvent 逻辑有点麻烦,因为我必须将大量状态传递给事件。最后,对于主循环,我将只使用信号。
猜你喜欢
  • 2016-03-17
  • 1970-01-01
  • 2013-05-13
  • 2014-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-09
相关资源
最近更新 更多