今天来简单的看看C++的回调函数
为什么需要回调函数
为什么需要回调函数?
- 程序中有些逻辑无法提前写死(比如事件触发、异步操作)。
- 希望把“做什么”和“什么时候做”分离开。
基础知识
定义:把一个函数作为参数传递给另一个函数,在合适的时机调用。
本质:控制反转(Inversion of Control, IoC),把做什么交给用户。
常见场景:
- 事件驱动(按钮点击、信号触发)。
- 算法参数化(排序比较函数)。
- 异步编程(网络、I/O)。
语法
使用函数指针
1 2 3 4 5 6 7 8 9 10 11
| void callback(int result) { std::cout << "Callback result: " << result << std::endl; }
void process(int x, void (*cb)(int)) { cb(x * 2); }
int main() { process(5, callback); }
|
使用仿函数(函数对象)
1 2 3 4 5 6 7 8 9
| struct Callback { void operator()(int result) const { std::cout << "Callback result: " << result << std::endl; } };
void process(int x, Callback cb) { cb(x * 2); }
|
使用functional
1 2 3 4 5 6 7 8 9
| #include <functional>
void process(int x, std::function<void(int)> cb) { cb(x * 2); }
int main() { process(5, [](int v){ std::cout << "Result: " << v << std::endl; }); }
|
使用Lambda表达式
1
| process(5, [](int v){ std::cout << "Lambda result: " << v << std::endl; });
|
典型应用场景
- 排序自定义规则
1
| std::sort(v.begin(), v.end(), [](int a, int b){ return a > b; });
|
- 事件驱动(GUI 框架中的按钮点击)
1
| button.onClick([](){ std::cout << "Button clicked!" << std::endl; });
|
- 异步操作(如网络 I/O)
1 2 3
| asyncRead("file.txt", [](std::string data){ std::cout << "Read content: " << data << std::endl; });
|
回调函数的优点
- 解耦:模块之间减少依赖。
- 灵活性:调用者可以自由指定逻辑。
- 代码复用:相同框架代码可以复用在不同业务逻辑上。
- 支持异步、事件驱动编程。
回调函数的缺点
- 可读性问题:回调层层嵌套时(Callback Hell)会让逻辑难以跟踪。
- 调试困难:执行时机由外部决定,不容易排查问题。
- 生命周期管理复杂:特别是在多线程和异步场景中,可能导致悬空指针或资源未释放。