news 2026/7/1 2:04:55

C++ 回调函数搞懂指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ 回调函数搞懂指南

文章目录

  • C++ 回调函数完全搞懂指南
    • 一句话说清楚
    • 一、从一个生活例子开始
    • 二、不用回调 vs 用回调
      • 2.1 不用回调:死循环盯着
      • 2.2 用回调:发完请求立刻返回,结果到了自动处理
    • 三、回调的核心三要素
    • 四、我的项目中回调出现的每一处
      • 4.1 HTTP 路由——浏览器访问时触发
      • 4.2 IPC 请求处理器——收到消息时触发
      • 4.3 promise/future——异步等待结果
      • 4.4 串口任务完成 → 批次聚合
      • 4.5 并行查询多模块
    • 五、Qt 信号槽也是回调
    • 六、为什么不用死循环轮询
    • 七、回调的注意事项
      • 7.1 引用捕获的生命周期
      • 7.2 回调里不要做耗时操作
    • 八、常见应用场景汇总
    • 九、一句话记忆法

C++ 回调函数完全搞懂指南

一句话说清楚

回调函数就是你写好的代码,交给别人保管,等事情发生时别人帮你调用。你失去了"什么时候执行"的掌控权,但换来了"不用在那干等"的自由。


一、从一个生活例子开始

你去楼下快递柜寄快递:

正常做法(同步,自己盯着): 你把快递放进柜子 → 站在柜子前面等快递员来取 → 快递员来了 → 你看着他取走 → 回家 问题:你傻站了半小时,什么都没干 回调做法(异步,留个电话): 你把快递放进柜子 → 在系统里留个回调:"快递被取走时发短信通知我" → 立刻回家打游戏 → 半小时后手机响了:"您的快递已被取走" → 你知道完事了 好处:你不用盯着,该干嘛干嘛

你留在系统里的那个"发短信通知我",就是回调函数。


二、不用回调 vs 用回调

2.1 不用回调:死循环盯着

// 查 ODU 设备温度double查温度(){while(true){发查询命令();sleep(10ms);if(设备回复了()){return设备回复的数据;}// 没回复?继续循环等着}}// 调用doubletemp=查温度();// 这行要卡 800ms,期间什么都干不了处理温度(temp);

致命问题:线程被卡住,这 800ms 内不能处理任何其他事情。

2.2 用回调:发完请求立刻返回,结果到了自动处理

// 查 ODU 设备温度(异步)void查温度(std::function<void(double)>回调){发查询命令();把回调存起来;// 不执行,只是记下来}// 这一行立刻返回!不卡!// 另一个线程收到设备回复时:void收到回复(doublevalue){所有的回调(value);// 现在才执行}// 调用查温度([](doubletemp){处理温度(temp);// 这段代码 800ms 后才执行,但调用者不用等});做其他事情();// 这行立刻执行,不卡

三、回调的核心三要素

要素代码生活类比
注册setCallback(函数)给快递公司留电话
存储callback_ = 函数快递公司记下你的号码
触发callback_(参数)包裹到了,快递员打你电话
// 最小例子:就三样东西std::function<void(int)>callback;// ① 准备一个能装函数的变量callback=[](intx){cout<<x;};// ② 把函数装进去(此刻不执行)callback(42);// ③ 调用它(此刻才执行)

四、我的项目中回调出现的每一处

4.1 HTTP 路由——浏览器访问时触发

// webd/api_odu.cppserver.Get("/api/v1/odu/monitor",[&ipc](req,res){// ↑ 这个 lambda 就是回调// 浏览器访问这个路径时,httplib 框架才调用它writeJson(res,200,查询ODU数据());});

4.2 IPC 请求处理器——收到消息时触发

// odud/main.cppipc.setRequestHandler([&manager](constIpcMessage&req){manager.handleRequest(req);});// 回调存进 request_handler_ 变量里// rxLoop 线程收到 request 消息时调用// 触发处:ipc_client.hpp rxLoop()if(msg.type=="request"){request_handler_(msg);// ← 调回调}

4.3 promise/future——异步等待结果

// ipc_client.hpp request()autofuture=promise.get_future();waiters_.emplace(seq,std::move(promise));// 回调 = promise.set_value()send(msg);// 发请求returnfuture.get();// 阻塞等,直到 rxLoop 调回调// 触发处:rxLoop 收到 responseit->second.set_value(msg);// ← 调"回调"(promise 的 set_value)

4.4 串口任务完成 → 批次聚合

// odu_request_manager.cpp// 每个串口任务完成后调 finishBatchField// 最后一个任务调 finishBatchField 时会触发 ipc_.respond() —— 这个就是"最终回调"

4.5 并行查询多模块

// webd/api_odu.cppautoodu=std::async(std::launch::async,[&]{returnrequestData(ipc,"odud","odu.get_monitor",{});});// 开线程,线程里执行回调

五、Qt 信号槽也是回调

我在 Qt 里写的这些,本质都是回调:

