C++怎么实现观察者模式 C++事件通知机制设计与实现【设计模式】

发布时间 - 2026-01-29 00:00:00    点击率:
观察者模式核心是解决谁通知谁、何时解绑、生命周期管理三问题;需用std::weak_ptr避免野指针,通知时分离列表变更,参数按值或移动传递防悬空。

观察者模式的核心是避免硬编码依赖

C++里实现观察者模式,关键不是“怎么写类”,而是解决「谁通知谁、何时解绑、生命周期怎么管」这三个问题。硬写一个 Observer 基类加虚函数,很容易在对象析构后还被调用,触发野指针崩溃。

  • 观察者必须能安全注销自己,不能靠「手动调用 detach()」这种易遗漏的方式
  • 通知逻辑要支持异步或延迟执行(比如 UI 更新不能在数据线程直接调用)
  • std::function + std::shared_ptr 组合比纯虚函数接口更灵活,也更容易和现代 C++ 工具链(如 QObject 信号槽、boost::signals2)对齐

std::weak_ptr 管理观察者生命周期

裸指针或 std::shared_ptr 都会延长观察者寿命,造成循环引用或提前释放;std::weak_ptr 是唯一能“尝试访问、失败就跳过”的方案。

class Subject {
    std::vector> observers_;
public:
    void attach(std::shared_ptr obs) {
        observers_.push_back(obs);
    }
    void notify() {
        for (auto it = observers_.begin(); it != observers_.end();) {

if (auto obs = it->lock()) { obs->onEvent(); ++it; } else { it = observers_.erase(it); // 自动清理已销毁的观察者 } } } };
  • lock() 返回 std::shared_ptr,空则说明观察者已析构
  • 遍历时用 erase() 迭代器返回值,避免迭代器失效
  • 不要用 std::vector::remove_if + lock(),因为 lock() 可能抛异常(虽然通常不会),且语义不如显式遍历清晰

事件参数类型要支持值语义或移动语义

通知时传参别用 const T& 引用——如果事件源对象在通知中途析构,引用就悬空了。尤其在多线程下,这个坑非常隐蔽。

  • 简单类型(intstd::string)直接按值传递
  • 大对象优先用 std::unique_ptr 或移动构造,避免拷贝开销
  • 如果必须共享数据,用 std::shared_ptr,确保只读且生命周期可控

例如:

void notify(std::string event_name, std::shared_ptr data);
// 而不是 void notify(const Data& data);

不要在通知过程中修改观察者列表

这是最常被忽略的并发与迭代陷阱。哪怕单线程,onEvent() 内部调用 subject.detach(this),也会导致当前 notify() 循环中迭代器失效。

  • 解决方法:先收集待移除的 weak_ptr,再统一清理
  • 更稳妥的做法是把「变更观察者列表」推迟到通知结束后,比如用一个 std::vector<:function>> 缓存回调,在 notify() 尾部执行
  • Qt 的 QMetaObject::invokeMethod(..., Qt::QueuedConnection) 就是这个思路的工程化实现

实际项目里,越早引入 std::weak_ptr 和「通知/变更分离」设计,后期越不容易掉进析构期 crash 的坑。


# 编码  # 工具  # c++  # 解决方法  # red  # qt  # String  # const  # int  # void  # 循环  # 指针  # 虚函数  # 纯虚函数  # 接口  # 线程  # 多线程  # 值传递  # 并发  # function  # 对象  # 事件  # this  # 异步  # ui  # 迭代  # 遍历  # 这是  # 也会  # 很容易  # 能在  # 不容易  # 这三个  # 不要用 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: LinuxCD持续部署教程_自动发布与回滚机制  如何在腾讯云服务器快速搭建个人网站?  Claude怎样写结构化提示词_Claude结构化提示词写法【教程】  如何在Windows服务器上快速搭建网站?  javascript读取文本节点方法小结  进行网站优化必须要坚持的四大原则  HTML透明颜色代码在Angular里怎么设置_Angular透明颜色使用指南【详解】  济南网站建设制作公司,室内设计网站一般都有哪些功能?  ,网页ppt怎么弄成自己的ppt?  Android利用动画实现背景逐渐变暗  Laravel中间件起什么作用_Laravel Middleware请求生命周期与自定义详解  Laravel怎么创建自己的包(Package)_Laravel扩展包开发入门到发布  如何在Windows虚拟主机上快速搭建网站?  如何在建站之星绑定自定义域名?  WEB开发之注册页面验证码倒计时代码的实现  JavaScript如何实现倒计时_时间函数如何精确控制  Swift中swift中的switch 语句  详解vue.js组件化开发实践  怎样使用JSON进行数据交换_它有什么限制  专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?  零服务器AI建站解决方案:快速部署与云端平台低成本实践  清除minerd进程的简单方法  Laravel如何实现API版本控制_Laravel版本化API设计方案  猎豹浏览器开发者工具怎么打开 猎豹浏览器F12调试工具使用【前端必备】  如何在局域网内绑定自建网站域名?  Laravel怎么实现一对多关联查询_Laravel Eloquent模型关系定义与预加载【实战】  lovemo网页版地址 lovemo官网手机登录  怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?  如何获取PHP WAP自助建站系统源码?  在线教育网站制作平台,山西立德教育官网?  最好的网站制作公司,网购哪个网站口碑最好,推荐几个?谢谢?  网站页面设计需要考虑到这些问题  Google浏览器为什么这么卡 Google浏览器提速优化设置步骤【方法】  如何在宝塔面板创建新站点?  浅谈redis在项目中的应用  Laravel怎么配置.env环境变量_Laravel生产环境敏感数据保护与读取【方法】  大学网站设计制作软件有哪些,如何将网站制作成自己app?  如何生成腾讯云建站专用兑换码?  如何快速搭建高效香港服务器网站?  宙斯浏览器怎么屏蔽图片浏览 节省手机流量使用设置方法  网站建设整体流程解析,建站其实很容易!  如何用狗爹虚拟主机快速搭建网站?  详解jQuery停止动画——stop()方法的使用  如何在阿里云ECS服务器部署织梦CMS网站?  Laravel中Service Container是做什么的_Laravel服务容器与依赖注入核心概念解析  Laravel如何使用Scope本地作用域_Laravel模型常用查询逻辑封装技巧【手册】  在线制作视频的网站有哪些,电脑如何制作视频短片?  使用Dockerfile构建java web环境  Win11怎样安装网易有道词典_Win11安装词典教程【步骤】  详解MySQL数据库的安装与密码配置