将const std::function<void()>作为void*传入SDL_AddTimer时崩溃的问题排查
问题原因
崩溃的核心是你传递给SDL_AddTimer的std::function指针指向了一个已经销毁的对象:
- 在
set函数中,你传入的lambda会被隐式转换为std::function<void()>临时对象,这个对象的生命周期只到set函数执行结束为止。 - 当SDL定时器触发回调时,临时对象早已被销毁,
wrapper里访问的是野指针,必然导致崩溃。 - 另外,你用
const_cast去掉const属性是多余的——std::function<void()>的operator()本身是const成员函数,不需要修改对象就能调用。
解决方案
要确保传递给定时器的std::function对象生命周期覆盖定时器的整个运行周期,最简单的方式是把回调函数存储为timer类的成员变量:
class timer { private: std::function<void()> m_callback; SDL_TimerID m_timer_id = 0; // 用于存储定时器ID,方便后续取消 public: ~timer() { if (m_timer_id != 0) { SDL_RemoveTimer(m_timer_id); } } void set(uint32_t interval, std::function<void()> fn) { // 先移除已有的定时器(如果存在) if (m_timer_id != 0) { SDL_RemoveTimer(m_timer_id); } // 将回调存储为成员变量,延长生命周期 m_callback = std::move(fn); // 传递成员变量的地址给SDL_AddTimer m_timer_id = SDL_AddTimer(interval, wrapper, reinterpret_cast<void*>(&m_callback)); } static uint32_t wrapper(uint32_t interval, void* param) { // 转换回const指针(因为不需要修改回调对象) const auto* fn = static_cast<const std::function<void()>*>(param); (*fn)(); return interval; } };
额外注意事项
- SDL的定时器回调是在独立的线程中执行的,如果后续需要修改或销毁
m_callback,必须加线程同步(比如用互斥锁),避免竞态条件。 - 不要传递临时对象、局部变量的地址给SDL_AddTimer,除非你能保证这些对象在定时器运行期间始终有效。
内容的提问来源于stack exchange,提问作者Rodrigo
相关产品推荐
相关产品推荐

