<p>函数指针声明必须严格匹配目标函数签名,包括返回类型、参数类型、个数、const 限定符和调用约定;声明为 int ()(int, double),可赋值为 &func 或 func,调用用 pf(42, 3.14) 或 (pf)(42, 3.14);回调即传入并延迟调用函数指针,C 风格需靠 void* 传参维持上下文,C++11 后推荐 std::function + lambda,但需注意生命周期、空状态及调用开销。</p>

函数指针的声明语法必须匹配目标函数签名
定义函数指针不是随便写个 void* 或 auto* 就行,它必须和要指向的函数在返回类型、参数个数、参数类型、const 限定符甚至调用约定(如 __stdcall)上完全一致。否则编译失败或运行时崩溃。
常见错误是漏掉参数名(其实可省略),但绝不能省略类型;或者把 int (*)() 错写成 int *()(后者是函数返回指针)。
-
int func(int, double);对应的指针类型是int (*)(int, double) - 声明变量:
int (*pf)(int, double) = &func;或直接int (*pf)(int, double) = func;(函数名自动转地址) - 调用方式:
int res = pf(42, 3.14);或int res = (*pf)(42, 3.14);(两种等价)
回调函数本质就是传入函数指针并后期调用
C++ 中没有“回调类型”关键字,回调就是把函数指针(或可调用对象)作为参数传给另一个函数,由后者在合适时机调用它。关键在于:谁负责生命周期?谁保证指针有效?
典型场景是异步 I/O、GUI 事件、STL 算法(如 std::sort 的比较器)或 C 风格库(如 qsort)的集成。
立即学习“C++免费学习笔记(深入)”;
- 纯 C 风格回调只能接受函数指针,不支持捕获局部变量 → 必须用全局/静态变量或
void*传参中转 - C++11 后推荐用
std::function+ lambda,但要注意:lambda 若捕获局部变量且被存储到 long-lived 对象中,必须确保被捕获对象的生命周期长于回调执行时间 - 示例(C 风格):
void register_callback(void (*cb)(int), void* user_data);—— 这里user_data是唯一能带上下文的方式
std::function 比裸函数指针更安全但有开销
std::function 是类型擦除容器,能装函数指针、lambda、绑定表达式、成员函数指针等,接口统一,但每次调用有间接跳转成本,且可能触发堆分配(对小闭包会优化掉)。
它解决了函数指针无法捕获环境的问题,但引入了新的隐患:空状态(std::function 可为空)、异常传播(调用空 std::function 抛 std::bad_function_call)。
- 声明:
std::function<void const std::string> cb;</void> - 赋值 lambda(捕获局部变量):
int x = 10; cb = [x](int a, const std::string& s) { std::cout - 调用前建议检查:
if (cb) cb(1, "done"); - 若性能敏感(如高频回调),优先考虑裸函数指针 + 显式上下文参数,而非
std::function
成员函数指针不能直接当回调传给 C 接口
普通成员函数有隐式 this 参数,其类型是 Ret (Class::*)(Args...),和普通函数指针 Ret (*)(Args...) 类型不兼容,也不能直接转换。这是最常卡住初学者的点。
解决方法只有三种:静态成员函数、全局包装函数、或把对象指针塞进 void* 再用静态函数转发。
- 错误写法:
&MyClass::on_event传给要求void (*)()的 C API → 编译失败 - 可行方案 1(静态):
static void wrapper(void* obj) { static_cast<myclass>(obj)->on_event(); }</myclass>,再传wrapper和this - 可行方案 2(C++17
std::bind不适用 C 接口,仅用于std::function) - 现代替代:用
std::function封装成员函数(std::bind(&MyClass::on_event, this)或[this]{ on_event(); }),但前提是接收方支持std::function
实际项目里,裸函数指针 + void* 上下文仍是跨语言/嵌入式/高性能场景的主力;而 std::function 更适合内部模块解耦。别在回调里做耗时操作,也别在回调中随意 delete this —— 这些细节比语法更容易引发崩溃。








