如何实现优于为每个事件类型单独维护vector的C++事件系统
优化方案1:使用std::unordered_map替代multimap消除冗余逻辑与性能开销
你之前的multimap实现存在两个明显问题:
- multimap允许多个相同key的条目,构造函数预插所有事件的逻辑如果后续不小心重复插入,会出现同一个事件对应多份回调列表的bug
- 每次绑定回调、触发事件都要遍历全表,事件数量多了之后性能会大幅下降
关联容器本身支持O(1)(unordered_map)或O(logn)(map)的key查找,完全不需要自行遍历匹配,同时也不需要提前在构造函数里预插所有事件类型,绑定回调时如果对应事件的回调列表不存在会自动创建:
#include <vector> #include <unordered_map> #include <SDL2/SDL.h> // 可扩展为带事件参数的回调,方便业务逻辑获取事件数据 typedef void(*CALLBACK)(const SDL_Event*); enum EventType { WINDOW_RESIZE = SDL_WINDOWEVENT_RESIZED, WINDOW_CLOSE = SDL_QUIT, KEY_DOWN = SDL_KEYDOWN, KEY_UP = SDL_KEYUP, MOUSE_DOWN = SDL_MOUSEBUTTONDOWN, MOUSE_UP = SDL_MOUSEBUTTONUP, MOUSE_MOVED = SDL_MOUSEMOTION, MOUSE_SCROLLED = SDL_MOUSEWHEEL }; class EventManager { public: void bind(CALLBACK f, EventType type) { // 直接索引即可,不存在的key会自动创建空回调列表 events[type].push_back(f); } void update() { SDL_Event event; while (SDL_PollEvent(&event)) { // 直接查找当前事件对应的回调列表,找不到直接跳过 auto it = events.find(static_cast<EventType>(event.type)); if (it != events.end()) { run(it->second, &event); } } } private: void run(const std::vector<CALLBACK>& callbacks, const SDL_Event* event) { for (auto cb : callbacks) { cb(event); } } // 键为事件类型,值为对应事件的回调列表 std::unordered_map<EventType, std::vector<CALLBACK>> events; };
该方案的优势:
- 新增事件类型无需修改EventManager内部代码,仅需扩展EventType枚举即可,无任何冗余逻辑
- 回调查找开销从O(n)降到O(1),性能提升明显
- 无需提前预注册所有事件类型,使用时绑定会自动创建对应条目
优化方案2:支持任意可调用对象的通用版本
如果你的回调需要捕获变量、绑定类成员函数,可以将回调类型替换为std::function,扩展性更强:
#include <functional> using CALLBACK = std::function<void(const SDL_Event*)>; // 绑定类成员函数示例 class MyClass { public: void onWindowResize(const SDL_Event* e) { // 处理窗口大小变化逻辑 } }; // 绑定时用std::bind传递类实例指针 MyClass obj; eventManager.bind(std::bind(&MyClass::onWindowResize, &obj, std::placeholders::_1), WINDOW_RESIZE); // 也可直接绑定lambda表达式 eventManager.bind([](const SDL_Event* e){ printf("窗口大小变化:%d x %d\n", e->window.data1, e->window.data2); }, WINDOW_RESIZE);
内容的提问来源于stack exchange,提问作者Jaan
相关产品推荐
相关产品推荐

