c++ 之观察者模式

观察者模式(Observer Pattern)一次性讲透,C++版,适配你Qt工控开发场景

1、一句话定义

观察者模式:一对多的依赖关系。当「主题(被观察对象)」状态发生变化时,自动通知所有「观察者」,观察者收到通知后自行做处理。

通俗例子:

主题 = 温度传感器;观察者 = 显示屏、报警模块、日志模块。

传感器温度一变,自动推消息给显示屏刷新数值、超温就触发报警、同时写入日志。传感器不需要硬编码调用显示屏/报警的函数,解耦。

两个核心角色:

  1. Subject 主题(被观察者)

◦ 维护观察者列表

◦ 提供接口:增加观察者、移除观察者、通知全部观察者

  1. Observer 观察者

◦ 统一接口 update(),主题通知时,调用这个函数更新自己

核心思想:主题只知道观察者实现了统一接口,不关心观察者具体是谁、做什么业务,实现解耦。

2、适用场景(工控Qt项目,重点)

✅适合:

  1. 一个数据变化,多个模块需要同步更新(比如:设备状态变更 → UI界面刷新、日志记录、告警推送、DDS上报),你做的想定编辑工具非常适合这个场景

  2. 事件广播,发布订阅,模块之间不想强依赖(A模块代码里不写B模块头文件)

  3. 运行时动态增加/删除观察者(比如插件,加载插件就注册观察者,卸载就取消注册)

❌不适合:

  1. 观察者数量巨大、通知频繁,性能压力高;

  2. 简单单次调用,没必要上这套模式,过度设计。

3、基础C++最简代码(纯C++,无Qt)

#include <iostream>

#include <vector>

#include <memory>

// 观察者抽象基类

class Observer {

public:

virtual ~Observer() = default;

// 收到通知的回调接口

virtual void update(int state) = 0;

};

// 主题(被观察者)抽象基类

class Subject {

public:

virtual ~Subject() = default;

// 注册观察者

void attach(std::shared_ptr<Observer> obs) {

observers.push_back(obs);

}

// 移除观察者

void detach(std::shared_ptr<Observer> obs) {

for(auto it = observers.begin(); it != observers.end();)

{

if(*it == obs)

it = observers.erase(it);

else

++it;

}

}

// 通知所有观察者

void notify(int newState) {

for(auto &obs : observers)

{

obs->update(newState);

}

}

private:

std::vector<std::shared_ptr<Observer>> observers;

};

// 具体主题:设备数据源

class DeviceSubject : public Subject {

private:

int deviceState = 0;

public:

void setState(int s) {

deviceState = s;

notify(s); // 状态修改,自动广播通知所有观察者

}

};

// 具体观察者1:UI界面

class UiObserver : public Observer {

public:

void update(int state) override {

std::cout << "UI刷新,设备状态:" << state << "\n";

}

};

// 具体观察者2:告警模块

class AlarmObserver : public Observer {

public:

void update(int state) override {

if(state > 80)

std::cout << "告警!设备异常!\n";

}

};

int main()

{

auto device = std::make_shared<DeviceSubject>();

auto ui = std::make_shared<UiObserver>();

auto alarm = std::make_shared<AlarmObserver>();

// 注册

device->attach(ui);

device->attach(alarm);

// 修改状态,自动通知所有观察者

device->setState(50);

device->setState(90);

// 取消注册,告警不再接收消息

device->detach(alarm);

device->setState(40);

return 0;

}

4、Qt里面的观察者模式:信号槽(重点!你工控开发天天在用)

Qt的 Signal & Slot 本质就是观察者模式!

• QObject(主题):emit信号 emit stateChanged(val)

• Slot槽函数(观察者update):收到信号执行回调

对比:

• 原生观察者:手动维护观察者vector,循环调用update

• Qt信号槽:Qt框架底层帮你管理订阅列表,线程安全版本还支持跨线程信号槽

// Qt信号槽等价于观察者

class Device : public QObject

{

Q_OBJECT

public:

void setValue(int v){

m_val = v;

emit valueChanged(m_val); // 通知所有订阅者

}

signals:

void valueChanged(int);

private:

int m_val = 0;

};

class UiWidget : public QObject

{

Q_OBJECT

public slots:

void onValueUpdate(int v){

qDebug()<<"UI更新 "<<v;

}

};

// 使用:connect 就是 attach注册;disconnect就是detach取消订阅

// connect(device, &Device::valueChanged, ui, &UiWidget::onValueUpdate);

面试高频点:Qt信号槽是观察者模式的一种实现。

5、关键坑点(工控项目踩坑重点,面试必考)

  1. 循环引用问题

用裸指针:忘记detach,主题销毁了,观察者还在,野指针;

shared_ptr:互相持有,内存泄漏。

✅解决:weak_ptr存观察者列表,不要全部shared_ptr。

  1. 通知顺序不可控

遍历vector通知,观察者执行顺序是注册顺序;如果业务强依赖先后顺序,这个模式不合适。

  1. 同步阻塞

默认是同步调用:notify的时候,逐个调用update,一个观察者卡很久,后面全部阻塞。

✅工控方案:异步队列(把回调丢线程池),避免一个模块卡死整个通知链路。

  1. 一次性通知/增量数据

默认只会推送变化,新注册的观察者拿不到当前的旧状态。

✅解决方案:attach注册的时候,主动把当前state传给新观察者。

  1. 多线程场景:主题状态修改+notify,观察者增删列表,必须加锁,不然vector遍历过程中增删元素直接崩溃(工控高频bug来源)。

6、面试高频问答(直接背)

Q:观察者模式解决了什么?

A:实现主题和观察者解耦,主题不需要硬编码依赖各个业务模块,新增观察者不需要修改主题代码,符合开闭原则。

Q:缺点是什么?

  1. 通知默认同步阻塞;

  2. 多线程下要处理锁;

  3. 观察者不知道其他观察者的存在;

  4. 大量观察者时通知耗时。

Q:和发布订阅(DDS)区别?

普通观察者:进程内,内存直接调用;

DDS发布订阅:跨进程/跨机器,网络层面的观察者模式,中间有数据总线。

7、一句话收尾总结

观察者 = 一对多事件广播,状态一变自动通知所有订阅模块;Qt信号槽就是它的工程化实现,DDS是跨设备的分布式观察者。

相关推荐
坚定学代码1 小时前
static在c++中的使用
开发语言·c++
hansang_IR2 小时前
【题解】P5364 [SNOI2017] 礼物
c++·线性代数·算法
无小道2 小时前
C/C++——tuple小记
c++·tuple
上官唐云2 小时前
2024届本科 | C++ Linux服务端开发求职:从嵌入式到底层服务端的技术之路(附开源项目+简历)
linux·c++·开源·求职招聘
蒸蒸yyyyzwd3 小时前
面经学习笔记
c++·面试·八股
影视飓风TIM3 小时前
C++11 新特性:Lambda表达式、std::function与std::bind
开发语言·c++
欧特克_Glodon4 小时前
OpenCV计算机视觉开发入门与实践<三十六>:机器学习概述
c++·opencv·机器学习·计算机视觉
小七在进步4 小时前
C++入门(3)
android·java·c++
码匠许师傅4 小时前
【设计模式精讲】27.模板方法模式(Template Method)
c++·设计模式·uml·模板方法模式