回调函数
回调函数
回调函数
C++ 中的 回调函数(Callback Function) 是一种把函数当作参数传递给另一个函数,并在特定时机调用它的机制。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
void onEvent() {
std::cout << "Event triggered!" << std::endl;
}
// 回调接受者
void doSomething(void (*callback)()) {
std::cout << "Doing something...\n";
callback(); // 调用回调
}
int main() {
doSomething(onEvent); // 把函数传进去作为回调
return 0;
}
核心要点
特性 | 描述 |
---|---|
“回调”本质 | 把函数作为参数传递给另一个函数 |
触发时机 | 由被调用函数决定(你控制不了调用时机,但提供了函数) |
使用目的 | 提高灵活性、控制反转(Inversion of Control) |
实现方式 | 函数指针、函数对象、Lambda、std::function |
更现代写法(Lambda + std::function
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <functional>
void doSomething(std::function<void()> callback) {
std::cout << "Doing something...\n";
callback(); // 调用回调
}
int main() {
doSomething([]() {
std::cout << "Lambda callback triggered!" << std::endl;
});
return 0;
}
这里的 std::function<void()>
可以接收:
- 函数指针
- lambda 表达式
- 函数对象(只要重载了
operator()
)
应用场景
- 事件驱动编程:比如 UI 框架中点击按钮触发回调
- 异步编程:线程完成任务后调用回调通知主线程
- 策略模式:将不同策略函数传入算法中动态调用
- 模拟信号与槽:类似 Qt 的 signal/slot 机制
对比不同的实现方式
方法 | 是否支持状态 | 是否可捕获变量 | 是否可用作回调 |
---|---|---|---|
函数指针 | ❌ | ❌ | ✅ |
函数对象 | ✅ | ✅(通过成员) | ✅ |
Lambda(无捕获) | ❌ | ❌ | ✅ |
Lambda(捕获) | ✅ | ✅ | ✅(需要 std::function ) |
std::function | ✅(包装) | ✅ | ✅✅✅ |
为什么普通成员函数不能直接作为回调?
1. 函数调用的本质区别
普通函数(包括全局函数和静态成员函数)在调用时,只有显式传入的参数。
例如:
1 2
void func(int x); func(10); // 只传了一个参数 x = 10
普通成员函数调用时,除了显式传入的参数外,还有一个“隐藏参数” ——
this
指针,它指向调用该函数的对象。换句话说:
1 2 3 4 5 6 7
class MyClass { public: void memberFunc(int x); }; MyClass obj; obj.memberFunc(10); // 实际调用时会隐式传入 obj 的地址作为 this 指针
在底层,这相当于调用:
1
memberFunc(&obj, 10);
this
指针告诉函数它是在哪个对象上操作。
2. 函数指针类型不匹配
普通函数指针类型
1
typedef void (*FuncPtr)(int);
这表示指向普通函数的指针,调用时只传入一个
int
参数。普通成员函数指针类型
1
typedef void (MyClass::*MemFuncPtr)(int);
这是成员函数指针,调用时还需要传入调用的对象实例(即
this
指针)。
3. 回调函数为什么用普通函数指针?
许多库或API(特别是C风格的)期望你传入一个普通函数指针作为回调,调用方式是:
1
callback(42); // 只传入一个参数
但是普通成员函数调用需要:
1
(obj->*memberFunc)(42); // 需要明确的对象实例 obj
这两种调用方式不一样,不能直接互换。
4. 结论
- 普通成员函数隐式带了
this
参数,需要明确对象实例。 但是回调函数调用时,库只知道要传入显式参数,不知道也不能传
this
。“库”是调用你回调函数的那段代码,它不知道你想用哪个对象调用成员函数,因为你只给了它函数指针,它调用时也没有办法传
this
指针。所以你不能直接传普通成员函数当回调,必须用静态函数包一层或者用更高级的方式(如std::function
)。
- 所以普通成员函数不能直接作为普通函数指针类型的回调函数。
5. 解决方案示例
常用的做法是:
- 用静态成员函数或全局函数作为回调,因为它们没有
this
参数; - 如果需要调用普通成员函数,则在静态成员函数里传入对象指针,再调用成员函数。
本文由作者按照 CC BY 4.0 进行授权