如何让C++定时器支持绑定任意类型函数(含带参、类成员函数)
C++ 通用定时器改造方案
原有实现的核心问题
- 用固定签名的裸函数指针
typedef void (*callback)(),只能匹配无参全局函数,不支持带参函数、lambda、类成员函数等可调用对象 - lambda捕获存在安全隐患:
[sec, &f]按引用捕获局部参数f,start函数返回后f生命周期结束,线程执行时访问野指针,触发未定义行为 - 依赖POSIX的
sleep函数,跨平台兼容性差
改造方案
核心是用C++11引入的std::function做可调用对象包装,结合可变参数模板+完美转发实现任意类型回调的适配,改造后代码如下:
#include <thread> #include <functional> #include <chrono> class timer { public: virtual ~timer() { if (t_.joinable()) { t_.join(); } } // 可变参数模板,支持任意签名的可调用对象和参数 template<typename Func, typename... Args> void start(int sec, Func&& func, Args&&... args) { if (t_.joinable()) { t_.join(); } // 按值捕获绑定后的可调用对象,避免生命周期问题 // 用std::bind完美转发所有参数,包装成无参可调用对象传给线程 auto task = std::bind(std::forward<Func>(func), std::forward<Args>(args)...); t_ = std::thread([sec, task]() { // 用标准库的sleep_for替代POSIX sleep,跨平台兼容 std::this_thread::sleep_for(std::chrono::seconds(sec)); task(); }); } private: std::thread t_; };
不同场景的使用示例
1. 带参数的全局函数
#include <cstdio> void test_with_param(int a, const char* str) { printf("param a: %d, str: %s\n", a, str); } int main() { timer t; // 直接传入参数即可 t.start(3, test_with_param, 100, "hello timer"); while(1); return 0; }
2. 非静态类成员函数
非静态成员函数需要绑定对应的类实例,有两种写法:
#include <iostream> class TestClass { public: void member_func(int x) { std::cout << "member function called, x: " << x << ", this ptr: " << this << std::endl; } }; int main() { timer t; TestClass obj; // 写法1:用std::bind绑定实例 t.start(2, std::bind(&TestClass::member_func, &obj, 200)); // 写法2:用lambda包装 t.start(3, [&obj]() { obj.member_func(300); }); while(1); return 0; }
3. lambda表达式
int main() { timer t; int num = 400; t.start(1, [num](){ printf("lambda called, captured num: %d\n", num); }); while(1); return 0; }
注意事项
- 如果lambda捕获了局部变量,要确保变量在线程执行回调前不会被销毁,或者改用值捕获
- 定时器析构时会join等待线程执行完,不要在回调里操作已经销毁的对象
- 若需要支持取消定时器、重复触发等功能,可以新增原子变量控制线程执行逻辑
内容的提问来源于stack exchange,提问作者cppdev
相关产品推荐
相关产品推荐

