QT5.14.2 探秘Qt信号槽奥秘--让对象间通信如虎添翼

2024-03-19 16:12

本文主要是介绍QT5.14.2 探秘Qt信号槽奥秘--让对象间通信如虎添翼,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


一、前言


在当今这个万物互联的时代,对象间通信无疑是编程领域中最为基础也最为重要的问题。作为知名的跨平台开发框架,Qt自然也需要解决这一问题。于是,Qt巧妙地提出了信号与槽(Signals & Slots)这一机制,以观察者模式的思路让对象间通信变得行云流水。那么,Qt信号与槽的本质是什么?它是如何在底层实现的?又有哪些实战应用技巧?本文将为您一一道来。


二、Qt信号槽本质剖析


1、Qt信号槽的核心思想

  • 信号(Signal)作为对象状态或行为变化的通知;

  • 槽(Slot)作为相应该通知的处理函数;

  • 通过Connect将信号与槽关联,使通知能精准触达指定槽函数。

从语法上看,Qt通过自创的signals、slots和emit等关键字模拟了信号与槽:


signals: // 声明信号void valueChanged(int newValue);public slots: //声明槽函数void valueChangedHandler(int value); //发射信号 
emit valueChanged(25);

2、信号和槽的语法规则

  • 信号必须用signals关键字声明在类定义中

  • 信号没有函数体实现

  • 槽可以是普通的成员函数,或用slots关键字声明

  • 只有继承自QObject的类才能定义信号和槽

  • 只有在QObject子类实例化后才能使用信号和槽机制


3、信号与槽之间的关系

在这里插入图片描述


通过这些规则和关系,Qt构建了一个高度灵活可扩展的信号与槽通信机制。


三、信号槽原理揭秘

那么信号和槽在底层又是如何实现的呢?原理如下:

  1. Qt的moc(Meta-Object Compiler元对象编译器)会扫描通过Q_OBJECT宏声明的类,为其生成元对象数据(elements/meta-object.cpp等)

  2. 该元数据包含了信号与槽的名称、参数等完整信息

  3. 对象在运行时,根据这些元数据查找并调用目标槽函数

  4. 通过QObject::connect()函数将信号与槽关联起来:


QObject::connect(sender, signal, receiver, slot)

其中:

  • sender是发送信号的对象
  • signal是发送的信号,使用&Sender_Class::signal语法指定
  • receiver是接收信号的对象
  • slot是要调用的槽函数,使用&Receiver_Class::slot语法指定

Connect()函数一共有多种重载形式,支持使用函数指针或者字符串的方式指定信号和槽。

让我们看一个实例:

// 定义发送者和接收者对象
QPushButton *button = new QPushButton;
QDialog *dialog = new QDialog;// 使用函数指针方式连接信号与槽
QObject::connect(button, &QPushButton::clicked,dialog, &QDialog::accept);// 使用字符串方式连接信号与槽
QObject::connect(button, SIGNAL(clicked()),dialog, SLOT(accept()));

上例中,当按钮被点击时,就会发射clicked()信号,进而触发QDialog的accept()槽函数被调用。

需要注意的是,使用函数指针的方式更加安全,支持参数隐式转换以及自定义类型,而字符串方式则需要确保参数名称和顺序完全一致。

由此可见,QObject::connect()是将发送者对象的信号绑定到接收者对象的槽函数上,从而建立两者间的通信通道。这正是Qt信号与槽大显身手的核心所在。

因此,Qt信号槽的本质就是一种基于元数据的动态绑定机制。相比函数指针,它更加智能且自动化。这种设计使得Qt极大提升了多态、可扩展性等面向对象特性,成为当代C++大作的有力支撑。


四、代码实战:订单监控器

理论说了那么多,我们还是通过一个实战案例来印证一下信号槽的魔力。

比如我们需要开发一款订单状态监控器:当订单状态发生变化时,自动记录日志、发送通知等。

这种监听机制,正好可以利用信号槽来实现:

