观察者模式通过定义一对多依赖关系,实现对象间松耦合通信。1. 使用抽象接口:定义Observer基类和Subject类,通过继承与虚函数实现通知机制,适合需要多态的场景。2. 使用std::function:以函数对象替代继承,支持lambda、函数指针等可调用对象,提升灵活性。3. 智能指针版本:结合shared_ptr与weak_ptr自动管理订阅生命周期,避免悬空指针。适用于GUI事件、MVC架构、消息总线等场景,建议根据需求选择继承或回调方式,并注意线程安全与资源管理。
观察者模式(Observer Pattern),也叫发布-订阅模式,是一种行为设计模式,用于在对象之间定义一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会自动收到通知。在 C++ 中实现这一机制,可以通过抽象基类、函数指针、std::function 或智能指针等现代 C++ 特性来完成。
这是最经典的方式,通过定义观察者和被观察者的抽象接口,实现松耦合通信。
步骤说明:
代码示例:
#include#include #include class Observer;
class Subject { private: std::vector
observers; int state; public: void attach(Observer obs); void detach(Observer obs); void notify(); void setState(int s); int getState() const { return state; } };
class Observer { protected: Subject subject; public: explicit Observer(Subject s) : subject(s) { subject->attach(this); } virtual ~Observer() { subject->detach(this); } virtual void update() = 0; };
void Subject::attach(Observer* obs) { observers.push_back(obs); }
void Subject::d
etach(Observer* obs) { observers.erase( std::remove(observers.begin(), observers.end(), obs), observers.end() ); }
void Subject::notify() { for (auto obs : observers) { obs->update(); } }
void Subject::setState(int s) { state = s; notify(); }
// 具体观察者 class ConcreteObserverA : public Observer { public: explicit ConcreteObserverA(Subject* s) : Observer(s) {} void update() override { std::cout << "Observer A: Subject state is now " << subject->getState() << "\n"; } };
class ConcreteObserverB : public Observer { public: explicit ConcreteObserverB(Subject* s) : Observer(s) {} void update() override { std::cout << "Observer B: Subject state changed to " << subject->getState() << "\n"; } };
使用方式:
int main() {
Subject subject;
ConcreteObserverA observerA(&subject);
ConcreteObserverB observerB(&subject);
subject.setState(10); // 两个观察者都会收到通知
subject.setState(20);
return 0;
}
2. 使用 std::function 实现更灵活的订阅机制
如果不想使用继承,可以用函数对象代替抽象类,让订阅者更加灵活,支持 lambda、函数指针或任意可调用对象。
#include
#include
#include
#include
class EventSystem {
private:
std::vector> listeners;
public:
// 订阅事件
void subscribe(std::function callback) {
listeners.push_back(callback);
}
// 发布事件
void publish(int value) {
for (const auto& listener : listeners) {
listener(value);
}
}};
使用示例:
int main() {
EventSystem eventBus;
// 使用 lambda 订阅
eventBus.subscribe([](int val) {
std::cout zuojiankuohaophpcnzuojiankuohaophpcn "Logger: Value updated to " zuojiankuohaophpcnzuojiankuohaophpcn val zuojiankuohaophpcnzuojiankuohaophpcn "\n";
});
eventBus.subscribe([](int val) {
if (val youjiankuohaophpcn 50) {
std::cout zuojiankuohaophpcnzuojiankuohaophpcn "Alert: High value detected: " zuojiankuohaophpcnzuojiankuohaophpcn val zuojiankuohaophpcnzuojiankuohaophpcn "\n";
}
});
// 模拟数据变化
eventBus.publish(30);
eventBus.publish(60);
return 0;}
3. 支持自动解绑的智能指针版本(进阶)
为了避免手动管理生命周期导致的悬空指针问题,可以结合 std::shared_ptr 和 weak_ptr 实现自动清理。
思路:每个订阅者返回一个句柄(token),当句柄销毁时自动从列表中移除。或者使用 weak_ptr 存储回调,在调用前检查是否有效。
#include
#include
#include
#include
class Publisher {
private:
struct CallbackWrapper {
std::weak_ptr> cb;
};
std::vector callbacks;
public:
std::shared_ptr>
subscribe(std::function func) {
auto shared_func = std::make_shared>(func);
callbacks.push_back({shared_func});
return shared_func; // 返回共享指针作为“订阅句柄”
}
void publish(int value) {
callbacks.erase(
std::remove_if(callbacks.begin(), callbacks.end(),
[value](const CallbackWrapper& wrapper) {
auto locked = wrapper.cb.lock();
if (locked) {
(*locked)(value);
return false;
}
return true; // 已失效,移除
}),
callbacks.end()
);
}};
这样当外部持有的 shared_ptr 被释放,下次 publish 时会自动清理无效项。
应用场景与建议
观察者模式适用于以下场景:
- GUI 事件系统(按钮点击通知多个组件)
- 模型-视图架构中,模型更新通知视图刷新
- 日志系统、消息总线、状态监控等
设计建议:
- 若需要多态行为,使用接口 + 继承方式。
- 若注重灵活性和轻量级,推荐 std::function + 回调。
- 注意循环引用和生命周期管理,尤其是在使用智能指针时。
- 线程安全需额外加锁(如 std::mutex),若跨线程发布。
基本上就这些。根据项目复杂度选择合适实现方式即可。
# go
# app
# ai
# c++
# ios
# stream
# red
# mvc
# 架构
# 多态
# Token
# 循环
# Lambda
# 指针
# 继承
# 虚函数
# 纯虚函数
# 接口
# 线程
# 空指针
# function
# 对象
# 事件
# 句柄
# 回调
# 移除
# 适用于
# 自己的
# 进阶
# 这是
# 这一
# 是在
# 是一种
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
网络优化76771 】
【
技术知识130152 】
【
IDC云计算60162 】
【
营销推广131313 】
【
AI优化88182 】
【
百度推广37138 】
【
网站推荐60173 】
【
精选阅读31334 】
相关推荐:
Win10如何卸载预装Edge扩展_Win10卸载Edge扩展教程【方法】
Win11如何关闭小娜Cortana Win11禁用Cortana语音助手【优化】
c++怎么使用std::filesystem遍历文件夹_c++ 递归查找文件与权限修改【技巧】
Windows系统被恶意软件破坏后的恢复策略_错误提示修复方式
Win11怎么关闭专注助手 Win11关闭免打扰模式设置【操作】
Windows音频驱动无声音原因解析_声卡驱动错误修复步骤
VSC怎么快速定位PHP错误行_错误追踪设置法【方法】
php删除数据怎么加限制_带where条件删除避免全删【指南】
Python如何创建带属性的XML节点
Windows10如何删除恢复分区_Win10 Diskpart命令强制删除分区
mac怎么打开终端_MAC终端Terminal使用入门与常用命令【教程】
c++协程和线程的区别 c++异步编程模型对比【核心】
Win11怎么检查TPM2.0模块_Windows11受信任平台模块开启状态查询
Windows10怎么用“讲述人”读屏辅助 Windows10轻松使用开启讲述人朗读屏幕文字帮助视障用户【教程】
如何在 PHP 中按相同键合并两个关联数组为二维数组
Python数据抓取合法性_合规说明【指导】
Windows10系统怎么查看IP地址_Win10网络连接状态详细信息
Win11怎么设置任务栏大小_Windows11注册表修改TaskbarSi值
Windows10系统怎么查看设备管理器_Win10快捷键Win+X菜单使用
Mac如何与安卓手机传文件_Mac和Android设备互通【必备工具】
C#如何在一个XML文件中查找并替换文本内容
Win10系统更新错误0x80240034怎么办 Win10更新错误解决法【方法】
Mac如何调整Dock栏大小和位置_Mac程序坞个性化设置
Win11怎么设置虚拟内存_Windows 11优化内存性能提升速度【技巧】
Win11麦克风没声音怎么设置_Win11麦克风权限及驱动修复【教程】
Win11怎么更改任务栏位置_修改注册表将Win11任务栏置顶【教程】
如何在Golang中实现并发消息队列消费者_Golang channel消息消费实践
Python日志系统设计与实现_高可观测性架构实战
Win11怎么开启移动热点_Windows11共享网络给手机设置教程
如何使用 Selenium 正确获取篮球参考网站球员名单元素列表
LINUX怎么查看进程_LINUX ps命令查看运行服务
如何使用正则表达式提取以编号开头、后跟多个注解的完整代码块
c++怎么使用std::unique实现去重_c++ 容器元素排序与连续重复删除【教程】
Win11怎么设置任务栏透明_Windows11使用工具美化任务栏
windows 10应用商店区域怎么改_windows 10微软商店切换地区方法
Win11如何连接Xbox手柄 Win11蓝牙连接游戏手柄教程【步骤】
如何使用Golang配置安全开发环境_防止敏感信息泄露
Win11怎么设置触控板手势_Windows11三指四指操作自定义
Win10怎样清理C盘阿里旺旺缓存_Win10清理阿里旺旺缓存步骤【步骤】
Win11怎么关闭OneDrive同步_Win11取消自动备份文件【教程】
Win11怎么设置ip地址_Windows 11手动配置网络IP教程【详解】
c++中如何使用虚函数实现多态_c++多态性实现原理
Win11怎么打开注册表_Windows 11注册表编辑器启动命令【步骤】
如何在Golang中实现WebSocket广播_使用Channel和协程分发消息
Win11怎么关闭SmartScreen_禁用Windows Defender筛选器教程【步骤】
Win11怎么关闭开机声音_Win11系统启动提示音静音【教程】
Win11怎么关闭透明效果_Windows11个性化颜色关闭透明
Win11怎么设置默认输入法 Win11固定中文输入法【步骤】
Go 中实现 Python urllib.quote() 功能的等效方法
如何在 Go 中高效缓存与分发网络视频流
2025-11-19
致胜网络推广营销网专注海外推广十年,是谷歌推广.Facebook广告全球合作伙伴,我们精英化的技术团队为企业提供谷歌海外推广+外贸网站建设+网站维护运营+Google SEO优化+社交营销为您提供一站式海外营销服务。