应使用std::weak_ptr存储观察者以避免循环引用和悬空指针:subject用vector持有观察者,通知前调用lock()获取shared_ptr,为空则清理;观察者不得强持有subject,禁用observer_ptr因其无生命周期管理能力。

用 std::shared_ptr 和 std::weak_ptr 管理观察者生命周期
观察者模式最常崩在对象提前销毁导致的悬空指针上。C++ 里不能靠裸指针或 std::unique_ptr 实现安全的“被观察者持有观察者”,因为会引发循环引用或强制要求观察者活得比被观察者久。std::shared_ptr 负责观察者自身的生命周期管理,而被观察者必须只用 std::weak_ptr 持有它——这样既可临时访问,又不会延长其寿命。
- 被观察者(Subject)的观察者容器应为
std::vector<:weak_ptr>></:weak_ptr>,不是shared_ptr或裸指针 - 每次通知前,必须调用
lock():若返回空shared_ptr,说明观察者已析构,直接跳过 - 观察者自身通常用
std::shared_ptr创建(例如std::make_shared<myobserver>()</myobserver>),确保能被外部和被观察者共同持有
避免 std::shared_ptr 循环引用:被观察者不能被观察者反向强持有
如果观察者内部保存了被观察者的 std::shared_ptr(比如为了调用 remove_observer()),就会形成循环引用:Subject → weak_ptr → Observer → shared_ptr → Subject,两者永远无法析构。
- 解决方法是观察者改用
std::weak_ptr<subject></subject>存储被观察者引用 - 需要操作 Subject 时,先
lock()获取临时shared_ptr,检查是否仍有效再调用 - 或者彻底剥离“观察者主动注销”的需求,改由 Subject 在通知前自动清理失效项(更简单、更常用)
通知时遍历 std::vector<:weak_ptr>></:weak_ptr> 的典型写法
很多人直接对 weak_ptr 调用 get() 或强制转换,结果触发未定义行为。正确路径只有一条:先 lock(),判空,再用。
void Subject::notify() {
// 注意:遍历时不能边删边遍历,先收集待删索引或用 erase-remove 惯用法
for (auto it = observers_.begin(); it != observers_.end();) {
auto obs = it->lock();
if (obs) {
obs->on_update(data_);
++it;
} else {
it = observers_.erase(it); // 自动清理已销毁的观察者
}
}
}
为什么不用 std::observer_ptr?
std::observer_ptr 是 C++17 引入的非拥有型指针包装器,但它不解决生命周期问题——它不阻止底层对象销毁,也不提供 lock() 这类安全访问机制。它适合“我保证这个指针一直有效”的场景,比如函数参数传递;但观察者模式恰恰无法做这种保证。
立即学习“C++免费学习笔记(深入)”;
- 用
observer_ptr替代weak_ptr会导致通知时可能解引用已析构对象,崩溃概率极高 - 它的语义是“只读观察”,不包含任何生存期协调能力,和模式本质冲突
- 目前没有标准库容器能配合
observer_ptr自动剔除失效项
std::vector<:weak_ptr>></:weak_ptr> 的清理时机和方式,很容易被当成“事后清理”随便处理——其实它得在每次通知前做,否则残留的空 weak_ptr 会越积越多,影响性能也掩盖逻辑问题。










