必须用虚析构函数;若通过observer*指针删除派生对象,无虚析构会导致派生类析构函数不被调用,引发资源泄漏或未定义行为。

Observer 接口要不要用虚析构函数?
必须用。如果 Observer 是基类,且你打算通过 Observer* 指针删除派生对象(比如在被观察者清理订阅列表时),没有虚析构函数会导致未定义行为——派生类的析构逻辑根本不会执行。
实操建议:
- 声明为
virtual ~Observer() = default;或virtual ~Observer() {} - 哪怕当前所有派生类析构函数为空,也别省略
virtual - 如果用
std::shared_ptr<observer></observer>管理生命周期,虚析构仍是必需的——shared_ptr删除时仍会调用所指对象的析构函数
怎么避免 notify 时迭代器失效或重复通知?
常见错误是边遍历 std::vector<:weak_ptr>></:weak_ptr> 边调用 observer->update(),而 update() 内部又可能触发 detach(),导致 vector 重分配或元素移动,当前迭代器立刻失效。
安全做法是先收集有效指针,再统一通知:
- 遍历容器时,对每个
std::weak_ptr调用lock(),得到std::shared_ptr<observer></observer>;若返回空,说明观察者已销毁,跳过 - 把所有非空的
shared_ptr存进临时std::vector,遍历这个副本去调用update() - 不要在
notify()中修改原始容器(如 erase 已失效的 weak_ptr),留到下一次 notify 前集中清理
示例关键片段:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::vector<:shared_ptr>> alive_observers;<br>for (const auto& wptr : observers_) {<br> if (auto sptr = wptr.lock()) {<br> alive_observers.push_back(std::move(sptr));<br> }<br>}<br>for (const auto& obs : alive_observers) {<br> obs->update();<br>}</:shared_ptr>
std::weak_ptr 和裸指针哪个更适合存 observer?
优先选 std::weak_ptr,裸指针几乎不可取。
原因很实际:
- 裸指针无法判断观察者是否还活着,
notify()时一解引用就崩(Segmentation fault或Access violation) -
std::weak_ptr配合lock()是零成本运行时检查,失败即跳过,安全且明确 - 代价只是多一个控制块(通常 16 字节),换来的是整个模式的健壮性
- 如果 observer 生命周期严格由外部管理、且保证永不早于 subject 销毁(极少见),才考虑裸指针 + 注释强约束,但不推荐
subject 的 attach/detach 需要线程安全吗?
取决于使用场景。如果 observer 可能在任意线程调用 attach() 或 detach(),而 notify() 又在另一个线程执行,就必须同步。
简单有效的方案:
- 用
std::shared_mutex(C++17):读多写少,notify()用lock_shared(),attach/detach用lock() - 若编译器不支持 C++17,用
std::mutex也可,只是notify()会阻塞其他 attach/detach,但比数据竞争好得多 - 完全回避锁?可以改用无锁队列暂存待添加/移除请求,在
notify()前批量合并,但复杂度陡增,一般项目没必要
最容易被忽略的一点:即使你认为“只在主线程操作”,只要用了异步回调、定时器、或任何第三方库可能回调 observer,就得重新评估线程安全边界。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










