用std::function+std::vector可轻量实现观察者模式,核心是存储回调并遍历通知;需规避悬空指针、迭代器失效及自我注销问题,推荐结合weak_ptr/shared_ptr管理生命周期,比虚函数继承更灵活高效。

用 std::function + std::vector 实现轻量观察者注册表
不需要引入第三方库或复杂基类,C++11 起就能靠标准库搭出可用的观察者模式。核心是把回调抽象为 std::function(或带参数的变体),用 std::vector 存储,注册即 push_back,通知即遍历调用。
常见错误是直接存裸函数指针或 lambda 捕获局部变量,导致调用时悬空。必须确保回调对象生命周期长于观察者容器,或用 std::shared_ptr 管理。
- 推荐签名:
using Callback = std::function,; Event是自定义结构体,比 void* 安全、比模板泛型更易维护 - 注册时检查重复:可加
std::weak_ptr配合std::shared_ptr实现自动去重与自动注销 - 通知时避免迭代器失效:若回调内可能调用
unregister(),需先拷贝回调列表再遍历
如何安全处理观察者在回调中自我注销
这是最常踩的坑:遍历时删除容器元素,引发迭代器失效和未定义行为。不能边 for-range 边 erase。
正确做法是分离“通知”和“清理”。先收集待移除项,或改用索引遍历并倒序删,但更稳妥的是两阶段策略:
立即学习“C++免费学习笔记(深入)”;
- 第一阶段:遍历原始
std::vector<:weak_ptr>>,调用lock()获取有效回调,存入临时std::vector<:function>> - 第二阶段:遍历临时列表执行调用;结束后再扫描原容器,erase 已过期的
weak_ptr - 如果不用智能指针,就要求调用方显式调用
unregister(),并在容器内部用std::list替代std::vector,支持 O(1) 删除
为什么不用虚函数+继承实现?
纯虚基类(如 class Observer { virtual void onEvent(const Event&) = 0; };)看似经典,但在现代 C++ 中往往过度设计:
- 每个具体观察者都要继承、实现虚函数,增加编译依赖和头文件耦合
- 多态调用有虚表开销,虽小但对高频事件(如游戏帧更新)可测出差异
- 无法直接绑定成员函数而不传对象指针,需额外包装(
std::bind或 lambda),反而不如std::function直观 - 不支持无状态函数、静态函数、函数对象等灵活来源
除非你已有严格接口契约、需要运行时动态切换观察者类型,否则没必要上继承体系。
Qt 的 QObject::connect() 和标准库方案本质区别在哪
Qt 版本不是“更高级”,而是为特定场景深度优化:信号与槽自动管理对象生命周期(父对象析构时自动断连)、线程安全(跨线程队列投递)、元对象系统支持字符串名连接。这些都以宏、moc 编译器和运行时开销为代价。
标准库方案没这些——它也不该有。如果你的模块不涉及 GUI、不跨线程通信、不需要运行时反射,硬套 Qt 机制反而让逻辑变得隐晦且难测试。
真正要注意的是:Qt 的连接默认是 Qt::AutoConnection,跨线程时会排队,而手写方案默认是同步调用,想异步必须自己扔进 std::thread 或 std::async,这点容易忽略。









