0

0

c++如何实现观察者模式_c++ 事件委托与通知机制设计【案例】

裘德小鎮的故事

裘德小鎮的故事

发布时间:2025-12-27 14:47:34

|

805人浏览过

|

来源于php中文网

原创

std::function + std::vector 足以实现轻量、类型安全、无内存泄漏的观察者模式,无需GoF类图;核心是支持注册、调用与解绑,配合 weak_ptr 和 erase_if 可安全处理生命周期与中途移除问题。

c++如何实现观察者模式_c++ 事件委托与通知机制设计【案例】

为什么 std::function + std::vector 就够用了

观察者模式在 C++ 里不必强行套用经典 GoF 类图。现代 C++ 的 std::functionstd::vector 足以支撑轻量、类型安全、无内存泄漏的通知机制,且避免虚函数开销和继承耦合。

关键不是“实现模式”,而是“让通知可注册、可调用、可解绑”。下面所有设计都围绕这个目标展开:

  • std::function 封装任意可调用体(lambda、成员函数指针、普通函数),类型擦除但编译期检查参数匹配
  • std::vector 存储观察者,简单直接;若需频繁增删查,可换 std::unordered_map 配唯一 ID,但多数场景没必要
  • 不依赖基类或接口,观察者无需继承、无需实现特定函数,降低侵入性

如何安全绑定成员函数并避免悬空调用

成员函数指针必须绑定对象实例,而对象生命周期常早于事件源。最常见错误是捕获局部对象或已析构对象的 this 指针,导致未定义行为。

推荐做法:统一用 std::shared_ptr 管理被观察对象,并要求观察者也通过 std::weak_ptr 持有引用 —— 这样既支持自动解绑,又不延长对象寿命:

立即学习C++免费学习笔记(深入)”;

class Subject {
public:
    using Callback = std::function;
    void attach(Callback cb) { observers_.push_back(cb); }
    void notify(int value) {
        for (auto& cb : observers_) cb(value);
    }

private: std::vector observers_; };

struct Observer { explicit Observer(std::sharedptr sub) : subject(sub) { // 绑定时捕获 weakptr,调用前 lock() subject->attach([wp = std::weak_ptr(shared_from_this())](int v) { if (auto self = wp.lock()) { self->onEvent(v); } }); } void onEvent(int v) { / ... / } std::shared_ptr shared_from_this() { return shared_from_this(); } std::sharedptr subject; };

std::erase_if + std::shared_ptr 解决通知中途移除观察者的问题

通知过程中,某个回调可能调用 detach(),直接 erase 迭代器会破坏循环。传统方案用两遍遍历(标记+清理)或手动维护索引,繁琐易错。

Litero
Litero

AI co-writer for students

下载

C++20 起,std::erase_if 是更干净的解法:先收集待移除项,再批量擦除。配合 std::shared_ptr 的引用计数,还能自然处理“通知中 self-destruct”场景:

  • notify() 前复制一份 observers_,确保迭代安全
  • 每个回调内部若调用 detach(),只标记自身失效,不立即修改原容器
  • 通知结束后,用 std::erase_if(observers_, [](auto& cb) { return cb.expired(); }) 清理(若用 std::weak_ptr 包裹回调)

若不用弱引用,也可用 token 机制:每次 attach() 返回一个 std::size_t id,detach(id) 标记该 id 失效,notify() 跳过失效项。

事件委托与跨线程通知的边界在哪

纯内存内通知(同一线程)不需要锁或队列。一旦涉及多线程,就不再是“观察者模式”的范畴,而是“事件总线”或“消息泵”的职责。

常见误用:在 UI 线程注册回调,却从后台线程触发 notify(),导致 GUI 操作崩溃。此时必须明确约定:

  • 事件源不负责线程调度 —— 由使用者决定是否投递到主线程(如 Qt 的 QMetaObject::invokeMethod,或 Windows 的 PostMessage
  • 若需异步通知,应显式封装为 std::threadstd::async,并在回调内做线程安全判断(例如检查 QThread::currentThread() == ui_thread_
  • 不要在 std::function 容器里存裸函数指针 + void* 用户数据 —— 这种 C 风格写法丢失类型信息,且无法自动管理生命周期

真正难的从来不是怎么发通知,而是谁来保证“发出去之后,接收方还活着、还在正确线程、还没被用户关掉窗口”。这部分逻辑必须外提,不能塞进 Subject 类里。

相关专题

更多
视频后缀名都有哪些
视频后缀名都有哪些

视频后缀名都有avi、mpg、mpeg、rm、rmvb、flv、wmv、mov、mkv、ASF、M1V、M2V、MPE、QT、VOB、RA、RMJ、RMS、RAM、等等。更多关于视频后缀名的相关知识,详情请看本专题下面的文章,php中文网欢迎大家前来学习。

3319

2023.10.31

C++ Qt图形开发
C++ Qt图形开发

本专题专注于 C++ Qt框架在图形界面开发中的应用,系统讲解窗口设计、信号与槽机制、界面布局、事件处理、数据库连接与跨平台打包等核心技能,通过多个桌面应用项目实战,帮助学员快速掌握 Qt 框架并独立完成跨平台GUI软件的开发。

67

2025.08.15

C++ 图形界面开发基础(Qt方向)
C++ 图形界面开发基础(Qt方向)

本专题系统讲解 使用 C++ 与 Qt 进行图形界面(GUI)开发的核心技能,内容涵盖 Qt 项目结构、窗口组件、信号与槽机制、事件处理、布局管理、资源管理,以及跨平台编译与打包流程。通过多个小型桌面应用实战案例,帮助学习者掌握从界面设计到功能实现的完整 GUI 开发能力。

38

2025.12.05

登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

6030

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

778

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

1044

2023.12.21

token什么意思
token什么意思

token是一种用于表示用户权限、记录交易信息、支付虚拟货币的数字货币。可以用来在特定的网络上进行交易,用来购买或出售特定的虚拟货币,也可以用来支付特定的服务费用。想了解更多token什么意思的相关内容可以访问本专题下面的文章。

1067

2024.03.01

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

172

2023.11.23

ip地址修改教程大全
ip地址修改教程大全

本专题整合了ip地址修改教程大全,阅读下面的文章自行寻找合适的解决教程。

27

2025.12.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 3万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号