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& 引用——如果事件源对象在通知中途析构,引用就悬空了。尤其在多线程下,这个坑非常隐蔽。
- 简单类型(
int、std::string)直接按值传递 - 大对象优先用
std::unique_ptr或移动构造,避免拷贝开销 - 如果必须共享数据,用
std::shared_ptr,确保只读且生命周期可控
例如:
void notify(std::string event_name, std::shared_ptrdata); // 而不是 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数据库的安装与密码配置


