raii风格观察者模式靠shared_ptr生命周期、weak_ptr检查和析构自动解绑协同实现安全,禁用裸指针;observer须继承enable_shared_from_this并用make_shared构造;notify时先清理失效weak_ptr再调用lock通知。

RAII 风格的观察者模式在 C++ 里不是靠“注册/注销”手动配对来保证安全,而是靠 std::shared_ptr 生命周期 + weak_ptr 检查 + 析构自动解绑这三者协同——否则你写的“轻量级”实际是悬空回调高发区。
为什么不能直接用 raw pointer 存 observer
裸指针存 observer 是最常见翻车点:被观察者生命周期长于 observer 时,notify() 一调就崩。哪怕加了 if (obs) 也无效——obs 可能已释放但内存未覆写,条件成立却访问非法地址。
正确做法是让 observer 自己决定“是否还活着”,被观察者只持 std::weak_ptr<observer></observer>:
-
weak_ptr::lock()在调用瞬间检查对象是否存活,返回shared_ptr或空值 - 被观察者不参与 observer 的生命周期管理,只做“尝试通知”
- observer 析构时,所有关联的
weak_ptr自动失效,无需显式 unregister
Observer 必须继承 std::enable_shared_from_this
否则无法从 this 安全构造 shared_ptr 传给被观察者。漏掉这行,shared_from_this() 抛 std::bad_weak_ptr 异常:
class MyObserver : public std::enable_shared_from_this<myobserver> {
public:
void onEvent(int data) { /* ... */ }
};</myobserver>
使用时必须用 std::make_shared<myobserver>()</myobserver> 构造,不能 new 后再包 shared_ptr——否则 enable_shared_from_this 内部 weak_ptr 未初始化。
- 构造必须走
make_shared,否则shared_from_this()失效 - 回调函数签名建议统一为
std::function<void></void>,避免模板爆炸 - 若 observer 是栈对象?不行——RAII 封装的前提是 observer 必须动态分配并由 shared_ptr 管理
Subject 的回调存储与通知逻辑怎么写才不漏不重
用 std::vector<:weak_ptr>></:weak_ptr> 存 observer 弱引用,notify() 里逐个 lock(),空则 erase(注意迭代器失效):
void notify(int data) {
// 先清理已销毁的 observer
observers_.erase(
std::remove_if(observers_.begin(), observers_.end(),
[](const auto& w) { return w.lock() == nullptr; }),
observers_.end()
);
// 再通知存活的
for (auto& w : observers_) {
if (auto obs = w.lock()) {
obs->onEvent(data);
}
}
}
关键细节:
- 不推荐边遍历边 erase——容易跳过下一个元素;
remove_if + erase是标准安全写法 - 清理和通知拆成两步,避免“通知中 observer 自毁导致后续 lock 失败”这类竞态(单线程下也建议拆)
- 如果需要线程安全,
observers_加mutable std::mutex,但 lock/erase/notify 全部锁住——别只锁 erase
真正轻量的关键:不依赖基类、不强制虚函数、不引入信号槽框架
这个方案没抽象基类 IObserver,observer 只需有匹配的 onEvent 成员函数即可,靠 std::function 绑定或模板推导适配:
// 支持自由函数、lambda、成员函数
template <typename f>
void attach(F&& f) {
observers_.emplace_back(
std::static_pointer_cast<void>(std::make_shared<decltype>(std::forward<f>(f)))
);
// 实际中建议封装为 type-erased wrapper,此处简化示意
}</f></decltype></void></typename>
但更推荐聚焦在“对象生命周期安全”而非“语法糖泛化”——多数业务场景 observer 是明确类,用 shared_ptr<t></t> + weak_ptr<t></t> 已足够清晰可靠。
最容易被忽略的是:observer 的析构函数里不能调用 subject_->detach(this) ——它本就不该知道 subject 的存在;真正的解耦,是 observer 一销毁,所有 weak_ptr 自动失效,subject 甚至不用感知。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











