Qt6入门教程 7:信号和槽机制(原理和优缺点)

目录

一.简介

二.信号和槽

1.信号和槽机制是类型安全的

2.信号和槽是松散耦合的

三.信号(signals)

四.槽(slots)

五.信号与槽的简单模拟

六.第三方信号槽实现

七.在Qt中使用第三方的Signals和Slots

八.总结一下优点和缺点

1.优点

2.缺点


信号和槽用于对象之间的通信。信号和槽机制是Qt的核心机制,也是Qt不同于其他框架的最突出的特征。Qt的元对象系统使信号和槽成为可能。

一.简介

在GUI编程中,当我们改变一个控件,通常希望其他控件被通知到。更一般的,我们希望任意对象之间能够通信。例如,如果我们点击了“关闭”按钮,我们希望窗口的close()函数被调用。
其他工具包使用回调来实现这种通信。回调函数是一个指向函数的指针,所以如果你想要一个处理函数通知你一些事件,你可以将一个指向另一个函数(回调函数)的指针传递给处理函数。处理函数然后在适当的时候调用回调函数。但回调可能不太直观,而且在确保回调参数的类型正确性方面可能会遇到问题。

二.信号和槽

在Qt中,我们有一个回调技术的替代方案:我们使用信号和槽。当特定事件发生时发出信号。Qt的控件有许多预定义的信号,但是我们总是可以子类化控件,添加一些自定义信号。槽(slots)是响应特定信号(signals)而调用的函数。Qt的控件有许多预定义的槽,但是通常的做法是子类化控件并自定义槽函数,这样就可以处理感兴趣的信号。
下图为信号与槽的关系图:支持一对多,多对一

1.信号和槽机制是类型安全的

信号与槽机制要求信号和槽的参数一致,所谓一致,是参数类型一致。如果不一致,允许的情况是,信号的参数可以比槽函数的参数多,此时槽可以忽略多余参数。由于参数的一致性,所以当使用基于函子(functor-base)的信号与槽语法时,编译器可以帮助我们检测类型是否匹配(Qt5开始支持的语法);基于字符串(string-base)的信号和槽语法将在运行时检测类型不匹配(Qt4开始支持的语法)。

注:functor(函子)是C++编程语言中的一个概念,它允许将一个可调用对象(函数、函数指针、成员函数指针等)以及其相关的状态(变量、成员变量等)封装起来,形成一个对象,这个对象可以像函数一样被调用。

2.信号和槽是松散耦合的

信号和槽是松散耦合的,发出信号的类既不知道也不关心哪个槽接收信号。Qt的信号和槽机制确保,如果您将信号连接到槽,将在正确的时间使用信号的参数调用该槽。信号和槽可以采用任意数量的任何类型的参数。它们完全是类型安全的。
从QObject或其子类(例如QWidget)继承的所有类都可以包含信号和槽。当对象的状态发生变化时,它们会发出信号,它不知道或不关心是否有任何槽在接收它发出的信号,这足以实现信息封装。
槽可以用于接收信号,但它们也是正常的成员功能。就像一个对象不知道是否有任何其他对象接收到它的信号一样,槽也不知道它是否有任何信号连接到它。这确保了可以使用Qt创建真正独立的组件。
您可以将任意多个信号连接到一个槽,并且可以将信号连接到任意多个槽。甚至可以将一个信号直接连接到另一个信号。(每当发出第一个信号时,这将立即发出第二个信号。)
信号和槽共同构成了强大的组件编程机制。

三.信号(signals)

当对象内部状态以对象的客户端或用户感兴趣的某种方式发生变化时—比如点击、鼠标移动等,对象就会发出信号。信号(signals)都是共有(public)的,可以从任何地方发出,但最好只从该定义信号的类及其子类使用该信号。当信号发出时,通常采用直连方式连接槽函数,这种连接方式会立即执行槽函数,就像普通的函数调用一样。此时信号和槽机制完全独立于任何GUI事件循环。一旦所有的槽都返回,emit语句之后的代码就会执行(同步发送)。当使用队列连接时,情况略有不同;在这种情况下,emit关键字后面的代码将立即继续,槽将稍后执行(异步发送)。如果多个槽连接到一个信号,当信号发出时,槽将按照它们连接的顺序依次执行。信号是由moc(元对象编译器)自动生成的,在build目录中的moc_**文件中。信号永远不能有返回类型(即使用void)。

四.槽(slots)

