在其他类内部调用接收void*参数的EventManager::AddEventHandler报错问题
问题核心原因
你当前实现的事件管理器仅支持无上下文的普通函数指针,但类的非静态成员函数调用时需要隐含传入类实例的this指针,函数签名和你要求的void(*)(Args...)不匹配,所以无法直接强转为void*传入AddEventHandler。同时你的实现用void*存储函数指针本身存在类型安全问题,传错参数会直接触发内存访问错误。
可行的解决方案
方案1:注册静态成员函数
如果你的回调逻辑不需要访问类的非静态成员,可以直接把类的回调函数声明为static,静态成员函数没有隐含this指针,和普通函数签名一致,可以直接传入:
class MyClass { public: static void OnUpdateCallback(float delta) { // 静态函数内仅可访问类静态成员/全局变量 } }; // 注册方式 EventManager::AddEventHandler(EventType::OnUpdate, (void*)&MyClass::OnUpdateCallback);
方案2:修改事件管理器支持带上下文的回调(推荐)
这是通用性最强的解决方案,修改你的事件管理器存储回调时同时支持上下文绑定,可以兼容lambda、成员函数、普通函数等所有可调用对象:
#pragma once #include <vector> #include <functional> #include <any> namespace EventManager { enum class EventType { OnUpdate, OnDraw, Size }; // 改用std::function存储回调,天然支持上下文捕获 extern std::vector<std::function<void(std::any)>> EventCallbacks[(unsigned int)EventType::Size]; template<typename Func> void AddEventHandler(EventType eventId, Func&& callback) { EventCallbacks[(unsigned int)eventId].emplace_back([cb = std::forward<Func>(callback)](std::any args) { // 根据不同事件类型解析参数,以下为OnUpdate事件示例(参数为float delta) if constexpr (std::is_invocable_v<Func, float>) { cb(std::any_cast<float>(args)); } }); } template <typename... Args> void Trigger(EventType eventId, Args... args) { // 单参数事件可直接传参,多参数可以打包为tuple传递 for (auto& callback : EventCallbacks[(unsigned int)eventId]) { if constexpr (sizeof...(Args) == 1) { callback((args, ...)); } } } // RemoveEventHandler和TriggerProcess接口可按同理修改 }
修改后注册类成员函数可以直接用lambda捕获实例指针:
class MyClass { public: void Init() { EventManager::AddEventHandler(EventType::OnUpdate, [this](float delta) { // 可直接访问类的所有非静态成员 this->Update(delta); }); } void Update(float delta) { // 业务逻辑实现 } };
方案3:临时适配方案(不推荐)
如果你不想修改现有事件管理器的实现,可以用静态变量存储实例指针,通过静态函数做一层转发:
class MyClass { public: static inline MyClass* s_instance = nullptr; static void StaticOnUpdate(float delta) { if (s_instance) s_instance->OnUpdate(delta); } void OnUpdate(float delta) { // 业务逻辑实现 } }; // 注册时先赋值静态实例指针,再传入静态函数 MyClass::s_instance = this; EventManager::AddEventHandler(EventType::OnUpdate, (void*)&MyClass::StaticOnUpdate);
该方案仅支持单类实例,线程不安全,仅适合临时测试使用。
内容的提问来源于stack exchange,提问作者P Sawicki
相关产品推荐
相关产品推荐

