You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.09 19:09:01