📘 本篇围绕
std::function为核心,深入探讨其在事件回调、接口抽象、函数封装等实战场景中的用法。结合前一天的函数对象与 Lambda 表达式知识,我们从底层原理出发,逐步构建一个轻量级的可插拔回调机制,帮助你理解类型擦除、参数绑定、函数对象封装的完整思路。
🔁 Day 8 回顾:函数对象与 Lambda 的本质与用法
| 知识点 | 核心理解 |
|---|---|
| 函数对象 | 定义 operator() 成员,封装状态 + 可调用行为 |
| Lambda 表达式 | 匿名可调用对象,支持变量捕获、传参、嵌套组合 |
| 混合用法 | 函数对象负责状态逻辑,Lambda 编排任务流程 |
| 应用场景 | 排序、过滤、延迟执行、注册回调、策略接口等 |
📌 回顾思考:如何将这些“可调用对象”在运行期灵活统一管理?如何封装成更易用的接口?答案正是 std::function。
🎯 今日目标:掌握 std::function 的本质、优势与系统性用法
| 关键词 | 学习目标 |
|---|---|
| std::function | 理解其类型擦除原理、构造方式与使用技巧 |
| std::bind / std::invoke | 掌握参数绑定与延迟调用的机制与实用场景 |
| 回调系统 | 搭建一个轻量级事件注册与触发框架 |

✅ 一、std::function:统一所有“可调用对象”的万能容器
🔹 基本用法
#include <functional>
#include <iostream>
std::function<int(int, int)> add;
add = [](int a, int b) { return a + b; };
std::cout << add(3, 4); // 输出 7
📌 能接受以下类型:
- 普通函数指针
- 成员函数(需绑定)
- Lambda(含捕获)
- 函数对象类实例
🔹 核心原理:类型擦除
- C++ 中函数对象类型各异,不能统一
std::function内部通过虚函数 + 堆分配实现“隐藏真实类型”- 用户只需关注“签名”,不必关心传入对象的具体类名
✅ 二、std::bind 与 std::invoke:封装 + 延迟调用工具
🔹 示例:bind 绑定参数
#include <functional>
int mul(int a, int b) { return a * b; }
auto times2 = std::bind(mul, 2, std::placeholders::_1);
std::cout << times2(10); // 输出 20
🔹 示例:invoke 统一调用形式
#include <functional>
#include <iostream>
void greet(const std::string& name) {
std::cout << "Hello, " << name << "!\n";
}
std::invoke(greet, "Alice"); // 等价于 greet("Alice")
📌 典型应用:
- 回调系统中传递已绑定对象函数
- 封装多态回调统一入口调用
✅ 三、实战:设计一个轻量级回调系统
🧩 目标
- 注册回调函数(任意可调用对象)
- 多个回调可独立存在
- 支持触发与取消绑定
🔸 Step 1:定义回调管理器
class EventManager {
public:
using Callback = std::function<void()>;
int addCallback(Callback cb) {
callbacks[++currentId] = cb;
return currentId;
}
void removeCallback(int id) {
callbacks.erase(id);
}
void triggerAll() {
for (auto& [id, cb] : callbacks) {
cb();
}
}
private:
std::map<int, Callback> callbacks;
int currentId = 0;
};
🔸 Step 2:注册回调(函数 / lambda / 对象)
EventManager manager;
manager.addCallback([](){ std::cout << "Callback 1\n"; });
manager.addCallback([](){ std::cout << "Callback 2\n"; });
struct Printer {
void operator()() const { std::cout << "Printer called\n"; }
};
manager.addCallback(Printer{});
manager.triggerAll();
📌 输出:
Callback 1
Callback 2
Printer called
✅ 四、进阶设计:支持带参数回调
🔹 改进支持:
class IntEventManager {
public:
using Callback = std::function<void(int)>;
int add(Callback cb) {
callbacks[++id] = cb;
return id;
}
void trigger(int value) {
for (auto& [_, cb] : callbacks) {
cb(value);
}
}
private:
std::map<int, Callback> callbacks;
int id = 0;
};
🔹 使用:
IntEventManager em;
em.add([](int v) { std::cout << "Value: " << v << std::endl; });
em.trigger(42);
📌 本质:std::function 允许你自由定义签名,只要签名匹配,即可任意组合封装函数对象。
✅ 五、实战应用场景总结
| 场景 | 使用建议 |
|---|---|
| 注册点击按钮事件 | std::function<void()> + lambda |
| 数据到达回调 | std::function<void(Data)> 或封装消息处理结构 |
| 组件通信 | std::function + bind 成员方法 |
| 插件机制 | 提供 std::function 接口注册插件 |
| 批量调度/取消任务 | 结合 id 存储,triggerAll + remove 实现调度控制 |
📚 总结与回顾
std::function提供统一函数封装,支持多种可调用体- 类型擦除机制让它能隐藏 Lambda、函数对象的具体实现
std::bind可提前绑定参数,std::invoke可统一调用方式- 搭建回调系统的关键是:函数封装 + 映射管理 + 安全调用
🔭 Day 10 预告:智能指针 + 生命周期管理
下一期我们将结合回调机制,进入:
std::shared_ptr/std::weak_ptr- 回调引用对象生命周期的正确处理方式
- 避免 dangling reference / callback leak 的模式设计
如果你希望加入异步调用、线程安全等更高级用法,也可以提前告诉我,我会将内容调整为你更关注的实战方向 💡

被折叠的 条评论
为什么被折叠?



