前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >回调函数实战解读:从 C/C++ 到现代 C++ 实现方案

回调函数实战解读:从 C/C++ 到现代 C++ 实现方案

作者头像
程序员的园
发布2025-03-17 15:36:54
发布2025-03-17 15:36:54
7000
代码可运行
举报
运行总次数:0
代码可运行

您好,我是昊天,国内某头部音频公司的C++主程,多年的音视频开发经验,熟悉Qt、FFmpeg、OpenGL。

当年我不想学习回调函数,所以转向学习Qt,因为Qt有信号槽,不用研究回调函数。可是终究没有躲过去,后边还是认真学习了一遍。 近日群内的小伙伴对回调函数存在部分疑惑,

我便把我了解的关于回到函数的知识整理成文,本文包含:回调函数的概念、C/C++和现代 C++ 中的实现方案、我踩过的坑 三个部分如果你也想加入到群内参与讨论,欢迎点击公众号里的【联系方式】,加我微信,拉你进群。

1. 函数指针

函数指针与普通指针类似,唯独不同的是,它指向的是函数而不是变量。普通指针可以获取指针的值,而函数指针则可以调用它所指向的函数。

下面的示例展示了如何定义和使用函数指针。

代码语言:javascript
代码运行次数:0
运行
复制
#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等函数。

2. 同步与异步

在编程中,同步和异步是两种不同的执行逻辑。它们在处理任务时有着本质的区别。

  • 同步执行: 任务按照顺序执行,每个任务必须等待前一个任务完成后才能继续。这种顺序执行的特征,也使得同步编程模型相对简单,易于理解和调试
  • 异步执行: 任务可以并发执行,不需要等待前一个任务完成即可继续。当任务完成后,通知调用该任务的任务即可。异步执行由于无需等待,可以显著提高程序的响应速度和吞吐量,但同时也增加了编程的复杂度。

形容下图

3. 回调函数

回调函数是异步执行的一种实现方式,它允许将函数作为参数传递给另一个函数,并在特定时机调用该函数。可是如何才能将函数作为一个参数传递呢?

3.1 函数指针

在C/C++中,函数指针是一种常用的通过参数传递函数的方式。 如下便是一个使用函数指针实现回调函数的例子:

代码语言:javascript
代码运行次数:0
运行
复制
#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,并将结果传递给它。

3.2 std::function

C++11 引入了 std::function,它是一种通用、多态的函数封装器,可以存储普通函数、lambda 表达式、绑定表达式以及其他函数对象。相比传统函数指针,std::function 提供了更高的灵活性和类型安全性。

代码语言:javascript
代码运行次数:0
运行
复制
#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 表达式作为回调函数。

3.3 future

std::future 是 C++11 中引入的一个模板类,用于获取异步操作的结果。它既可以与 std::async 配合使用,也可以与 std::promise 配合使用。 但是 std::future 不同于函数指针或 std::function,它会主动获取异步操作的结果,获取时如果异步操作尚未完成,会阻塞当前线程,直到异步操作完成并返回结果。

如下是一个使用 std::future 的示例:

代码语言:javascript
代码运行次数:0
运行
复制
#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() 方法等待任务完成并获取结果。这种方式使得异步操作的流程更加清晰,避免了复杂的回调嵌套。

4. 实战指南

由于回调函数通常被应用于异步操作,因此在使用回调函数时,需要注意以下几点:

  • 注册回调函数的时机:确保回调函数在异步操作开始之前已经被注册,否则可能会错过异步操作完成的通知。
  • 线程安全:如果回调函数会在多个线程中执行,需要确保回调函数是线程安全的,以避免数据竞争和死锁等问题。
  • 异常处理:在回调函数中处理异常时,需要注意捕获所有可能的异常类型,并确保异常被正确处理或传递。
  • 生命周期管理:确保回调函数的生命周期长于异步操作的生命周期,否则可能会导致回调函数在异步操作完成后被销毁,从而引发未定义行为。窗口对象尤为重要,回调至一个已经被销毁的窗口将会触发未定义行为

5. 总结

回调函数是一种在 C++ 中广泛使用的技术,它允许将函数作为参数传递给其他函数,并在适当的时候被调用。回调函数在异步编程、事件驱动编程以及插件式架构中非常有用。本文介绍了回调函数的基本概念、使用方式以及注意事项,并提供了几个示例来帮助理解回调函数的使用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-03-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员的园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 函数指针
  • 2. 同步与异步
  • 3. 回调函数
    • 3.1 函数指针
    • 3.2 std::function
  • 3.3 future
  • 4. 实战指南
  • 5. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档