槽在信号发出时被调用。槽也是C++函数,可以像普通函数一样正常调用,它们唯一的特点是可以连接信号。因为槽函数是普通的成员函数,所以当直接调用时,它们遵循普通的C++规则。与回调相比,信号和槽稍微慢一些,因为它们提供了更大的灵活性,尽管实际应用程序中的差异并不大。一般来说,emit一个连接到一些槽的信号,大约比直接调用非虚函数慢十倍。原因是在定位连接对象、安全遍历所有连接(即检查在发送过程中后续槽函数是否被销毁)等所需的开销。虽然十个非虚函数调用可能听起来很多,但它的开销比任何new或delete操作都要小得多。一旦在后续执行需要new或delete字符串、vector or list等操作,信号和槽开销只占整个函数调用开销的很小一部分。信号和槽机制的简单性和灵活性是非常值得的,用户甚至不会注意到这些开销。
注:当定义了signals或slots变量的第三方库与基于qt的应用程序一起编译时,可能会导致编译器警告和错误。要解决这个问题,请#undef有问题的预处理器符号

五.信号与槽的简单模拟

#include <string>
#include <map>
#include <functional>
#include <iostream>
// 这里是以字符串“CaoShangPa”作为连接信号和槽的媒介
class Connections
{
public:// 按名称建立映射关系void connect(const std::string &name, const std::function<void()> &callback){m_callbackMap[name] = callback;}// 按名称调用void invoke(const std::string &name){auto it = m_callbackMap.find(name);if (it != m_callbackMap.end()) {it->second();}}
private:std::map<std::string, std::function<void()>> m_callbackMap;
};static Connections g_connections;class Signal
{
public:void send(){std::cout << "I am signal" << std::endl;// 调用名字为CaoShangPa的回调g_connections.invoke("CaoShangPa");}
};class Slot
{
public:Slot(){// 构造函数中,建立映射关系g_connections.connect("CaoShangPa", std::bind(&Slot::recieve, this));}void recieve(){std::cout << "I am slot" << std::endl;}
};int main(int argc, char *argv[])
{Signal signal;Slot slot;// 此时signal并未直接调用slot,却能打印出I am slotsignal.send();return 0;
}

六.第三方信号槽实现

信号-槽是非常优秀的通信机制,但Qt的moc实现方式,被一些人诟病,所以他们造了新的轮子,比如:
https://woboq.com/blog/verdigris-qt-without-moc.html
http://sigslot.sourceforge.net/
https://github.com/NoAvailableAlias/nano-signal-slot
https://github.com/pbhogan/Signals

七.在Qt中使用第三方的Signals和Slots

可以将Qt与第三方信号/槽机制一起使用。你甚至可以在同一项目中使用这两种机制。只需将以下行添加到qmake项目(.pro)文件中。

CONFIG += no_keywords

它告诉Qt不要定义moc关键字signals、slot和emit,因为这些名称将由第三方库使用,例如Boost。然后,要继续使用带有no_keywords标志的Qt信号和槽,只需将源代码中Qt moc所使用的关键字替换为相应的Qt宏Q_SIGNALS(或Q_SIGNAL)、Q_SLOTS(或Q_SLOT)和Q_EMIT。
以上内容参考:Qt Assistant—>Signals & Slots

八.总结一下优点和缺点

1.优点

①类型安全。
②松散耦合。信号和槽机制减弱了Qt对象的耦合度。发送信号的Qt对象无需知道是那个对象的那个信号槽接收它发出的信号,它只需在适当的时间发送适当的信号即可。Qt可以保证了适当的槽得到了调用,即使关联的对象在运行时被删除。程序也不会奔溃。
③灵活性。一个信号可以关联多个槽,或多个信号关联同一个槽。

2.缺点

速度较慢。与回调函数相比,信号和槽机制运行速度比直接调用非虚函数慢10倍。
原因:
①需要定位接收信号的对象。
②安全地遍历所有关联槽。
③编组、解组传递参数。
④多线程的时候,信号需要排队等待。
然而,与创建对象的new操作及删除对象的delete操作相比,信号和槽的运行代价只是他们很少的一部分。信号和槽机制导致的这点性能损耗,对实时应用程序是可以忽略的。

原文链接:Qt6入门教程 7:信号和槽机制(原理和优缺点)-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构:顺序栈

栈是一种先进后出的数据结构&#xff0c;只允许在一端&#xff08;栈顶&#xff09;操作&#xff0c;代码中top表示栈顶。 stack.h /* * 文件名称&#xff1a;stack.h * 创 建 者&#xff1a;cxy * 创建日期&#xff1a;2024年01月17日 * 描 述&#xff1a; …

Spring MVC的原理

Spring MVC中的MVC即模型-视图-控制器&#xff0c;该框架围绕一个DispatcherServlet设计而成&#xff0c;DispatcherServlet会把请求分发给各个处理器&#xff0c;并支持可配置的处理器映射和视图渲染等功能。Spring MVC的具体工作流程如下&#xff1a; &#xff08;1&#xff…

C——语言内存函数

目录 一、memcpy的使用和模拟实现 1.memcpy函数原型 2.memcpy函数的使用 3.memcpy函数的模拟实现 二、memmove的使用和模拟实现 1.memmove函数原型 2.memmove函数的使用 3.memmove函数的模拟实现 三、memset的使用 1.memset函数原型 2.memset函数的使用 四、memcmp…

bash shell基础命令(一)

1.shell启动 shell提供了对Linux系统的交互式访问&#xff0c;通常在用户登录终端时启动。系统启动的shell程序取决于用户账户的配置。 /etc/passwd/文件包含了所有用户的基本信息配置&#xff0c; $ cat /etc/passwd root:x:0:0:root:/root:/bin/bash ...例如上述root账户信…

Python新手常见问题——列表中删不掉的0

1.测试代码 运行代码 nums1 [1,2,3,0,0,0] print(type(nums1))for i in nums1:if i 0:nums1.remove(i) print(nums1)效果如下 2.疑问&#xff1a; 上面代码&#xff0c;为什么把nums1里面的0移除不干净 3.原因&#xff1a; 在 Python 中&#xff0c;不建议在循环中直接…

修改SSH默认端口,使SSH连接更安全

以CentOS7.9为例&#xff1a; 1、修改配置文件 vi /etc/ssh/sshd_config 2、远程电脑可连接&#xff0c;暂时将SELinux关闭 # 查询状态 getenforce # 关闭 setenforce 0 # 开启 setenforce 1 3、SELinux设置&#xff08;如果启用&#xff09;&#xff0c;semanage管理工具安…

mac上部署单体hbase

1. 简介 HBase 是一个开源的、分布式的、版本化的典型非关系型数据库。它是 Google BigTable 的开源实现&#xff0c;并且是 Apache 基金会的 Hadoop 项目的一部分1。HBase 在 Hadoop Distributed File System (HDFS) 上运行&#xff0c;作为一个列式存储非关系数据库管理系统…

Angular系列教程之变更检测与性能优化

文章目录 前言变更检测的原理脏检查OnPush策略 示例代码总结 前言 Angular 除了默认的变化检测机制&#xff0c;也提供了ChangeDetectionStrategy.OnPush&#xff0c;用 OnPush 可以跳过某个组件或者某个父组件以及它下面所有子组件的变化检测。 在本文中&#xff0c;我们将探…

vscode显示120字符或者80字符提示线或者显示垂直标尺

vscode显示120字符或者80字符提示线或者显示垂直标尺 一般规定一行代码不超过80或者120个字符。取决于团队的编码规范。 不同公司不同团队有不同的规定。 当单行代码过长。产生横向滚动条。使得代码难以阅读。 打开全局设置的settings.json /C:/Users/xxx/AppData/Roaming/Cod…

java音乐交流和周边售卖商城系统springboot+vue

不同的系统提供的服务也不相同&#xff0c;其对应的功能也不相同&#xff0c;所以&#xff0c;系统开工前&#xff0c;需要明确其用途&#xff0c;确定其功能。由此&#xff0c;才可以进行各个任务的开展。 国外摇滚乐队交流和周边售卖系统经过分析&#xff0c;确定了其需要设置…

【昕宝爸爸小模块】图文源码详解什么是线程池、线程池的底层到底是如何实现的

➡️博客首页 https://blog.csdn.net/Java_Yangxiaoyuan 欢迎优秀的你&#x1f44d;点赞、&#x1f5c2;️收藏、加❤️关注哦。 本文章CSDN首发&#xff0c;欢迎转载&#xff0c;要注明出处哦&#xff01; 先感谢优秀的你能认真的看完本文&…

轻松识别Midjourney等AI生成图片,开源GenImage

AIGC时代&#xff0c;人人都可以使用Midjourney、Stable Diffusion等AI产品生成高质量图片&#xff0c;其逼真程度肉眼难以区分真假。这种虚假照片有时会对社会产生不良影响&#xff0c;例如&#xff0c;生成公众人物不雅图片用于散播谣言&#xff1b;合成虚假图片用于金融欺诈…