Qt 的信号槽机制详解:之信号槽引发的 Segmentation Fault 问题拆析(上)
Qt 的信号槽机制详解:之因信号槽误用引发的 Segmentation Fault 问题拆析(上)
- 前言
- 一. 信号与槽的基本概念
- 信号(Signal)
- 槽(Slot)
- 连接信号与槽
- 二. 信号槽机制的实现原理
- 元对象系统(Meta-Object System)
- 信号与槽的内部数据结构
- 三. 信号与槽的连接方式
- 经典语法
- 新语法(推荐)
- Lambda 表达式
- 静态槽
- 四. 信号与槽的连接类型
- 1. Qt::AutoConnection(默认)
- 2. Qt::DirectConnection
- 3. Qt::QueuedConnection
- 4. Qt::BlockingQueuedConnection
- 5. Qt::UniqueConnection
- 五. 信号与槽的高级特性
- 1. 多对多连接
- 2. 可断开连接
- 3. 自定义信号与槽
- 六. 信号槽的线程安全性
- 七. 性能分析
- 总结
前言
该系列文章中,我主要和大家一同探讨因为Qt 的信号槽机制误用,而引发的 Segmentation Fault 问题。
作为自己目前经手项目的阶段性总结,同时也给大家分享几个正确使用QT信号槽的定向性方式。
在该篇内容中,我将结合 Qt 编译器的特性,详细分析信号槽使用不当可能引发的崩溃问题。
【系列文章】索引:
Qt 的信号槽机制详解:之信号槽引发的 Segmentation Fault 问题拆析(上)
Qt 的信号槽机制详解:之信号槽引发的 Segmentation Fault 问题拆析(下)
Qt 的信号与槽(Signal & Slot)机制是其核心特性之一,用于实现组件之间的解耦通信。
它本质上是基于 观察者模式(Observer Pattern) 的一种实现,但由于 Qt 信号槽系统结合了 元对象系统(Meta-Object System),使其更加高效和灵活。
一. 信号与槽的基本概念
信号(Signal)
- 信号是一个由对象发出的通知,表示某个事件的发生。
- 信号没有返回值,但可以携带参数。
- 信号是由
emit
关键字触发的。 - 信号是
protected
或public
的类成员,不能直接调用,只能通过emit
触发。
槽(Slot)
- 槽是一个函数,用于处理信号发出的通知。
- 槽可以是类的普通成员函数,也可以是 lambda 表达式。
- 一个槽函数可以连接到多个信号。
- 槽函数也可以是静态函数或全局函数,但这需要通过适配器(如
std::bind
或 lambda)来实现。
连接信号与槽
- 通过
QObject::connect
将信号与槽连接。 - 当信号被触发时,Qt 自动调用与之连接的槽函数。
二. 信号槽机制的实现原理
元对象系统(Meta-Object System)
Qt 的信号槽机制依赖于元对象系统(QObject
和 QMetaObject
)。
- 每个继承自
QObject
的类都会通过moc
工具生成对应的元对象代码。 - 元对象包含类的元数据(包括信号、槽、属性等信息)。
- 通过元对象系统,Qt 可以在运行时找到信号和槽,并在触发信号时动态调用槽函数。
信号与槽的内部数据结构
-
信号
- 信号在
QObject
的子类中以QMetaObject
的形式注册。 - 信号的发射会触发
QObject
中的QMetaObject::activate()
函数,该函数会查找所有连接的槽并调用它们。
- 信号在
-
槽
- 槽函数被存储为回调函数指针或函数对象(如 lambda)。
- 信号触发时,通过函数指针调用槽函数。
-
连接
- Qt 会在内部建立一个连接映射表,记录每个信号与对应槽的关系。
三. 信号与槽的连接方式
Qt 提供多种方式连接信号与槽:
经典语法
QObject::connect(sender, SIGNAL(signalName(params)), receiver, SLOT(slotName(params)));
- 缺点:使用字符串,容易导致参数错误,无法检查类型。
新语法(推荐)
QObject::connect(sender, &SenderClass::signalName, receiver, &ReceiverClass::slotName);
- 优点:类型安全,编译时检查参数匹配。
Lambda 表达式
QObject::connect(sender, &SenderClass::signalName, [](int value) {qDebug() << "Signal emitted with value:" << value;
});
- 优点:可以在槽中直接捕获局部变量。
静态槽
QObject::connect(sender, &SenderClass::signalName, staticFunction);
- 优点:适合不依赖实例的全局函数。
四. 信号与槽的连接类型
连接类型由 Qt::ConnectionType
决定:
1. Qt::AutoConnection(默认)
- 如果信号和槽在同一线程中,使用直接调用(Direct Connection)。
- 如果信号和槽跨线程,使用事件队列(Queued Connection)。
2. Qt::DirectConnection
- 信号触发时,直接调用槽函数(在信号的调用线程中执行)。
- 适用于需要实时响应的情况,但线程不安全。
3. Qt::QueuedConnection
- 信号触发时,将调用请求放入目标线程的事件队列中,由目标线程处理。
- 用于跨线程通信。
4. Qt::BlockingQueuedConnection
- 类似于
Qt::QueuedConnection
,但信号触发线程会阻塞,直到槽函数执行完毕。 - 注意:容易引发死锁,应谨慎使用。
5. Qt::UniqueConnection
- 确保一个信号只连接到某个槽一次。
- 可以与其他类型联合使用,如
Qt::UniqueConnection | Qt::QueuedConnection
。
五. 信号与槽的高级特性
1. 多对多连接
- 一个信号可以连接到多个槽。
- 一个槽可以连接到多个信号。
- 信号也可以连接到另一个信号。
示例:
QObject::connect(sender, &SenderClass::someSignal, receiver1, &ReceiverClass::slot1);
QObject::connect(sender, &SenderClass::someSignal, receiver2, &ReceiverClass::slot2);
QObject::connect(sender, &SenderClass::someSignal, sender, &SenderClass::anotherSignal);
2. 可断开连接
- 可以使用
QObject::disconnect
断开信号与槽的连接:
QObject::disconnect(sender, &SenderClass::someSignal, receiver, &ReceiverClass::someSlot);
- 如果不指定槽,则断开所有与信号相关的连接。
3. 自定义信号与槽
- 可以在类中自定义信号与槽。
示例:
class MyClass : public QObject {Q_OBJECTsignals:void mySignal(int value);public slots:void mySlot(int value) {qDebug() << "Value received:" << value;}
};// 使用
MyClass obj;
QObject::connect(&obj, &MyClass::mySignal, &obj, &MyClass::mySlot);
emit obj.mySignal(42);
六. 信号槽的线程安全性
- 信号槽机制默认是线程安全的,尤其是在
Qt::QueuedConnection
模式下。 - 直接连接(
Qt::DirectConnection
)需要开发者手动确保线程安全。 - 在跨线程通信中,尽量使用
Qt::QueuedConnection
。
七. 性能分析
信号槽的调用效率虽略低于直接调用,但优化得非常好:
- 直接连接的效率与函数指针调用接近。
- 使用事件队列的性能则取决于事件的处理速度。
- 如果性能是关键问题,可以使用 Qt 的低级机制(如函数指针)代替信号槽。
总结
Qt 的信号槽机制使得组件间的通信变得简单且高效,但也需要注意以下几点:
- 确保信号与槽的参数完全匹配。
- 合理选择连接类型,尤其是跨线程通信时。
- 使用
QObject::disconnect
或对象生命周期管理机制,防止悬挂指针问题。 - 对于复杂场景,可结合 lambda 表达式或
std::bind
使用。
相关文章:
Qt 的信号槽机制详解:之信号槽引发的 Segmentation Fault 问题拆析(上)
Qt 的信号槽机制详解:之因信号槽误用引发的 Segmentation Fault 问题拆析(上) 前言一. 信号与槽的基本概念信号(Signal)槽(Slot)连接信号与槽 二. 信号槽机制的实现原理元对象系统(M…...
uniapp中实现APP调用本地通知栏通知、震动、本地提示音或者mp3提醒
要在uniapp中实现APP调用本地通知栏通知、震动和本地提示音或者mp3提醒,你可以使用uni-app提供的原生API和插件来实现。 通知栏通知: 你可以使用uni-app的原生API uni.showToast() 或者 uni.showModal() 来实现通知栏通知的功能。可以在需要发送通知的地…...
ADB 上传文件并使用脚本监控上传百分比
有个需求,需要测试 emmc的外部连续写入性能,使用 ADB 上传一个巨大的文件。并且在上传到一定值时进行干预。 因此但是 adb push 命令本身会 block 运行并且不返回进度,因此需要一个额外的监控脚本。 上传脚本: echo off setloc…...
【数据库】PostgreSQL(持续更新)
目录 K8S 部署基本使用高级特性 K8S 部署 # pg.yaml --- apiVersion: v1 kind: PersistentVolume metadata:name: tv-postgres-pvnamespace: locallabels:storage: tv-postgres-pv spec:accessModes:- ReadWriteOncecapacity:storage: 50Gi # 按需修改,需要保持与…...

overleaf中出现TeX capacity exceeded PDF object stream buffer=5000000的原因和解决方案
在插入pdf 配图后,编译出错提示信息如图,很可能的一个原因是pdf文件大小太大了,最好压缩一下,压缩到1MB以内。...

pyqt5冻结+分页表
逻辑代码 # -*- coding: utf-8 -*- import sys,time,copy from PyQt5.QtWidgets import QWidget,QApplication, QDesktopWidget,QTableWidgetItem from QhTableWidgetQGN import Ui_QhTableWidgetQGN from PyQt5.QtCore import Qt from PyQt5 import QtCore, QtGui, QtWidgets…...

一起学Git【第四节:添加和提交文件】
通过前三节的学习,基本上对Git有了初步的了解,下面开始进行文件的添加和提交的流程。 这里主要涉及四个命令: git init 创建仓库git status查看仓库状态git add添加至暂存区git commit提交文件之前已经使用过git init命令了,此处不再具体讲解。参照一起学Git【第二节:创建…...

【鸿蒙实战开发】HarmonyOS集成高德地图定位实现
背景 随着HarmoneyOS 应用的井喷式增长,各大厂商也都加快了自己原生应用鸿蒙化的脚步,今天使用高德打车的时候忽然间想到高德在鸿蒙上有没有实现呢?打开next bate 版本的手机发现高德已经上架了,但是功能还不是特别完善。那么几乎每个应用都…...

ECharts散点图-气泡图,附视频讲解与代码下载
引言: ECharts散点图是一种常见的数据可视化图表类型,它通过在二维坐标系或其它坐标系中绘制散乱的点来展示数据之间的关系。本文将详细介绍如何使用ECharts库实现一个散点图,包括图表效果预览、视频讲解及代码下载,让你轻松掌握…...
python操作Elasticsearch执行增删改查
文章目录 基本操作更多查询方法1. 查询全部数据2. 针对某个确定的值/字符串的查询:term、match3. 在多个选项中有一个匹配,就查出来:terms4. 数值范围查询:range5. 多个条件同时触发 bool6. 指定返回值个数 size7. 返回指定列 _so…...

学习C++:关键字
关键字: 作用:关键字是C预先保留的单词(标识符) 在定义变量或者常量时候,不要用关键字 不要用关键字给变量或者常量起名称...

FFmpeg在python里推流被处理过的视频流
链式算法处理视频流 视频源是本地摄像头 # codinggbk # 本地摄像头直接推流到 RTMP 服务器 import cv2 import mediapipe as mp import subprocess as sp# 初始化 Mediapipe mp_drawing mp.solutions.drawing_utils mp_drawing_styles mp.solutions.drawing_styles mp_holis…...
为什么推荐使用构造函数注入而非@Autowired注解进行字段注入
在 Spring 框架中,推荐使用构造函数注入而非Autowired注解进行字段注入,主要有以下几个原因: 1. 依赖不可变和空指针安全 构造函数注入:使用构造函数注入时,依赖在对象创建时就必须提供,一旦对象创建完成&…...

如何卸载和升级 Angular-CLI ?
Angular-CLI 是开发人员使用 Angular 的必备工具。然而,随着频繁的更新和新版本的出现,了解如何有效地卸载和升级 Angular-CLI 对开发人员来说至关重要。本指南提供了一个全面的、循序渐进的方法来帮助您顺利过渡到最新版本。 必备条件 确保您的系统上…...
在线excel编辑(luckysheet)
项目地址:Luckysheet: 🚀Luckysheet ,一款纯前端类似excel的在线表格,功能强大、配置简单、完全开源。 可以下载项目使用npm安装运行,也可以用cdn 加载excel文件(使用luckyexcel): …...
【ES6复习笔记】Symbol 类型及其应用(9)
一、Symbol 简介 Symbol 是 JavaScript 中的一种基本数据类型,它表示唯一的标识符。Symbol 的主要目的是防止属性名冲突,尤其是在多个代码库或模块中共享对象时。Symbol 值可以用作对象的属性名,这样可以确保属性名是唯一的,不会…...

[原创](Modern C++)现代C++的第三方库的导入方式: 例如Visual Studio 2022导入GSL 4.1.0
[简介] 常用网名: 猪头三 出生日期: 1981.XX.XX 企鹅交流: 643439947 个人网站: 80x86汇编小站 编程生涯: 2001年~至今[共23年] 职业生涯: 21年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delphi、XCode、Eclipse…...
【ES6复习笔记】Class类(15)
介绍 ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,…...
【Golang 面试题】每日 3 题(六)
✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/UWz06 📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏…...
openEuler安装OpenGauss5.0
OpenGauss5.0 Linux服务器 极简版 服务器安装 单节点安装 极简版安装 安装准备 获取安装包 下载地址:https://opengauss.org/zh/download/archive/版本选择:5.0.0 (LTS)系统架构:uname -m操作系统:cat /etc/os-release完整性校…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...