目录
1. ⾃定义信号和槽
1.1 基本语法
在 Qt 中,允许⾃定义信号的发送⽅以及接收⽅,即可以⾃定义信号函数和槽函数。但是对于⾃定义的信号函数和槽函数有⼀定的书写规范。
1、⾃定义信号函数书写规范
(1)⾃定义信号函数必须写到 "signals" 下;
(2)返回值为 void,只需要声明,不需要实现;
(3)可以有参数,也可以发⽣重载;
2、⾃定义槽函数书写规范
(1)早期的 Qt 版本要求槽函数必须写到 "public slots" 下,但是现在⾼级版本的 Qt 允许写到类的"public" 作⽤域中或者全局下;
(2)返回值为 void,需要声明,也需要实现;
(3)可以有参数,可以发⽣重载;
3、发送信号
使⽤ "emit" 关键字发送信号 。"emit" 是⼀个空的宏。"emit" 其实是可选的,没有什么含义,只是为了提醒开发⼈员。
⽰例1:
1、在 widget.h 中声明⾃定义的信号和槽,如图所⽰;
2、在 widget.cpp 中实现槽函数,并且关联信号和槽
注意:图中的 ① 和 ② 的顺序不能颠倒。原因是, ⾸先关联信号和槽,⼀旦检测到信号发射之后就会⽴⻢执⾏关联的槽函数。反之,若先发射信号,此时还没有关联槽函数,当信号发射之后槽函数不会响应.
⽰例2:当⽼师说 "上课了",学⽣们就 "回到座位,开始学习"。
1、在源⽂件中新建两个类,⼀个是⽼师类,⼀个是学⽣类;⾸先选中项⽬名称,⿏标右键 -----> "add new..."
点击 "add new..." 之后,出现如下界⾯:
选择 "choose" 出现如下界⾯.
注意:在 Qt 中新建类时, 要选择新建类的⽗类.显然,当前项⽬中还没啥类适合做新类的⽗类, 同时新的类也不是⼀个 "窗⼝" 或者 "控件". 这种情况⼀般选择 QObject 作为基类.这样做的好处是这个新类的对象可以搭配 Qt 的对象树机制. 便于对象的正确释放.
选择 "下⼀步",出现如下界⾯ :
对于 "学⽣类" 以上述同样的⽅式进⾏添加,添加完成之后,项⽬⽬录新增⽂件如下:
在 teacher.h 中声明信号函数:
在 student.h 中声明槽函数:
在 widget.h 中实例化 "⽼师类对象" 和 "学⽣类对象";
在 student.cpp 中实现槽函数:
在 widget.cpp 中连接⾃定义信号和槽;
运⾏结果如下图⽰:
⽰例3:⽼师点击 "按钮" 触发学⽣上课;
运⾏结果如下图⽰:
1.2 带参数的信号和槽
Qt 的信号和槽也⽀持带有参数, 同时也可以⽀持重载.
此处我们要求, 信号函数的参数列表要和对应连接的槽函数参数列表⼀致.
此时信号触发, 调⽤到槽函数的时候, 信号函数中的实参就能够被传递到槽函数的形参当中.
通过这样的机制, 就可以让信号给槽传递数据了.
⽰例1:重载信号槽
(1)在 "widget.h" 头⽂件中声明重载的信号函数以及重载的槽函数;如下图所⽰:
(2)在 "Widget.cpp" ⽂件实现重载槽函数以及连接信号和槽。
注意:在定义函数指针时要指明函数指针的作⽤域。


