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 】
相关推荐:
html如何与html链接_实现多个HTML页面互相链接【互相】
Laravel如何实现邮箱地址验证功能_Laravel邮件验证流程与配置
如何快速重置建站主机并恢复默认配置?
怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?
网站制作免费,什么网站能看正片电影?
Android仿QQ列表左滑删除操作
php json中文编码为null的解决办法
Laravel如何实现密码重置功能_Laravel密码找回与重置流程
Laravel控制器是什么_Laravel MVC架构中Controller的作用与实践
网站制作公司哪里好做,成都网站制作公司哪家做得比较好,更正规?
如何在Ubuntu系统下快速搭建WordPress个人网站?
详解MySQL数据库的安装与密码配置
南京网站制作费用,南京远驱官方网站?
如何选择可靠的免备案建站服务器?
Laravel如何安装Breeze扩展包_Laravel用户注册登录功能快速实现【流程】
微信小程序 canvas开发实例及注意事项
中山网站推广排名,中山信息港登录入口?
公司门户网站制作公司有哪些,怎样使用wordpress制作一个企业网站?
如何在建站宝盒中设置产品搜索功能?
HTML5段落标签p和br怎么选_文本排版常用标签对比【解答】
iOS UIView常见属性方法小结
DeepSeek是免费使用的吗 DeepSeek收费模式与Pro版本功能详解
如何在阿里云香港服务器快速搭建网站?
如何自定义建站之星模板颜色并下载新样式?
javascript中闭包概念与用法深入理解
Laravel如何使用Vite进行前端资源打包?(配置示例)
如何快速建站并高效导出源代码?
利用JavaScript实现拖拽改变元素大小
Laravel项目怎么部署到Linux_Laravel Nginx配置详解
如何在阿里云购买域名并搭建网站?
Midjourney怎么调整光影效果_Midjourney光影调整方法【指南】
网站制作壁纸教程视频,电脑壁纸网站?
为什么php本地部署后css不生效_静态资源加载失败修复技巧【技巧】
Laravel如何创建自定义Facades?(详细步骤)
用v-html解决Vue.js渲染中html标签不被解析的问题
Laravel怎么使用Intervention Image库处理图片上传和缩放
如何快速使用云服务器搭建个人网站?
Laravel如何记录日志_Laravel Logging系统配置与自定义日志通道
简历在线制作网站免费版,如何创建个人简历?
如何快速打造个性化非模板自助建站?
如何快速启动建站代理加盟业务?
Laravel Eloquent访问器与修改器是什么_Laravel Accessors & Mutators数据处理技巧
Laravel如何清理系统缓存命令_Laravel清除路由配置及视图缓存的方法【总结】
INTERNET浏览器怎样恢复关闭标签页_INTERNET浏览器标签恢复快捷键与方法【指南】
公司网站制作需要多少钱,找人做公司网站需要多少钱?
使用豆包 AI 辅助进行简单网页 HTML 结构设计
微信小程序 wx.uploadFile无法上传解决办法
LinuxCD持续部署教程_自动发布与回滚机制
,交易猫的商品怎么发布到网站上去?
Laravel的路由模型绑定怎么用_Laravel Route Model Binding简化控制器逻辑