// Qt C++connect(button,&QPushButton::clicked,this,&MyClass::onClicked);// onClikcked 就是回调,存进 Qt 元对象系统,鼠标点击时调用// QMLButton{onClicked:{console.log("点了")}// 这个 {} 就是回调,Qt 替你存好了}
Qt 信号槽C++ 原生回调
存储Qt 元对象系统std::function变量
注册connect()/onXxx: {}setHandler(lambda)
触发emit signal()callback_(args)
依赖必须 QObject + moc纯 C++ 标准库,零依赖
一对多❌(要自己实现)
跨线程❌(要自己处理)

Qt 信号槽 = 加强版回调。嵌入式 Linux 没有 Qt,只能用标准库的std::function


六、为什么不用死循环轮询

// 轮询方式while(!消息到了){sleep(1ms);// CPU 空转,浪费电}处理消息();// 回调方式收消息线程{recv();// 阻塞等待,没消息就睡觉,CPU 占用 0%回调(消息);// 有消息才被唤醒}
轮询回调
CPU 占用一直占着0%(阻塞等待)
响应速度取决于轮询间隔来即响应
代码耦合框架和业务粘死框架只管调回调,不管内容

七、回调的注意事项

7.1 引用捕获的生命周期

// ❌ 危险void设置回调(){Manager manager;ipc.setRequestHandler([&manager](msg){// 按引用捕获manager.handleRequest(msg);});}// manager 析构了!但回调还在 → 悬垂引用 → 崩溃// ✅ 安全(本项目做法)intmain(){Manager manager;// manager 在 main 栈上ipc.setRequestHandler([&manager](msg){// 按引用捕获manager.handleRequest(msg);});while(true)sleep(24h);// main 不退出,manager 一直活着}

规则:引用捕获时,被捕获对象必须比回调活得久。

7.2 回调里不要做耗时操作

// ❌ 危险ipc.setRequestHandler([](msg){发串口请求并等800ms();// 卡住 rxLoop 线程!其他消息收不到了});// ✅ 正确(本项目做法)ipc.setRequestHandler([](msg){把任务扔进队列();// 几微秒,立刻返回// 耗时操作在别的线程里做});

八、常见应用场景汇总

场景例子
网络/串口 I/O发请求 → 等回复 → 收到时调回调
UI 事件点按钮 → 调回调
定时器设一个定时器,“5 秒后调这个回调”
异步计算提交任务,“算完了调这个回调”
库/框架扩展框架写好流程,用户填回调(std::sort的比较函数)
中断处理硬件中断 → 调 ISR 回调
多结果聚合每个结果到了调回调,足够数量时触发最终处理

九、一句话记忆法

正常调用 = 你去敲门。回调 = 你留电话号码,别人打给你。

  • 正常:你知道什么时候调,你掌控时机
  • 回调:你写好逻辑交给别人,别人掌控时机

回调不是为了炫技,是为了:

  • 不卡线程(异步)
  • 解耦框架和业务(一份框架代码多处复用)
  • 省 CPU(阻塞等待代替轮询)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/1 2:03:37

2026亚太科技前沿EMBA测评与科学选型指南

一、引言&#xff1a;亚太科技EMBA选型核心痛点随着大中华区企业数字化转型、全球化出海进程加速&#xff0c;科技前沿导向的亚太EMBA成为企业高管、创始人深造的核心选择。当前亚太EMBA市场项目繁杂&#xff0c;涵盖内地传统商科、香港国际化、新加坡跨境办学等多类项目&#…

作者头像 李华
网站建设 2026/7/1 2:03:33

MCP 与 Skills 深度对比:不是谁取代谁,而是各司其职

MCP 与 Skills 深度对比&#xff1a;不是谁取代谁&#xff0c;而是各司其职1. 引言&#xff1a;一场关于“谁更好”的错位争论2. 核心概念&#xff1a;先搞清楚它们各自是什么2.1 MCP&#xff1a;AI 世界的“USB 协议”2.2 Skills&#xff1a;AI 的“岗位操作手册”2.3 定位差异…

作者头像 李华
网站建设 2026/7/1 2:02:24

数论】lcm与质因子计算

二、数学推导核心 等式等价条件 所有 pi两两互质。 原理&#xff1a;若两个数共享质因子&#xff0c;lcm 只会保留一次该质因子&#xff0c;乘积会保留两次&#xff0c;等式不成立。 单个质数分配规则 对任意质数 pr&#xff0c;最多只能出现在一个 pi 中。 定义 (v_pr(x))&a…

作者头像 李华
网站建设 2026/7/1 2:00:12

揭阳普宁电商税务处理公司哪家更专业?创业易成为本土首选

本文含 AI 辅助创作内容&#xff0c;全文经人工梳理修正&#xff0c;内容仅为普宁本土电商财税行业客观科普&#xff0c;不构成个性化税务实操专属建议。 普宁是揭阳核心服装直播电商产业带&#xff0c;抖音、淘宝、拼多多、快手海量店群商家集中&#xff0c;金税四期实现平台订…

作者头像 李华
网站建设 2026/7/1 1:58:12

openGauss JDBC PreparedStatement 参数上限问题

一、报错现象当单条预编译 SQL 中的 ? 占位符数量超过驱动上限时&#xff0c;会抛出如下异常&#xff1a;org.opengauss.util.PSQLException: PreparedStatement can have at most 65,535 parameters. Please consider using arrays, or splitting the query in several ones,…

作者头像 李华