[Qt] 信号和槽(2) | 多对多 | disconnect | 结合lambda | sum

目录

[1. 带参数的信号和槽](#1. 带参数的信号和槽)

重载信号槽

参数列表匹配规则

信号参数多于槽的情况

[2. 信号与槽的连接方式](#2. 信号与槽的连接方式)

[⭕ 信号槽 的意义](#⭕ 信号槽 的意义)

[3. 信号和槽的其他说明](#3. 信号和槽的其他说明)

[1. 信号与槽的断开](#1. 信号与槽的断开)

[2. Qt4 版本信号与槽的连接](#2. Qt4 版本信号与槽的连接)

[Qt4 优缺点](#Qt4 优缺点)

[3.Lambda 定义槽函数](#3.Lambda 定义槽函数)

语法格式

[槽函数使用 Lambda](#槽函数使用 Lambda)

信号与槽的优缺点

补充

1.对于回调函数

[2.对于 linux 中 信号的处理](#2.对于 linux 中 信号的处理)

3.C++多态的实现

[4.模板 偏特化](#4.模板 偏特化)

小结

[1. 信号槽是什么](#1. 信号槽是什么)

[2. 信号槽使用 connect](#2. 信号槽使用 connect)

[3. 如何查阅文档](#3. 如何查阅文档)

[4. 自定义槽函数](#4. 自定义槽函数)

[5. 自定义信号](#5. 自定义信号)

[6. 信号和槽还可以带有参数](#6. 信号和槽还可以带有参数)

[7. 信号槽存在的意义](#7. 信号槽存在的意义)

[8. disconnect 使用方式](#8. disconnect 使用方式)

[9. lambda 表达式简化槽函数的定义](#9. lambda 表达式简化槽函数的定义)


前文:

[Qt] 信号和槽(1) | 本质 | 使用 | 自定义

1. 带参数的信号和槽

Qt 的信号和槽不仅支持无参,还支持带有参数的形式,并且可以重载。这意味着你可以有多个同名但参数列表不同的信号或槽。

  • 信号与槽参数一致性 :当连接带参数的信号和槽时,要求信号函数的参数列表类型要与槽函数相匹配。
  • 如果个数不一致,则信号的参数个数必须多于或等于槽的参数个数,这样信号触发时能够传递实参给槽函数的形参。
重载信号槽
  1. widget.h 头文件中声明重载的信号和槽。
  1. Widget.cpp 文件中实现这些重载成员并建立它们之间的连接。
  1. 执行后观察结果以确保连接正确。

参数列表匹配规则
  • 声明 :在 widget.h 中定义信号和槽的原型。
  • 实现与连接 :在 widget.cpp 中具体实现槽的功能逻辑,并进行信号到槽的连接。

注意,虽然允许信号参数多于槽参数,最佳实践是保持两者参数数量一致,以避免潜在问题。


信号参数多于槽的情况
  • 声明 :同样在 widget.h 中完成。
  • 实现与连接 :在 widget.cpp 中处理,只使用信号的一部分参数。

2. 信号与槽的连接方式

1. 一对一

  • 一个信号连接一个槽
    • widget.h 中声明信号、槽及信号发射函数。
    • widget.cpp 实现上述成员并连接信号与槽。
  • 一个信号连接另一个信号
    • widget.cpp 中添加额外代码来实现这种连接。

2. 一对多

  • 一个信号可以连接多个槽,在 widget.hwidget.cpp 中分别声明和实现。

.h

.cpp

3. 多对一

  • 多个信号可以连接同一个槽,同样需要在 widget.hwidget.cpp 中操作。
  • 类比上面的

4. 多对多

  • 理论上可以实现多个信号连接多个槽,但实际上这很少见,因为"一对一"已经能满足大多数需求。

在 "widget.h" 头文件中声明三个信号以及三个槽:

在 "widget.cpp" 文件中实现槽函数以及连接信号和槽:

实际上,随着程序员经验越来越多,在 GUI 开发的过程中,"多对多" 其实是个 "伪需求",实际开发很少会用到,绝大部分情况来说,"一对一" 就够用了。

⭕ 信号槽 的意义
  • 解耦合(写代码追求 "高内聚,低耦合")
  • 多对多(非常类似于 MySQL 中的 "多对多")

3. 信号和槽的其他说明

1. 信号与槽的断开
  • 可以通过 disconnect 函数来断开信号与槽的连接,其用法与 connect 类似。
  • 在大多数情况下,一旦信号和槽被正确连接后就不再需要管理它们,因此 disconnect 的使用频率较低。
  • 主动断开通常是 为了解绑信号并将其重新绑定到另一个槽函数上。
2. Qt4 版本信号与槽的连接
  • 在 Qt4 中,connect 需要配合 SIGNALSLOT 宏使用,相比 Qt5 更加复杂,并且缺少对函数类型的检查,这可能导致代码更容易出错。
Qt4 优缺点
  • 优点 :参数直观
  • 缺点 :不进行参数类型检测

Qt 5 开始,对上述写法做了简化,不再需要写 SIGNAL 和 SLOT 宏了

  • 简化 :Qt 5 的connect 提供了重载版本。重载版本中,第二个参数和第四个参数成了泛型参数,允许传入任意类型的函数指针。

泛型参数的使用

template <typename Func1, typename Func2>
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender,
                                              Func1 signal,
                                              const typename QtPrivate::FunctionPointer<Func2>::Object *receiver,
                                              Func2 slot,
                                              Qt::ConnectionType type = Qt::AutoConnection)

参数检查功能

  • connect 函数 使用模板 带有一定的参数检查功能。
  • 如果你传入的第一个参数和第二个参数 不匹配,或者第三个参数和第四个参数不匹配(例如,2、4 参数的函数指针,不是 1、3 参数的成员函数),此时代码编译出错。

示例代码

// 连接信号到槽函数
QObject::connect(sender, &Sender::signal, receiver, &Receiver::slot);
  • Qt 封装的类型萃取器 使得泛型编程更加灵活。
  • 通过模板参数,可以处理不同类型的信号和槽函数。

3.Lambda 定义槽函数

之前 写的这篇博文中 有专门讲到

[C++11#46](三) 详解lambda | 可变参数模板 | emplace_back | 默认的移动构造

  • Qt5 引入了更大的灵活性,允许使用任意函数作为槽,包括通过 C++11 的 Lambda 表达式来简化槽函数的编写,无需命名函数。
  • Lambda 表达式提供了一种简洁的方式来创建匿名函数对象,增强了代码的表达力。
语法格式
[ capture ] ( params ) opt -> ret { 
    Function body;
};
  • 捕获列表 [capture] :标识 Lambda 表达式的开始。它决定了外部变量如何被捕获,通常使用 [=] 按值捕获所有外部变量,或 [&] 按引用捕获(较少见),也可以指定捕获特定变量 [a]
  • 参数列表 (params):类似于普通函数定义中的参数列表,可以为空。
  • 返回类型 -> ret:可选部分,用于指定 Lambda 表达式的返回类型。
  • 函数体:包含 Lambda 表达式的执行逻辑。

详解

局部变量引入方式 [ ]

标识[ ] 标识一个 Lambda 表达式的开始,不可省略。

说明

  • 使用引用方式捕获对象时,若局部变量在 Lambda 函数调用前被释放,则会导致不可预知的行为。
  • 对于早期版本的 Qt,需要在 .pro 文件中添加 CONFIG += C++11 来启用 Lambda 表达式支持;Qt5 及以上版本默认支持,无需手动配置。

使用

  • 按值捕获所有变量 :以 [=] 方式传递,Lambda 表达式可以访问所有外部变量。
  • 仅捕获特定变量 a :以 [a] 方式传递,Lambda 表达式只能使用指定的 a 变量。
  • 按引用捕获变量 [&] :虽然可以在 Qt 中这样写,但较少见。一般捕获的是控件指针,对于这些指针来说,按值或按引用传递区别不大。选择按引用来传递时,需注意引用变量的生命周期。

函数参数 ( )

  • (params) 定义了 Lambda 函数接收的参数类型和个数,类似于普通函数定义中的小括号部分。
  • 参数可以通过按值(如:(int a, int b))或按引用(如:(int &a, int &b))的方式传递。
  • 函数参数部分可以省略,省略后相当于无参函数。

选项 Opt

Opt 部分是可选项,最常用的是 mutable 声明,这部分可以省略。

Lambda 表达式外部的局部变量通过值传递进来时,其默认是 const ,所以不能修改这个局部变量的拷贝,加上 mutable 就可以修改。

返回值类型 ->

可以显式指定 Lambda 表达式的返回值类型。如果不指定,则编译器会根据实现推导出返回类型。如果 Lambda 表达式没有返回值,这部分可以忽略。

函数体 { }

  • { } 标识 Lambda 表达式的实现部分,不能省略,但函数体本身可以为空。
槽函数使用 Lambda
  • 示例 A:点击按钮关闭窗口。
  • 简化表达 :当 connect 函数第三个参数为 this 时,第四个参数可以直接使用 Lambda 表达式而省略掉 this
信号与槽的优缺点

优点

松散耦合。

  • 发送者和接收者不需要互相了解对方,增加了灵活性。
  • 所有支持信号槽机制的类或其父类必须继承自 QObject 类。

缺点

效率较低。

  • 相较于直接回调,信号槽机制稍微慢一些,因为提供了更多的灵活性。尽管实际应用中这种差异通常很小,对性能要求不是非常高的场景是可以接受的。
  • 对于用户而言,这样的延迟几乎不可感知。

补充

1.对于回调函数

15.C与指针--超全总结

之前 写的这篇文章中有提到

回调函数

  • 定义:把函数A的指针作为参数传递给另一函数B---实现B(A),A是被调用函数,就是回调函数

使用场景:

  • 模拟实现计算器
  • Qsort
2.对于 linux 中 信号的处理

[Linux][OS][详解信号的产生]

3.C++多态的实现

[C++#28][多态] 两个条件 | 虚函数表 | 抽象类 | override 和 final | 重载 重写 重定义

这样就实现出了不同对象去完成同一行为时,展现出不同的形态

⭕多态的两个条件:

  1. 虚函数覆盖:基类的虚函数被派生类重写。
  2. 对象的指针或引用调用虚函数 :通过基类的指针或引用来调用派生类中重写的虚函数。
4.模板 偏特化

Qt 5 开始,对写法做了简化,不再需要写 SIGNAL 和 SLOT 宏了

  • Qt 5 的 connect 函数简化了信号槽机制的使用。
  • 通过泛型参数,提高了代码的灵活性和可读性。
  • 参数检查功能确保了信号和槽函数的正确连接。

类型萃取器

  • 为了从成员函数指针中提取出对象类型信息,Qt 使用了一种称为"类型萃取器"的工具。
  • 这种工具通常是通过模板特化实现的,它可以识别并处理不同类型的成员函数指针。
  • 例如,QtPrivate::FunctionPointer<Func1>::Object 就是一个用于获取成员函数所属对象类型的模板表达式,它依赖于模板偏特化来为不同类型的成员函数提供正确的实现。

12.C++模板进阶 | 代码膨胀


小结

1. 信号槽是什么
  • 信号源 :触发信号的对象。
  • 信号的类型:信号可以是各种事件,如按钮点击、窗口关闭等。
  • 信号的处理方式:通过连接信号到槽函数来处理这些事件。
2. 信号槽使用 connect
  • 使用 connect 函数将信号与槽函数连接起来。
3. 如何查阅文档
  • 查阅一个控件内置了哪些信号和槽,以及它们的作用。
  • 可能需要查询该类的父类/爷爷类/祖宗类以获取更多信息。
4. 自定义槽函数
  • 实质上就是自定义一个普通的成员函数。
  • 可以让 Qt Creator 自动生成(虽然没有显式 connect,但可以通过函数名字特定规则来完成自动连接)。
5. 自定义信号
  • 这种情况 比较少
  • 信号本质就是成员函数(函数的定义是 Qt 自己生成的,只需要写函数声明)。
  • signals: 自定义关键字中定义信号。
  • 使用 emit 来完成信号的发射(emit 也可以省略)。
6. 信号和槽还可以带有参数
  • 发射信号时,把参数传递给对应的槽。
  • 信号的参数和槽的参数要一致:
    1. 类型匹配
    2. 个数,信号的参数要多于槽的参数。
7. 信号槽存在的意义
  • 多对多效率(非常类似于 MySQL 中的多对多)。(伪命题)
  • "高内聚,低耦合"。
8. disconnect 使用方式
  • 使用 disconnect 断开信号与槽的连接。
9. lambda 表达式简化槽函数的定义

像 Qt 这样的框架,里面涉及到很多机制,都是和之前学到过的一些 编程语言/数据结构/操作系统/网络/数据库 基础知识有关联关系的,例如上面的 补充内容

相关推荐
$程1 分钟前
【React】漫游式引导
前端·javascript·react.js
请叫我飞哥@1 分钟前
HTML5 波动动画(Pulse Animation)详解
前端·html·html5
山猪打不过家猪2 分钟前
React(二)——Admin主页/Orders页面/Category页面
前端·javascript·react.js
Hellc0074 分钟前
新手入门 React .tsx 项目:从零到实战
前端·react.js·前端框架
每天题库6 分钟前
特种设备安全管理人员免费题库限时练习(判断题)
学习·安全·考试·题库·考证
Antonio91511 分钟前
【Linux】环境变量
linux·运维·服务器
安全方案14 分钟前
网络安全的学习与实践经验(附资料合集)
学习·安全·web安全
&白帝&29 分钟前
HTML5 WebSocket
前端·websocket·html5
小蜗牛爱远行35 分钟前
软件开发为什么要用CI/CD方法
linux·运维·ci/cd
法迪37 分钟前
初学Linux电源管理
linux·运维·服务器·功耗