最轻量直接的回调实现是函数指针,需严格匹配签名,建议用using别名声明,调用前必须判空;它零开销但不支持成员函数或有捕获lambda。

怎么用函数指针传回调函数
在 C++ 中,最轻量、最直接的回调实现就是函数指针。它不依赖类或模板,适合简单场景,比如注册日志输出函数、定时器触发逻辑。
关键点:函数指针类型必须严格匹配——参数个数、类型、顺序,以及返回值类型,缺一不可。C++ 不会自动转换或重载解析。
-
void (*callback)(int)只能接收形如void log(int x)的函数,不能传void log(double)或int log(int) - 声明时建议用
using别名提高可读性:using CallbackFunc = void(*)(int); - 调用前务必判空,否则空指针解引用会崩溃:
if (callback) callback(42);
为什么不用 std::function 而选函数指针
函数指针比 std::function 更低开销、无堆分配、无类型擦除,适合嵌入式、高频调用(如音频处理每毫秒回调)或 ABI 稳定要求高的场景。
但代价是灵活性差:无法绑定对象成员函数、lambda(除非无捕获且显式转成函数指针)、或带状态的闭包。
立即学习“C++免费学习笔记(深入)”;
- 成员函数需额外传
this指针,函数指针本身不支持;得改用std::function或 C 风格的「上下文 void*」方案 - 无捕获 lambda 可隐式转为函数指针:
[](int x){ printf("%d\n", x); }能赋给void(*)(int) -
std::function有约 16–32 字节开销,且首次调用可能触发小内存分配
函数指针作为参数的典型写法
把回调函数当参数传入,本质是传递一个地址。常见于初始化、注册、配置类接口中。
using TaskCallback = void(*)(const char*, int);void register_task(TaskCallback cb) { if (cb) { // 保存或立即调用 cb("start", 100); } }
void my_handler(const char* msg, int code) { printf("[%d] %s\n", code, msg); }
// 使用: register_task(my_handler); // ✅ 正确 register_task([](const char*, int){}); // ✅ 无捕获 lambda 也可 register_task(nullptr); // ❌ 必须检查,否则 crash
容易踩的坑:生命周期和线程安全
函数指针本身是值类型,不管理所指函数的生命周期。但若回调指向栈上函数(比如局部 lambda)或已析构对象的成员函数,运行时行为未定义。
- 不要把局部函数(非 static)地址传出去:
void foo() { void local(){}; register_task(local); } ——local在foo返回后就无效 - 全局/静态函数、命名空间作用域函数、无捕获 lambda 是安全的
- 如果回调会在另一线程执行,确保被调函数不访问已销毁的局部变量或非线程安全的全局资源
真正麻烦的从来不是“怎么传”,而是“传进去的东西还活着吗”和“谁负责清理”。函数指针不自带所有权语义,这点必须手动兜底。









