正常
阶段 1:纯 C++ Observer 接口 + ClassicSubject
目标
搓出 GoF 教科书版的观察者:一个 Observer 接口(纯虚 onUpdate)+ 一个 ClassicSubject(维护 vector<Observer*>,提供 attach/detach/notify)。再搓一个 TextObserver 把收到的温度写进一个 QPlainTextEdit。这一步完全不碰 Qt 信号槽——它是后面 Qt 路径的对照基准。
提示
Observer放AwesomeQt::命名空间,只一个virtual void onUpdate(double) = 0,虚析构给default(接口必备)ClassicSubject不继承任何东西(纯 C++ 类),成员std::vector<Observer*> observers_——裸指针,不持有所有权attach(Observer*):push_back之前防御一下空指针detach(Observer*):std::find+erase,只删第一个匹配(GoF 语义)notify(double):range-based for 遍历,逐个observer->onUpdate(temp)——禁 Q_FOREACHTextObserver : public Observer,构造接一个QPlainTextEdit*存成员,onUpdate里appendPlainText- demo 里
new一个ClassicSubject(用std::unique_ptr持)、new一个TextObserver(传面板指针),subject->attach(observer.get())
检查点
手动调 classic_subject_->notify(42.5),面板 D 里出现一行 [classic] temperature = 42.50 = 通知链通了。 然后 detach 再 notify,面板不再新增行 = 注销对了。 关键体会:你现在手写了 attach/detach/notify 全套——记住这个工作量,下一阶段看 Qt 怎么一行 emit 替代掉它。
信号槽 / 虚函数回调机制不熟?先读 信号与槽。
对照答案
Observer纯虚接口:include/observer-pattern.h:28ClassicSubject三件套声明:include/observer-pattern.h:47/51/55attach/detach/notify实现:src/observer-pattern.cpp:17/24/32notify的裸指针 for 循环(悬空风险点):src/observer-pattern.cpp:35- demo 里 attach + 手动 notify:
demo/observer-pattern_window.cpp:49与demo/observer-pattern_window.cpp:246 TextObserver::onUpdate:demo/observer-pattern_window.cpp:29
这一阶段的坑(必踩一个)
- detach 忘了调,observer 先析构:
notify遍历到悬空指针 → segfault。纯 C++ 路径没有 Qt 对象树那种自动失效,必须严格配对 attach/detach。成品在析构前没 detach 是因为unique_ptr成员析构顺序保证了 subject 先于 observer 销毁——但这是个脆平衡,别在生产代码里赌。