C++ 第 29 课:回调函数 Callback

上一课练习答案

第 1 题

复制代码
auto f = []()
{
    std::cout << "Hi";
};

这里只是:

定义了 lambda。

还没有执行。

必须:

复制代码
f();

才执行。


第 2 题

复制代码
int x = 10;

auto f = [x]()
{
    std::cout << x;
};

x = 20;

f();

通常输出:

复制代码
10

因为:

复制代码
[x]

是按值捕获,相当于保存了当时 x = 10 的一份副本。


第 3 题

复制代码
int x = 10;

auto f = [&x]()
{
    x = 50;
};

f();

最后:

复制代码
x = 50

因为:

复制代码
[&x]

是按引用捕获。


第 4 题

复制代码
[this]()
{
    speed = 100;
}

[this] 表示:

这个 lambda 可以访问当前对象的成员。

所以:

复制代码
speed = 100;

实际上是在操作当前对象自己的 speed。


第 5 题

复制代码
msg->data

如果 msg 是智能指针,就是:

通过 msg 找到它管理的消息对象,然后访问这个消息对象里的 data 成员。


第 29 课:回调函数 Callback

这一课是我们真正开始进入 ROS2 思维的关键课。

先记一句:

回调函数 = 现在把函数交出去,将来某件事情发生时,由系统替你调用。

它和普通函数最大的区别不是"函数长得不一样",而是:

谁负责调用它。


1. 普通函数

例如:

复制代码
void hello()
{
    std::cout << "Hello";
}

你必须主动:

复制代码
hello();

流程:

复制代码
你的代码
↓
主动调用 hello()
↓
hello 执行

2. 回调不是你主动调用

假设我们有:

复制代码
void onMessage(int data)
{
    std::cout << data;
}

普通情况下你可以:

复制代码
onMessage(10);

但是在回调模式里,我们希望:

把 onMessage 告诉系统。

然后系统说:

"好,以后消息来了我帮你调用。"

流程变成:

复制代码
你把函数交给系统
↓
程序继续运行
↓
消息到达
↓
系统自动调用这个函数

这就是 callback。


3. 用生活例子理解

你去餐厅排队。

服务员说:

"留下电话号码,有位置了我联系你。"

你不是站在那里不停问:

复制代码
有位置了吗?
有位置了吗?
有位置了吗?

而是:

复制代码
把联系方式交出去
↓
等待
↓
条件满足
↓
别人主动找你

回调就是这种思想。


4. 为什么 ROS2 特别需要回调?

因为机器人程序大量事件不是你能预测准确时间的。

例如:

复制代码
相机图像什么时候到?
JointState什么时候来?
激光雷达什么时候更新?
VLA Action什么时候生成?
Service请求什么时候来?

你不能写:

复制代码
if (现在消息来了)

然后一直傻等。

ROS2 更喜欢:

消息到达时自动执行你的函数。


5. ROS2 subscriber 的核心逻辑

比如订阅:

复制代码
/joint_states

可以理解成:

复制代码
程序启动
↓
创建 subscriber
↓
告诉 ROS2:
"收到 JointState 后执行 callback"
↓
继续等待
↓
JointState 到达
↓
ROS2 自动调用 callback(msg)

这就是 subscriber 的灵魂。


6. 为什么 lambda 很适合 callback?

例如:

复制代码
auto callback = [](int data)
{
    std::cout << data;
};

这个 lambda 本身就是:

一段可以被别人以后调用的功能。

所以可以想象:

复制代码
registerCallback(callback);

意思:

把 callback 交给系统。

以后系统内部可以:

复制代码
callback(100);

7. 一个最简单的模拟回调

先不用 ROS2:

复制代码
#include <iostream>

void runCallback(void (*callback)(int))
{
    callback(100);
}

这里出现一个新的东西:

复制代码
void (*callback)(int)

它是:

函数指针

先不要深入语法。

你只需要理解:

runCallback() 接收的不是普通数字,而是"一个函数"。


然后:

复制代码
void printData(int data)
{
    std::cout << data << std::endl;
}

调用:

复制代码
runCallback(printData);

执行过程:

复制代码
把 printData 函数交给 runCallback
↓
runCallback 内部决定什么时候调用
↓
callback(100)
↓
实际上执行 printData(100)

输出:

复制代码
100

8. 重点不是函数指针语法

对你现在最重要的是理解:

复制代码
函数也可以像"数据"一样交给另一个系统

然后:

由对方在未来合适的时候调用。

ROS2 正是大量使用这种模式。


9. ROS2 风格

以后你会看到类似:

复制代码
[this](const Msg::SharedPtr msg)
{
    current_angle = msg->angle;
}

这个 lambda 就可以作为:

subscriber callback。

意思:

ROS2 收到消息后,执行这段代码。


10. 放到 VLA 里

未来你真正可能搭这样的链路:

复制代码
VLA模型
↓
产生动作
↓
发布 ROS2 Action Topic
↓
机器人控制节点订阅
↓
Action 消息到达
↓
callback 自动执行
↓
解析 msg
↓
生成关节目标
↓
发送给仿真/真机

所以你现在学习 callback,不是在学一个孤立的 C++ 语法。

它直接对应:

VLA 输出怎样进入 ROS2 机器人控制系统。


本课先记住三个角色

以后看到 ROS2 subscriber 时,先找:

复制代码
1. 谁在监听?
   subscriber

2. 监听什么?
   topic / message

3. 消息来了做什么?
   callback

这是一个非常重要的 ROS2 阅读框架。


本课小练习

1

普通函数:

复制代码
hello();

是谁主动调用?


2

回调函数最大的特点是什么?


3

如果一个 ROS2 subscriber 订阅:

复制代码
/joint_states

那么 callback 一般在什么时候执行?


4

为什么 lambda 很适合用作 callback?


5

VLA 发布了一个新的动作消息后,控制节点的 subscriber callback 最可能负责做什么?

相关推荐
笨笨饿1 小时前
140_AI新手村MCP与Skills是干嘛的
开发语言·人工智能·python·stm32·单片机·嵌入式硬件·物联网
Godspeed Zhao1 小时前
现代智能雷达技术14——算法(0)
线性代数·算法·雷达
YOLO数据集集合2 小时前
EvoAgent:面向PR研发治理的自进化Multi-Agent Harness系统
java·开发语言·目标检测·agent·自进化
kaixin_learn_qt_ing2 小时前
C++多态实现原理
c++
可乐ea2 小时前
Agent 模型分级升级路由:用置信度阈值把贵模型省下来
人工智能·算法·机器学习·结构化输出·置信度路由·模型升级路由·大模型成本优化
无名猿2 小时前
unique_ptr 完全指南:独占所有权与零开销
c++·性能优化·内存管理·标准库·现代c++
笑鸿的学习笔记2 小时前
C++笔记之大块顺序写
java·c++·笔记
Navigator_Z2 小时前
LeetCode //C - 1278. Palindrome Partitioning III
c语言·算法·leetcode
Godspeed Zhao2 小时前
现代智能雷达技术18——算法(4)
算法