c++中如何实现观察者模式_c++设计模式之观察者模式代码【汇总】

9次阅读

Observer接口必须声明虚析构函数,否则多态删除会导致析构不完整、内存泄漏或未定义行为;纯接口也需显式声明,编译器不会自动生成。

c++中如何实现观察者模式_c++设计模式之观察者模式代码【汇总】

Observer 接口必须用虚析构函数

不加 virtual ~Observer() = default; 会导致派生类对象通过基类指针删除时析构不完整,内存泄漏或未定义行为。这是 c++ 观察者模式最常被忽略的底层陷阱。

实际场景中,Subject 通常持有 std::vector<:unique_ptr>>std::vector,而后者更常见(避免强制所有权转移)。无论哪种,只要涉及多态删除,就必须有虚析构。

  • std::unique_ptr → 必须虚析构
  • 用裸指针 Observer* → 仍需虚析构(因为 delete ptr 会调用基类析构)
  • 若 Observer 是纯接口(无数据成员),也仍需显式声明虚析构,否则编译器不会自动生成

std::function + std::vector 实现松耦合 Observer

比起继承接口,用回调函数注册更轻量、无侵入性,适合事件驱动或脚本化扩展场景。但要注意生命周期管理——std::function 捕获局部变量时极易悬垂。

典型错误是把 Lambda 捕获了上对象后存进 Subject 的容器里,稍后调用就崩溃。安全做法是只捕获 this 或智能指针,或确保观察者对象比 Subject 活得久。

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

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

// ✅ 安全:捕获 this(假设 ObserverImpl 生命周期可控) struct ObserverImpl { void onValue(int v) { / ... / } void registerTo(Subject& s) { s.attach([this](int v) { this->onValue(v); }); } };

线程安全不是默认选项

std::vector::push_back 和遍历通知都不是原子操作。多线程并发 attach()notify() 会触发 data race,甚至导致迭代器失效或容器重分配时崩溃。

常见折中方案是读多写少时用 std::shared_mutex(C++17),或直接加 std::mutex 锁住整个通知流程。但锁粒度太大会降低吞吐,尤其通知链路长时。

  • 避免在 notify() 中调用可能阻塞或长时间运行的回调
  • 若需异步通知,建议把回调转为 post 到线程池,而不是在锁内执行
  • 不要在回调里调用 detach() —— 遍历时修改容器是未定义行为;应改用标记+延迟清理(如 erase-remove idiom)

std::weak_ptr 防止循环引用(当用 shared_ptr 管理 Observer)

如果 SubjectObserver 都用 std::shared_ptr 互相持有,就会形成循环引用,导致对象永远不析构。解决方案是 Subject 改用 std::weak_ptr 存储,每次通知前先 lock()

这会带来额外开销(控制块访问 + 引用计数检查),但能彻底解决悬挂指针和泄漏问题。适用于 Observer 生命周期不可控(比如来自插件模块)的场景。

class Subject {     std::vector> observers_; public:     void attach(std::shared_ptr obs) {         observers_.push_back(obs);     }     void notify() {         // 注意:这里要拷贝并过滤已销毁的 observer         std::vector> alive;         for (auto& w : observers_) {             if (auto s = w.lock()) alive.push_back(s);         }         for (auto& obs : alive) obs->update();     } };

C++ 观察者最难的从来不是结构,而是谁负责生命周期、谁触发销毁、谁保证调用时对象还活着——这些不能靠模式图解决,得靠具体容器语义和所有权约定。

text=ZqhQzanResources