观察者模式详解:用 Qt 信号与槽机制深入理解

news2025/3/19 11:34:58

引言

你是否曾遇到这样的需求:一个对象的状态发生变化后,希望通知其他对象进行相应的更新?比如:

  • 新闻订阅系统:当新闻发布后,所有订阅者都会收到通知。
  • 股票行情推送:股价变化时,所有关注该股票的投资者都会收到更新信息。
  • Qt 界面开发:按钮被点击时,窗口应该发生某些变化。

这些场景都适用于观察者模式(Observer Pattern)

在本篇文章中,我们不仅讲清楚观察者模式的结构,还会用 Qt 的信号与槽机制 来深入解析,让你真正理解这一模式的奥秘!


1. 什么是观察者模式?

观察者模式是一种 一对多 的设计模式,允许多个对象(观察者)监听某个对象(被观察者)的状态变化,并在变化时收到通知。

简单来说:

  • 被观察者(Subject):负责维护一个观察者列表,并在状态发生变化时通知所有观察者。
  • 观察者(Observer):接收被观察者的通知并做出相应反应。

现实例子:

场景被观察者(Subject)观察者(Observer)
微信公众号订阅公众号订阅的用户
股票市场股票关注股票的投资者
UI 界面按钮点击按钮(QPushButton监听点击的槽函数

2. 观察者模式的结构

观察者模式一般包括以下角色:

  1. Subject(被观察者)

    • 维护一个观察者列表(即谁在关注它)。
    • 当自身状态发生变化时,通知所有观察者。
  2. Observer(观察者)

    • 订阅 Subject,并实现 update() 方法,接收状态变化通知。
  3. 通知机制

    • Subject 需要提供 attach()notify() 方法,管理观察者并进行通知。

观察者模式 UML 结构图

+-------------+       +----------------+
|  Observer   |<------|    Subject     |
+-------------+       +----------------+
| +update()   |       | +attach()      |
|             |       | +detach()      |
|             |       | +notify()      |
+-------------+       +----------------+

3. 传统 C++ 实现观察者模式

在 C++ 中,我们可以用 vector 存储观察者列表,并手动通知它们:

传统 C++ 实现

#include <iostream>
#include <vector>

// 观察者接口
class Observer {
public:
    virtual void update(int value) = 0;
};

// 被观察者(Subject)
class Subject {
private:
    std::vector<Observer*> observers;
    int state;

public:
    void attach(Observer* observer) { observers.push_back(observer); }
    void notify() {
        for (Observer* obs : observers) {
            obs->update(state);
        }
    }

    void setState(int value) {
        state = value;
        notify();
    }
};

// 具体观察者
class ConcreteObserver : public Observer {
public:
    void update(int value) override {
        std::cout << "Observer received update: " << value << std::endl;
    }
};

int main() {
    Subject subject;
    ConcreteObserver observer1, observer2;

    subject.attach(&observer1);
    subject.attach(&observer2);

    subject.setState(42);  // 触发通知
}

问题:

  • 需要手动管理 Observer 的列表。
  • notify() 需要手动遍历所有观察者,不够灵活。
  • 可能会出现空指针问题(被观察者销毁后,观察者仍在使用)。

4. 用 Qt 信号与槽实现观察者模式

Qt 提供了一种更强大、更安全的实现方式——信号(Signal)与槽(Slot)机制。它本质上就是观察者模式的扩展,但更加灵活和易用!

在这里插入图片描述

信号与槽如何实现观察者模式?

观察者模式角色Qt 信号与槽对应
Subject(被观察者)QObject + Signal
Observer(观察者)QObject + Slot
通知机制connect()

Qt 实现观察者模式

#include <QObject>
#include <QDebug>

// 被观察者(Subject)
class Subject : public QObject {
    Q_OBJECT

signals:
    void valueChanged(int newValue);  // 信号(事件)

public:
    void setValue(int value) {
        emit valueChanged(value);  // 触发信号,通知观察者
    }
};

// 观察者(Observer)
class Observer : public QObject {
    Q_OBJECT

public slots:
    void onValueChanged(int newValue) {
        qDebug() << "Observer received new value:" << newValue;
    }
};

int main() {
    Subject subject;
    Observer observer;

    // 连接信号和槽
    QObject::connect(&subject, &Subject::valueChanged, &observer, &Observer::onValueChanged);

    // 触发事件
    subject.setValue(42);
}

Qt 信号与槽 vs 传统观察者模式

对比项传统观察者模式Qt 信号与槽
观察者管理方式需手动维护列表自动管理
触发方式直接调用 update()emit 发送信号
线程安全需要手动同步Qt::QueuedConnection 支持多线程
解除连接需要手动移除观察者disconnect() 轻松解绑

5. 信号与槽让观察者模式更强大

  1. 自动管理连接,避免空指针错误。
  2. 支持跨线程通信,不需要额外的同步机制。
  3. 更灵活:可以在运行时动态连接和解除连接。
  4. 更易读:代码清晰,不需要手动维护 std::vector<Observer*>

6. 观察者模式如何运用到实际开发中?

1. UI 界面更新

  • 当数据变化时,自动更新 UI 界面(如 QLabelQProgressBar)。
QObject::connect(&subject, &Subject::valueChanged, ui->label, &QLabel::setText);

2. 多线程任务通知主线程

  • 通过 Qt::QueuedConnection 让后台线程通知主线程刷新 UI。

3. 事件驱动开发

  • Qt 本身就是事件驱动的,信号与槽大幅减少了回调函数的使用。

7. 总结

  • 观察者模式解决了 对象间的事件通知 问题。
  • Qt 信号与槽机制 是观察者模式的高级实现,提供自动管理、类型安全、跨线程支持
  • 在 Qt 开发中,几乎所有 UI 组件、后台任务、事件响应都基于 信号与槽

你学会了吗? 🎯 如果有疑问,欢迎讨论! 🚀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2317765.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OSWorld:开启多模态智能体的真实计算机环境革命

OSWorld:开启多模态智能体的真实计算机环境革命 在人工智能技术突飞猛进的今天,多模态智能体正逐步突破实验室的限制,试图融入人类的日常工作场景。然而,如何评估这些智能体在真实计算机环境中处理开放式任务的能力,成为学术界和产业界共同关注的难题。2024年,由xlang-ai…

LabVIEW烟气速度场实时监测

本项目针对燃煤电站烟气流速实时监测需求&#xff0c;探讨了静电传感器结构与速度场超分辨率重建方法&#xff0c;结合LabVIEW多板卡同步采集与实时处理技术&#xff0c;开发出一个高效的烟气速度场实时监测系统。该系统能够在高温、高尘的复杂工况下稳定运行&#xff0c;提供高…

强化学习基础篇二:马尔可夫决策过程

写在前面 本文是对李沐等“动手学强化学习”教程的个人阅读总结&#xff0c;原文链接&#xff1a;动手学强化学习。 第3章 马尔可夫决策过程 3.1 重要性 马尔可夫决策过程是强化学习中的基础概念&#xff0c;强化学习中的环境就是一个马尔可夫决策过程&#xff0c;与多臂老虎…

EtherCAT转profinet网关集成汽车变速箱制造生产线自动化升级

客户的汽车零部件制造商需要升级其变速箱齿轮加工生产线&#xff0c;面临的关键挑战是整合新引进的欧洲齿轮精密检测设备&#xff08;基于EtherCAT协议&#xff09;与现有使用profinet协议自动化系统通信。 企业核心控制平台基于西门子PLC&#xff0c;而现场各工位采用分布式I/…

tongweb7控制台无法访问

tongweb7控制台无法访问 排查 1.首先确认版本&#xff0c;如果版本是轻量级版本&#xff0c;轻量版不支持会话(session)的备份和复制、管理控制台、APM 运维工具等企业级增量功能。 2.查看端口 命令&#xff1a;ss -tnlp 或者netstat -tnlp 确认控制台端口是否开启 3.在conf…

【STM32】从新建一个工程开始:STM32 新建工程的详细步骤

STM32 开发通常使用 Keil MDK、STM32CubeMX、IAR 等工具来创建和管理工程。此处是 使用 Keil MDK5 STM32CubeMX 创建 STM32 工程的详细步骤。 新建的标准库工程文件已上传至资源中&#xff0c;下载后即可直接使用。 标准库新建 STM32 工程的基本目录结构&#xff1a;STD_STM…

搞定python之九----常用内置模块

本文是《搞定python》系列文章的第九篇&#xff0c;介绍常用的内置模块的使用。到此为止python的基础用法就彻底说完了&#xff0c;大家可以在此基础上学习爬虫、web处理等框架了。 本文的代码相对比较多&#xff0c;大家注意看代码即可。python的文档我贴出来&#xff0c;毕竟…

判断是不是完全二叉树(C++)

目录 1 问题描述 1.1 示例1 1.2 示例2 1.3 示例3 2 解题思路 3 代码实现 4 代码解析 4.1 定义队列&#xff0c;初始化根节点 4.2 层序遍历&#xff0c;处理每个节点 4.3 处理空节点 4.4 处理非空节点 5 总结 1 问题描述 给定一个二叉树&#xff0c;确定他是否是一…

神经外科手术规划的实现方案及未来发展方向

Summary: 手术规划软件 效果图&#xff0c;样例&#xff1a; 神经外科手术规划样例&#xff1a; 神经外科手术规划&#xff0c;三维重建&#xff0c;三维建模&#xff0c;三维可视化 Part1: 手术规划的定义与作用 一、手术规划的定义 手术规划是指在手术前&#xff0c;通过详…

easypoi导入Excel兼容日期和字符串格式的日期和时间

问题场景 在使用easypoi导入Excel时&#xff0c;涉及到的常用日期会有yyyy-MM-dd HH:mm:ss、yyyy-MM-dd和HH:mm:ss&#xff0c;但是Excel上面的格式可不止这些&#xff0c;用户总会输入一些其他格式&#xff0c;如 如果在定义verify时用下面这种格式定义&#xff0c;那么总会…

【计算机视觉】工业表计读数(2)--表计检测

1. 简介 工业表计&#xff08;如压力表、电表、气表等&#xff09;在工控系统、能源管理等领域具有重要应用。然而&#xff0c;传统人工抄表不仅工作量大、效率低&#xff0c;而且容易产生数据误差。近年来&#xff0c;基于深度学习的目标检测方法在工业检测中展现出极大优势&…

Zbrush插件安装

安装目录在: ...\Zbrush2022\ZStartup\ZPlugs64

LeRobot源码剖析——对机器人各个动作策略的统一封装:包含ALOHA ACT、Diffusion Policy、VLA模型π0

前言 过去2年多的深入超过此前7年&#xff0c;全靠夜以继日的勤奋&#xff0c;一天当两天用&#xff0c;抠论文 抠代码 和大模型及具身同事讨论&#xff0c;是目前日常 而具身库里&#xff0c;idp3、π0、lerobot值得反复研究&#xff0c;故&#xff0c;近期我一直在抠π0及l…

OpenCV基础【图像和视频的加载与显示】

目录 一.创建一个窗口&#xff0c;显示图片 二.显示摄像头/多媒体文件 三.把摄像头录取到的视频存储在本地 四.鼠标回调事件 五.TrackBar滑动条 一.创建一个窗口&#xff0c;显示图片 import cv2img_path "src/fengjing.jpg" # 自己的图片路径 img cv2.imre…

杨校老师课堂之编程入门与软件安装【图文笔记】

亲爱的同学们&#xff0c;热烈欢迎踏入青少年编程的奇妙世界&#xff01; 我是你们的授课老师杨校 &#xff0c;期待与大家一同开启编程之旅。 1. 轻松叩开编程之门 1.1 程序的定义及生活中的应用 程序是人与计算机沟通的工具。在日常生活中&#xff0c;像手机里的各类 APP、电…

Excel(函数篇):IF函数、FREQUNCY函数、截取函数、文本处理函数、日期函数、常用函数详解

目录 IF函数等于判断区间判断与AND函数、OR函数一同使用IFNA函数和IFERROR函数 FREQUNCY函数、分断统计LEFT、RIGHT、MID截取函数FIND函数、LEN函数SUBSTITUTE函数ASC函数、WIDECHAR函数实战&#xff1a;如何获取到表中所有工作簿名称文本处理函数TEXT函数TEXTJOIN函数 日期函数…

利用大语言模型生成的合成数据训练YOLOv12:提升商业果园苹果检测的精度与效

之前小编分享过关于《YOLO11-CBAM集成&#xff1a;提升商业苹果园树干与树枝分割的精准度》&#xff0c;改进YOLO11算法后&#xff0c;进行苹果树的实例分割。本期文章我们将分享关于最新的YOLO12算法改进的苹果目标检测。 论文题目&#xff1a;Improved YOLOv12 with LLM-Gen…

整合百款经典街机游戏的模拟器介绍

对于80、90后而言&#xff0c;街机游戏承载着童年的欢乐记忆。今天要给大家介绍一款超棒的软件——「MXui街机厅经典游戏101款」&#xff0c;它能带你重回那段热血沸腾的街机时光。 「MXui街机厅经典游戏101款」是一款绿色免安装的街机模拟器&#xff0c;体积约1.39G。无需繁琐…

中小型企业大数据平台全栈搭建:Hive+HDFS+YARN+Hue+ZooKeeper+MySQL+Sqoop+Azkaban 保姆级配置指南

目录 背景‌一、环境规划与依赖准备‌1. 服务器规划(3节点集群)2. 系统与依赖‌3. Hadoop生态组件版本与下载路径4. 架构图二、Hadoop(HDFS+YARN)安装与配置‌1. 下载与解压(所有节点)2. HDFS高可用配置3. YARN资源配置‌4. 启动Hadoop集群三、MySQL安装与Hive元数据配置…

Tomcat、Open Liberty 和 WebSphere Application Server (WAS) 的配置、调试和跟踪

一、Tomcat Tomcat 是一个轻量级的开源 Java Servlet 容器。 1、配置 Tomcat 的主要配置文件位于其安装目录下的 conf 文件夹中。 server.xml: 这是 Tomcat 的核心配置文件&#xff0c;包含了服务器的基本设置&#xff0c;例如端口号、连接器配置、虚拟主机配置、以及全局的…