观察者模式需用抽象接口解耦:Observer定义纯虚onNotify(),Subject仅持std::vector<Observer*>并提供attach/detach/notify;notify时应拷贝观察者列表或用weak_ptr防迭代器失效;std::function可替代但牺牲类型安全与生命周期管理;Qt用moc而非虚函数以支持多继承和跨线程。

Observer 接口和 Subject 基类怎么写才不耦合
观察者模式的核心是解耦通知方(Subject)和接收方(Observer),所以必须用抽象接口隔离具体实现。不要让 Subject 持有 std::vector<ConcreteObserver*>,而应定义纯虚基类:class Observer { public: virtual void onNotify(const std::string& event) = 0; virtual ~Observer() = default; }。同理,Subject 基类只管理 std::vector<Observer*>,提供 attach()、detach()、notify() 三个接口。这样任何具体类(比如 Button 或 TemperatureSensor)继承 Subject 后,都能被任意实现了 Observer 的类监听。
如何避免 notify 时迭代器失效或野指针
常见错误是在 notify() 循环中,某个 Observer 的回调里调用了 detach(),导致后续迭代访问已释放的指针或 vector 重分配后迭代器失效。解决方案有两个:
- 用
std::weak_ptr<Observer>替代裸指针存储,attach()传入std::shared_ptr<Observer>,notify()中先lock()再调用,自动跳过已销毁对象 - 若坚持裸指针,
notify()前先拷贝一份指针快照:auto observers_copy = observers_;,然后遍历副本,避免原容器被修改影响迭代
前者更安全但有引用计数开销;后者轻量,但要求调用方严格保证 Observer 生命周期长于 Subject。
std::function + std::bind 能替代 Observer 基类吗
可以,而且更灵活——适合简单场景或临时回调。例如:std::vector<std::function<void(const std::string&)>> callbacks_;,attach() 接收任意可调用对象:callbacks_.emplace_back([this]{ this->handleClick(); });。但它牺牲了类型安全和统一生命周期管理:无法在 detach() 时精确移除某个 lambda(除非保存句柄),也不能自动感知观察者析构。如果项目已有成熟内存管理策略(如 Qt 的 QObject 树),裸函数对象反而容易引发悬垂回调。
立即学习“C++免费学习笔记(深入)”;
为什么 Qt 的信号槽不用虚函数而用 moc
Qt 选择元对象编译器(moc)生成代码,核心是为了绕过虚函数表的运行时开销和单继承限制。C++ 原生虚函数只能支持单一基类,而 QObject 子类往往还需继承业务基类(如 Widget 或 Thread)。moc 把信号声明转成普通函数调用,再通过字符串匹配和运行时元信息做连接,既支持多继承语义,又允许跨线程自动排队。自己手写时若不需要跨线程或动态连接,基于虚函数的轻量版完全够用;真要对标 Qt,就得接受 moc 构建流程和额外的编译步骤。
真正难的不是写一个能跑的 observer,而是决定谁拥有观察者、谁负责清理、以及通知是否需要排队或跨线程转发——这些决策会渗透到整个对象生命周期设计里。


















