observer接口应定义纯虚update()函数,subject基类用vector管理观察者并提供attach/detach/notify方法,通过抽象接口隔离实现细节,避免编译依赖和紧耦合。

Observer 接口和 Subject 基类怎么写才不耦合
观察者模式的核心是解耦——Subject 不该知道具体哪个类在观察它,Observer 也不该依赖 Subject 的实现细节。所以必须用抽象接口隔离变化。
典型错误是直接让 Subject 持有 std::vector<concreteobserver></concreteobserver>,这会导致编译依赖和难以扩展。正确做法是定义纯虚基类:
class Observer {
public:
virtual ~Observer() = default;
virtual void update() = 0; // 或带参数的 update(const std::any& data)
};
class Subject {
protected:
std::vector<observer> observers_;
public:
virtual ~Subject() = default;
void attach(Observer* o) { observers_.push_back(o); }
void detach(Observer* o) {
observers_.erase(
std::remove(observers_.begin(), observers_.end(), o),
observers_.end()
);
}
void notify() {
for (auto* o : observers_) o->update();
}
};</observer>
- 用
Observer*而不是std::shared_ptr是为了降低侵入性;若需自动生命周期管理,可改用std::weak_ptr配合std::shared_ptr,但得额外处理过期检查 -
detach()里用std::remove + erase是标准写法,直接std::erase(C++20)更简洁,但老项目要注意兼容性 - 别在
notify()过程中修改observers_容器,否则迭代器失效——这是多线程和单线程下都容易踩的坑
如何避免 notify 时 observer 被析构导致崩溃
最常见 crash 场景:某个 Observer 在 update() 中调用了 subject->detach(this),甚至直接 delete this,而此时 notify() 还在遍历容器。
解决思路不是禁止这种操作,而是让通知机制能安全应对。
- 方案一(推荐):在
notify()前拷贝指针列表:auto copies = observers_;,然后遍历copies。这样即使原容器被修改也不影响当前通知 - 方案二:用
std::weak_ptr<observer></observer>存储,每次notify()时先lock(),跳过已失效的观察者。但要求所有Observer必须由std::shared_ptr管理,约束较强 - 方案三:引入“延迟 detach”机制——
detach()不立即移除,只标记为待删除,notify()结束后统一清理。适合高频更新+频繁增删的场景
实际项目中,方案一足够轻量且无额外内存管理负担,90% 的简单场景用它就够了。
std::function + lambda 能替代 Observer 类吗
可以,而且很常用——尤其当观察逻辑简单、不需复用或状态管理时。
把 Observer 抽象类换成 std::function<void></void>,Subject 改成持有一个 std::vector<:function>></:function>,attach 就变成:
void attach(std::function<void> cb) {
callbacks_.push_back(std::move(cb));
}</void>
- 优势:零继承开销,lambda 捕获局部变量极其方便,适合 UI 回调、日志钩子等一次性逻辑
- 劣势:无法在运行时识别/比较回调(比如想 detach 某个特定 lambda 就做不到),也不能在回调里访问
this成员而不捕获this,容易引发悬垂指针 - 如果需要 detach 特定回调,建议给每个
std::function分配唯一int id或用std::any存标识,但这就开始变重了
结论:不是替代,而是互补。复杂业务对象用类继承,胶水逻辑用 std::function。
为什么 Qt 的 signal/slot 不算“标准观察者模式”
Qt 的 QObject::connect() 看起来像观察者,但它底层是元对象系统驱动的,和传统 GoF 观察者有本质区别:
- Qt 的连接是“信号发射 → 元对象分发 → 槽函数调用”,中间经过事件循环或直接调用,支持跨线程队列,而标准模式是同步直接调用
- Qt 不要求显式
attach/detach,QObject生命周期结束时自动断开,这是靠父对象树和元信息实现的,C++ 原生没这能力 - 你不能用 Qt 的
signal去对接非QObject的普通类,而标准模式的Observer接口可以是任意类实现
换句话说:Qt 是观察者模式的工业级超集,但直接照搬它的设计到裸 C++ 项目里,反而会引入不必要的依赖和复杂度。简单需求,手写四五十行就足够。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