⽰例2:信号槽参数列表匹配规则
1、在 "widget.h" 头⽂件中声明信号和槽函数;
2、在 "widget.cpp" ⽂件中实现槽函数以及连接信号和槽
其实信号的参数个数可以多于槽函数的参数个数,但是槽的参数个数不能多于信号参数个数.但是实际开发中最好还是保持参数个数也能匹配⼀致.
⽰例3:
1、在 "widget.h" 头⽂件中声明信号和槽函数;
2、在 "widget.cpp" ⽂件中实现槽函数以及连接信号和槽;
2. 信号与槽的连接⽅式
2.1 ⼀对⼀
主要有两种形式,分别是:⼀个信号连接⼀个槽 和 ⼀个信号连接⼀个信号。
(1)⼀个信号连接⼀个槽
⽰例:
1、在 "widget.h" 中声明信号和槽以及信号发射函数;
2、在 "widget.cpp" 中实现槽函数,信号发射函数以及连接信号和槽;
(2)⼀个信号连接另⼀个信号
⽰例:
在上述⽰例的基础上,在 "widget.cpp" ⽂件中添加如下代码:
2.2 ⼀对多
⼀个信号连接多个槽
⽰例:
(1)在 "widget.h" 头⽂件中声明⼀个信号和三个槽;

2.3 多对⼀
多个信号连接⼀个槽函数
⽰例:
(1)在 "widget.h" 头⽂件中声明两个信号以及⼀个槽;
3. 信号和槽的其他说明
3.1 信号与槽的断开
使⽤ disconnect 即可完成断开.
disconnect 的⽤法和 connect 基本⼀致.
⽰例:
3.2 Qt4 版本信号与槽的连接
Qt4 中的 connect ⽤法和 Qt5 相⽐是更复杂的. 需要搭配 SIGNAL 和 SLOT 宏来完成.
⽽且缺少必要的函数类型的检查. 使代码更容易出错.
⽰例:
(1)在 "widget.h" 头⽂件中声明信号和槽
(2)在 "widget.cpp" ⽂件中实现槽函数以及连接信号与槽;
Qt4 版本信号与槽连接的优缺点:
优点:参数直观;
缺点:参数类型不做检测;
3.3 使⽤ Lambda 表达式定义槽函数
Qt5 在 Qt4 的基础上提⾼了信号与槽的灵活性,允许使⽤任意函数作为槽函数。
但如果想⽅便的编写槽函数,⽐如在编写函数时连函数名都不想定义,则可以通过 Lambda表达式 来达到这个⽬的。
Lambda表达式 是 C++11 增加的特性。C++11 中的 Lambda表达式 ⽤于定义并创建匿名的函数对象,以简化编程⼯作。
Lambda表达式 的语法格式如下:
[ capture ] ( params ) opt -> ret {
Function body;
};
1、局部变量引⼊⽅式 [ ]
[ ] : 标识⼀个 Lambda表达式 的开始。不可省略。
说明:
由于使⽤引⽤⽅式捕获对象会有局部变量释放了⽽Lambda函数还没有被调⽤的情况。如果执⾏Lambda函数,那么引⽤传递⽅式捕获进来的局部变量的值不可预知。所以绝⼤多数场合使⽤的形式为: [=] () { }
早期版本的 Qt,若要使⽤Lambda表达式,要在 ".pro" ⽂件中添加: CONFIG += C++11因为 Lambda表达式 是 C++11 标准提出的。Qt5 以上的版本⽆需⼿动添加,在新建项⽬时会⾃动添加。
3.4 信号与槽的优缺点
优点: 松散耦合
信号发送者不需要知道发出的信号被哪个对象的槽函数接收,槽函数也不需要知道哪些信号关联了⾃⼰,Qt的信号槽机制保证了信号与槽函数的调⽤。⽀持信号槽机制的类或者⽗类必须继承于 QObject类。
缺点: 效率较低
与回调函数相⽐,信号和槽稍微慢⼀些,因为它们提供了更⾼的灵活性,尽管在实际应⽤程序中差别不⼤。通过信号调⽤的槽函数⽐直接调⽤的速度慢约10倍(这是定位信号的接收对象所需的开销;遍历所有关联;编组/解组传递的参数;多线程时,信号可能需要排队),这种调⽤速度对性能要求不是⾮常⾼的场景是可以忽略的,是可以满⾜绝⼤部分场景。
⼀个客⼾端程序中, 最慢的环节往往是 "⼈".假设本⾝基于回调的⽅式是 10us, 使⽤信号槽的⽅式是 100us. 对于使⽤程序的⼈来说, 是感知不到的