您好,我是昊天,国内某头部音频公司的C++主程,多年的音视频开发经验,熟悉Qt、FFmpeg、OpenGL。
当年我不想学习回调函数,所以转向学习Qt,因为Qt有信号槽,不用研究回调函数。可是终究没有躲过去,后边还是认真学习了一遍。 近日群内的小伙伴对回调函数存在部分疑惑,
我便把我了解的关于回到函数的知识整理成文,本文包含:回调函数的概念、C/C++和现代 C++ 中的实现方案、我踩过的坑 三个部分如果你也想加入到群内参与讨论,欢迎点击公众号里的【联系方式】,加我微信,拉你进群。
函数指针与普通指针类似,唯独不同的是,它指向的是函数而不是变量。普通指针可以获取指针的值,而函数指针则可以调用它所指向的函数。
下面的示例展示了如何定义和使用函数指针。
#include <stdio.h>
// 定义四则运算函数
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { return b != 0 ? a / b : 0; }
int main() {
// 定义一个函数指针,指向接受两个int参数并返回int的函数
int (*operation)(int, int);
int x = 20, y = 10;
// 使用函数指针调用加法
operation = add;
printf("加法:%d + %d = %d\n", x, y, operation(x, y));
// 使用函数指针调用减法
operation = subtract;
printf("减法:%d - %d = %d\n", x, y, operation(x, y));
// 使用函数指针调用乘法
operation = multiply;
printf("乘法:%d * %d = %d\n", x, y, operation(x, y));
// 使用函数指针调用除法
operation = divide;
printf("除法:%d / %d = %d\n", x, y, operation(x, y));
return0;
}
/// 输出:
// 加法:20 + 10 = 30
// 减法:20 - 10 = 10
// 乘法:20 * 10 = 200
// 除法:20 / 10 = 2
其中int (*operation)(int, int);
就是函数指针,其可以指向任何接受两个int参数并返回int的函数,所以operation可以指向add、subtract、multiply、divide等函数。
在编程中,同步和异步是两种不同的执行逻辑。它们在处理任务时有着本质的区别。
形容下图
回调函数是异步执行的一种实现方式,它允许将函数作为参数传递给另一个函数,并在特定时机调用该函数。可是如何才能将函数作为一个参数传递呢?
在C/C++中,函数指针是一种常用的通过参数传递函数的方式。 如下便是一个使用函数指针实现回调函数的例子:
#include <stdio.h>
typedef void (*Callback)(int);
// 模拟一个异步操作:执行完毕后调用回调函数
void asyncOperation(int data, Callback cb) {
// 模拟处理过程...
printf("异步操作处理中...\n");
// 操作结束后,调用回调函数传递结果
cb(data);
}
// 回调函数实现:处理异步操作完成后的逻辑
void onComplete(int result) {
printf("异步操作完成,结果为:%d\n", result);
}
int main() {
// 发起异步操作,传入回调函数
asyncOperation(42, onComplete);
return0;
}
/// 输出:
// 异步操作处理中...
// 异步操作完成,结果为:42
在这个示例中,asyncOperation
函数接受一个数据和一个回调函数作为参数。当异步操作完成后,asyncOperation
调用传入的回调函数 onComplete
,并将结果传递给它。
C++11 引入了 std::function
,它是一种通用、多态的函数封装器,可以存储普通函数、lambda 表达式、绑定表达式以及其他函数对象。相比传统函数指针,std::function
提供了更高的灵活性和类型安全性。
#include <iostream>
#include <functional>
// 定义一个简单的运算函数
int add(int a, int b) {
return a + b;
}
// 使用 std::function 定义回调类型
void performOperation(int a, int b, std::function<int(int, int)> op) {
std::cout << "结果为:" << op(a, b) << std::endl;
}
int main() {
// 直接传入普通函数
performOperation(10, 20, add);
// 传入 lambda 表达式
performOperation(10, 20, [](int x, int y) { return x * y; });
return0;
}
以上示例展示了如何利用 std::function
使代码更加灵活,既可以接受普通函数也可以接受 lambda 表达式作为回调函数。
std::future
是 C++11 中引入的一个模板类,用于获取异步操作的结果。它既可以与 std::async
配合使用,也可以与 std::promise
配合使用。 但是 std::future
不同于函数指针或 std::function
,它会主动获取异步操作的结果,获取时如果异步操作尚未完成,会阻塞当前线程,直到异步操作完成并返回结果。
如下是一个使用 std::future
的示例:
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
// 异步任务:执行一段耗时操作,并返回结果
int asyncTask(int a, int b) {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
return a + b;
}
int main() {
// 创建异步任务,并获取 std::future 对象
std::future<int> resultFuture = std::async(std::launch::async, asyncTask, 10, 20);
// 在此期间可以执行其他任务
std::cout << "正在执行其他任务...\n";
// 等待异步任务完成,并获取结果
int result = resultFuture.get();
std::cout << "异步任务结果为:" << result << std::endl;
return0;
}
在这个示例中,std::async
启动了一个异步任务,并返回一个 std::future
对象。主线程在执行其他任务后,通过 get()
方法等待任务完成并获取结果。这种方式使得异步操作的流程更加清晰,避免了复杂的回调嵌套。
由于回调函数通常被应用于异步操作,因此在使用回调函数时,需要注意以下几点:
回调函数是一种在 C++ 中广泛使用的技术,它允许将函数作为参数传递给其他函数,并在适当的时候被调用。回调函数在异步编程、事件驱动编程以及插件式架构中非常有用。本文介绍了回调函数的基本概念、使用方式以及注意事项,并提供了几个示例来帮助理解回调函数的使用。