//订单类
class Order : public QObject
{Q_OBJECTpublic:enum Status {Open, Resolved, Closed};Order(QObject *parent = nullptr): QObject(parent), m_status(Open){}void setStatus(Status status){if (status != m_status) {m_status = status;emit statusChanged(status); //发射信号}}signals:void statusChanged(Order::Status newStatus);private:Status m_status;
};//监控器类
class Monitor : public QObject
{Q_OBJECTpublic:Monitor(QObject *parent = nullptr): QObject(parent){Order *order = new Order(this);connect(order, &Order::statusChanged, //连接信号与槽this, &Monitor::logStatus);order->setStatus(Order::Resolved); //状态变化,触发信号order->setStatus(Order::Closed); }private slots:void logStatus(Order::Status status){static const char *statusStr[] = {"Order Open","Order Resolved","Order Closed"};std::cout << statusStr[status] << std::endl;}
};

在上面的示例中,我们首先定义了Order类表示订单,它有一个statusChanged信号,在订单状态变化时会发射该信号。

然后定义了Monitor类作为监控器,在构造函数中:

  1. 创建Order对象
  2. 使用connect()函数连接Order对象的statusChanged信号与Monitor的logStatus槽函数
  3. 改变Order状态,触发信号发射,从而调用logStatus输出日志

其中,关键的连接代码是:

connect(order, &Order::statusChanged, this, &Monitor::logStatus);

这一语句就将order对象的statusChanged信号与this(Monitor实例)的logStatus槽绑定到了一起。

这样,只要订单状态改变,statusChanged信号就会被触发,Monitor的logStatus槽函数就会被自动调用,输出相应的日志,而不需要手动去调用。整个过程是自动完成的。

通过这个例子,我们可以看到Qt信号槽机制让对象间的监听与通信变得非常简单直观,大大增强了代码的可维护性和扩展性。


五、实战技巧剖析

通过上面的例子,我们可以总结出Qt信号槽的以下实战技巧:

  1. Qt分离了事件主体和监听器的耦合,增强了可维护性
  2. 信号槽允许参数传递,方便数据共享
  3. 一个信号可连接多个槽,一个槽也可被多个信号调用,扩展性强
  4. 信号可自动执行,不需要手动调用,代码更加简洁
  5. 使用lambda表达式连接信号槽,代码更紧凑
  6. 信号可过滤,根据条件选择是否执行槽
  7. Qt内置许多常用信号,方便快速集成

六、未来展望

本文从本质到实现,为您讲解了Qt信号槽的方方面面。相信通过这个机制,您对面向对象编程的理解会有进一步的提升。作为Qt框架的核心机制,信号与槽不仅让对象间通信变得行云流水,更是彰显了C++语言强大的可扩展性。通过Qt信号槽,我们可以写出更优雅、更易维护的面向对象代码。信号就是力量,与槽实现无限可能,期待您的大显身手!

这篇关于QT5.14.2 探秘Qt信号槽奥秘--让对象间通信如虎添翼的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/826557

相关文章

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

无线路由器哪个品牌好用信号强? 口碑最好的三个路由器大比拼

《无线路由器哪个品牌好用信号强?口碑最好的三个路由器大比拼》不同品牌在信号覆盖、稳定性和易用性等方面各有特色,如何在众多选择中找到最适合自己的那款无线路由器呢?今天推荐三款路由器让你的网速起飞... 今天我们来聊聊那些让网速飞起来的路由器。在这个信息爆炸的时代,一个好路由器简直就是家庭网编程络的心脏。无论你

电脑显示hdmi无信号怎么办? 电脑显示器无信号的终极解决指南

《电脑显示hdmi无信号怎么办?电脑显示器无信号的终极解决指南》HDMI无信号的问题却让人头疼不已,遇到这种情况该怎么办?针对这种情况,我们可以采取一系列步骤来逐一排查并解决问题,以下是详细的方法... 无论你是试图为笔记本电脑设置多个显示器还是使用外部显示器,都可能会弹出“无HDMI信号”错误。此消息可能

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

python与QT联合的详细步骤记录

《python与QT联合的详细步骤记录》:本文主要介绍python与QT联合的详细步骤,文章还展示了如何在Python中调用QT的.ui文件来实现GUI界面,并介绍了多窗口的应用,文中通过代码介绍... 目录一、文章简介二、安装pyqt5三、GUI页面设计四、python的使用python文件创建pytho

vue如何监听对象或者数组某个属性的变化详解

《vue如何监听对象或者数组某个属性的变化详解》这篇文章主要给大家介绍了关于vue如何监听对象或者数组某个属性的变化,在Vue.js中可以通过watch监听属性变化并动态修改其他属性的值,watch通... 目录前言用watch监听深度监听使用计算属性watch和计算属性的区别在vue 3中使用watchE

Java将时间戳转换为Date对象的方法小结

《Java将时间戳转换为Date对象的方法小结》在Java编程中,处理日期和时间是一个常见需求,特别是在处理网络通信或者数据库操作时,本文主要为大家整理了Java中将时间戳转换为Date对象的方法... 目录1. 理解时间戳2. Date 类的构造函数3. 转换示例4. 处理可能的异常5. 考虑时区问题6.

QT实现TCP客户端自动连接

《QT实现TCP客户端自动连接》这篇文章主要为大家详细介绍了QT中一个TCP客户端自动连接的测试模型,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录版本 1:没有取消按钮 测试效果测试代码版本 2:有取消按钮测试效果测试代码版本 1:没有取消按钮 测试效果缺陷:无法手动停

基于Qt实现系统主题感知功能

《基于Qt实现系统主题感知功能》在现代桌面应用程序开发中,系统主题感知是一项重要的功能,它使得应用程序能够根据用户的系统主题设置(如深色模式或浅色模式)自动调整其外观,Qt作为一个跨平台的C++图形用... 目录【正文开始】一、使用效果二、系统主题感知助手类(SystemThemeHelper)三、实现细节

Qt实现文件的压缩和解压缩操作

《Qt实现文件的压缩和解压缩操作》这篇文章主要为大家详细介绍了如何使用Qt库中的QZipReader和QZipWriter实现文件的压缩和解压缩功能,文中的示例代码简洁易懂,需要的可以参考一下... 目录一、实现方式二、具体步骤1、在.pro文件中添加模块gui-private2、通过QObject方式创建