如何让C++定时器调用类的非静态成员函数
解决方案
要让Timer支持类成员函数回调,同时保持语法贴近std::thread且不使用std::function、std::bind或lambda,可以通过模板成员函数+静态包装器的方式实现,核心思路是把成员函数和对象实例绑定到Timer内部,在定时器线程中调用对应的包装函数执行成员函数。
重构后的Timer类完整代码
#include <thread> #include <time.h> class Timer { // 存储自由函数指针 void (*free_func)(); // 存储成员函数调用所需的对象指针和包装器 void* obj_ptr; void (*member_invoker)(void*); bool running; bool use_member; // 标记当前使用的是成员函数还是自由函数 public: Timer() : free_func(nullptr), obj_ptr(nullptr), member_invoker(nullptr), running(false), use_member(false) {} ~Timer() { stop(); } void start(); void stop(); // 自由函数回调设置 void setCallback(void (*func)()); // 成员函数回调设置(模板版本,支持任意类的无参void成员函数) template <typename T, void (T::*MemFunc)()> void setCallback(T* instance) { use_member = true; obj_ptr = instance; // 绑定对应的静态包装器函数 member_invoker = &wrapper<T, MemFunc>; } private: // 静态包装器:把成员函数调用转换为普通函数调用,接收对象指针作为参数 template <typename T, void (T::*MemFunc)()> static void wrapper(void* obj) { // 将void*转换回原对象类型,调用成员函数 (static_cast<T*>(obj)->*MemFunc)(); } void run(); }; // 设置自由函数回调 void Timer::setCallback(void (*func)()) { use_member = false; free_func = func; obj_ptr = nullptr; member_invoker = nullptr; } // 启动定时器(非阻塞) void Timer::start() { std::thread th(&Timer::run, this); th.detach(); } // 停止定时器 void Timer::stop() { running = false; } // 定时器核心循环:周期性执行回调 void Timer::run() { running = true; const int period_ms = 100; // 固定100ms周期 struct timespec now, then; while (running) { // 记录回调执行前的时间 clock_gettime(CLOCK_MONOTONIC, &then); // 根据类型执行回调 if (use_member) { if (member_invoker && obj_ptr) { member_invoker(obj_ptr); } } else { if (free_func) { free_func(); } } // 记录回调执行后的时间 clock_gettime(CLOCK_MONOTONIC, &now); // 计算剩余睡眠时间(修复原代码中未定义变量和时间计算错误) long long elapsed_nsec = (now.tv_sec - then.tv_sec) * 1000000000LL + (now.tv_nsec - then.tv_nsec); long long total_period_nsec = period_ms * 1000000LL; long long remaining_nsec = total_period_nsec - elapsed_nsec; // 仅当剩余时间为正时才睡眠,避免负时间导致错误 if (remaining_nsec > 0) { struct timespec sleep_time; sleep_time.tv_sec = remaining_nsec / 1000000000LL; sleep_time.tv_nsec = remaining_nsec % 1000000000LL; clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep_time, nullptr); } } } // 示例自由函数 void tick() { // 执行自定义逻辑 } // 示例类及成员函数 class SomeClass { public: void someMember() { // 执行自定义成员函数逻辑 } }; int main () { // 使用自由函数回调 Timer timer1; timer1.setCallback(tick); timer1.start(); // 使用成员函数回调(语法接近std::thread) SomeClass instance; Timer timer2; timer2.setCallback<SomeClass, &SomeClass::someMember>(&instance); timer2.start(); // 保持主线程运行,避免程序退出 while(true) { sleep(1); } }
关键说明
- 模板成员函数:
setCallback的模板版本接收类类型T和成员函数指针MemFunc作为模板参数,同时接收对象实例指针,把这些信息绑定到Timer内部。 - 静态包装器函数:
wrapper是静态模板函数,负责把void*类型的对象指针转换回原类型,并调用指定的成员函数,解决了成员函数必须依赖对象实例才能调用的问题。 - 类型区分:用
use_member标记当前使用的是成员函数还是自由函数,在run循环中分支执行对应的回调逻辑。 - 修复原代码问题:修正了原Timer类中未定义的
ms变量和时间计算错误,确保睡眠时间计算正确。
使用方式
- 自由函数:直接传递函数名,和原用法一致。
- 成员函数:使用
timer.setCallback<类名, &类名::成员函数>(对象指针)的语法,和std::thread传递成员函数的逻辑一致。
内容的提问来源于stack exchange,提问作者Marcell
相关产品推荐
相关产品推荐

