首页 > 解决方案 > 事件系统:类型转换或联合的继承

问题描述

我目前正在为我的游戏引擎开发一个事件系统。我想过两种方法可以实现它:

1.带继承:

class Event
{
    //...
    Type type; // Event type stored using an enum class
}

class KeyEvent : public Event
{
    int keyCode = 0;
    //...
}

2.有工会:

class Event
{
    struct KeyEvent
    {
        int keyCode = 0;
        //...
    }

    Type type; // Event type stored using an enum class

    union {
        KeyEvent keyEvent;
        MouseButtonEvent mouseButtonEvent;
        //...
    }
}

在这两种情况下,您都必须检查Event::Type枚举,以了解发生了哪种事件。

当您使用继承时,您必须强制转换事件以获取它的值(例如强制Event转换KeyPressedEvent以获取关键代码)。所以你必须将原始指针转换为需要的类型,这通常很容易出错。

当您使用联合时,您可以简单地检索已发生的事件。但这也意味着内存中联合的大小总是与其中包含的最大可能类一样大。这意味着可能会浪费大量内存。

现在的问题:

是否有任何论据可以使用一种方式而不是另一种方式?我应该浪费内存还是冒着错误投射的风险?

或者有没有我没有想到的更好的解决方案?

最后,我只想通过例如 aKeyEvent作为Event. 然后我想检查Event::Type使用枚举类。如果类型等于说Event::Type::Key我要获取按键事件的数据。

标签: c++eventsevent-handlinggame-engineunions

解决方案


Event似乎比以行为为中心更以数据为中心,所以我会选择std::variant

using Event = std::variant<KeyEvent, MouseButtonEvent/*, ...*/>;
// or using Event = std::variant<std::unique_ptr<KeyEvent>, std::unique_ptr<MouseButtonEvent>/*, ...*/>;
// In you are concerned to the size... at the price of extra indirection+allocation

那么用法可能类似于

void Print(const KeyEvent& ev)
{
    std::cout << "KeyEvent: " << ev.key << std::endl;
}
void Print(const MouseButtonEvent& ev)
{
    std::cout << "MouseButtonEvent: " << ev.button << std::endl;
}

// ...

void Print_Dispatch(const Event& ev)
{
    std::visit([](const auto& ev) { return Print(ev); }, ev);
}

推荐阅读