使用观察者模式多次继承Subject时误用观察者列表问题
问题分析与解决方案
你的核心问题出在两个地方:一是Observer类的notify方法访问权限导致的编译错误(进而引发运行时未定义行为);二是需要确保多重继承的不同Subject<T>特化类各自独立维护观察者列表,避免调用混淆。
1. 修复Observer的notify访问权限
Subject的notify方法需要调用Observer的同名方法,但你当前将Observer::notify设为private,这会导致编译失败,后续的段错误大概率是由此引发的未定义行为。有两种修复方式:
方式A:将notify改为public
直接开放访问权限,最简单直接:
template<typename T> class Observer { public: virtual void notify(const T& data) = 0; virtual ~Observer() = default; };
方式B:声明Subject为友元
如果希望保持notify的私有性,可通过友元授权Subject<T>访问:
template<typename T> class Observer { private: virtual void notify(const T& data) = 0; friend class Subject<T>; // 允许Subject<T>调用notify public: virtual ~Observer() = default; };
2. 确保notify调用的基类唯一性
你在Car类中使用this->Subject<MoveData>::notify(moveData)的写法是正确的,这种显式限定能保证调用的是对应特化基类的notify方法,访问的也是该基类独立维护的observers列表。
如果省略基类限定(直接写notify(moveData)),会因两个基类都有notify方法而产生编译二义性,务必避免这种写法。
3. 优化建议:封装订阅接口
为了避免外部调用Car的subscribe方法时出现二义性(两个基类都有该方法),可以在Car类中封装针对不同事件的订阅方法:
// 假设已定义事件数据结构 struct MoveData {}; struct SteerData {}; class Car : public Subject<MoveData>, public Subject<SteerData> { public: // 封装Move事件的订阅 void subscribeMove(Observer<MoveData>* observer) { Subject<MoveData>::subscribe(observer); } // 封装Steer事件的订阅 void subscribeSteer(Observer<SteerData>* observer) { Subject<SteerData>::subscribe(observer); } void move() { const MoveData moveData{}; Subject<MoveData>::notify(moveData); } void steer() { const SteerData steerData{}; Subject<SteerData>::notify(steerData); } };
验证示例
创建具体观察者类测试:
#include <iostream> #include <list> // 此处省略Observer和Subject的修复代码,使用上述方式A或B class MoveObserver : public Observer<MoveData> { public: void notify(const MoveData& data) override { std::cout << "收到Move事件\n"; } }; class SteerObserver : public Observer<SteerData> { public: void notify(const SteerData& data) override { std::cout << "收到Steer事件\n"; } }; int main() { Car myCar; MoveObserver moveObs; SteerObserver steerObs; myCar.subscribeMove(&moveObs); myCar.subscribeSteer(&steerObs); myCar.move(); // 仅触发MoveObserver的notify myCar.steer(); // 仅触发SteerObserver的notify return 0; }
运行后会分别输出对应事件的通知,确保两个事件的观察者列表完全独立。
内容的提问来源于stack exchange,提问作者Placeblock
相关产品推荐
相关产品推荐

