原文:官方帮助文档:https://doc.qt.io/qt-5/signalsandslots.html
信号和槽(Signals & Slots)用于对象之间的通信。信号和槽机制是 Qt 的核心特性,可能也是与其他框架所提供的特性最不同的部分。信号和槽是由 Qt 的元对象系统(The Meta-Object System)实现的。
产生背景
在 GUI 编程中,当我们更改一个小部件时,我们通常希望通知另一个小部件。更一般地说,我们希望任何类型的对象都能够彼此通信。
例如,如果用户单击关闭按钮,我们可能希望调用窗口的 Close()函数。
其他工具包使用 回调 实现这种通信。回调是指向函数的指针,因此,如果希望某个处理函数通知你某个事件,则需要将一个指向另一个函数 (回调) 的指针传递给处理函数。然后处理函数在适当的时候调用回调。虽然使用这种方法的成功框架确实存在,但是回调可能不够直观,而且在确保回调参数的类型正确性方面可能会遇到问题。
信号与槽
在 Qt 中,我们有一种替代回调技术的方法: 使用信号和槽。当特定事件发生时发出信号。Qt 的 widgets 有许多预定义的信号,但是我们总是可以子类化 widgets 来添加我们自己的信号。槽是响应特定信号而 被调用的函数。Qt 的窗口小部件有许多预定义的槽,但通常的做法是子类化窗口小部件并添加自己的槽,以便处理需要的信号。
信号和槽机制是类型安全的:
信号的签名必须与接收槽的签名匹配。(事实上,槽的签名可能比它接收到的信号短,因为它可以忽略额外的参数。)由于签名是兼容的,编译器可以帮助我们在使用基于函数指针的语法时检测类型不匹配。基于字符串的信号和槽语法将在运行时检测类型不匹配。信号和槽是松散耦合的: 发出信号的类既不知道也不关心哪个槽接收信号。Qt 的信号和槽机制确保,如果你将信号连接到槽,该槽将在正确的时间与信号的参数一起被调用。信号和槽可以接受任意数量的任意类型的参数。它们是完全类型安全的。
继承自 QObject 的所有类都可以包含信号和槽。当对象改变它的一些状态时,就会发出信号。它不知道也不关心是否有谁正在接收它发出的信号。这是真正的 信息封装 ,并确保对象可以作为 软件组件 使用。
槽可以用来接收信号,但它们也是普通的成员函数。就像一个对象不知道是否有任何东西接收到它的信号一样,一个槽也不知道是否有任何信号连接到它。这确保了可以用 Qt 创建真正 独立的组件 。
你可以将任意多的信号连接到一个槽,一个信号可以连接到任意多的槽。甚至可以将一个信号直接连接到另一个信号。(这将在第一个信号发出时立即发出第二个信号。)
信号和槽一起构成了一个强大的组件编程机制。
信号
当对象的内部状态以某种方式发生变化时,对象的客户端或所有者可能会对此感兴趣,就会发出信号。信号是公共访问函数,可以从任何地方发出,但是我们建议只从定义信号及其子类的类发出信号。
当一个信号被发出时,连接到它的槽通常会立即执行,就像一个普通的函数调用一样。当发生这种情况时,信号和槽机制完全独立于任何 GUI 事件循环。当所有槽返回后,emit 语句后面的代码就会执行。在使用排队连接时,情况略有不同; 在这种情况下,emit 关键字后面的代码将立即继续执行,槽将在稍后执行。
如果几个槽连接到一个信号,当信号发出时,槽将依次执行。(这个顺序无法保证。)
信号是由 moc(元对象编译器)自动生成的,不能在.cpp 文件中实现。它们永远不能有返回类型(即使用 void)。
关于参数的注意事项: 我们的经验表明,如果信号和槽不使用特殊类型,那么它们的可重用性更好。如果 QScrollBar::valueChanged()使用一种特殊类型,例如假想的 QScrollBar::Range,那么它只能连接到专为 QScrollBar 设计的槽。将不同的输入小部件连接在一起是不可能的。
槽
当连接到槽的信号发出时,就会调用槽。槽是普通的 c ++ 函数,可以正常调用。它们唯一的特点是可以将信号连接到它们身上。
因为槽是普通的成员函数,所以它们在直接调用时遵循普通的 c ++ 规则。但是,作为槽,任何组件都可以通过信号槽连接调用它们 , 无论其访问级别如何。这意味着从任意类的实例发出的信号可以导致在不相关类的实例中调用私有槽。
你还可以将槽定义为虚拟的,这在实践中非常有用。
与回调相比,信号和槽的速度稍微慢一些 ,因为它们提供了更大的灵活性,尽管实际应用的差异并不大。一般来说,通过发出信号来调用槽的速度比使用非虚拟函数直接调用的速度大约慢十倍。这是定位连接对象、安全遍历所有连接(即检查后续接收器在发射过程中没有被销毁) 和并以通用方式编组任何参数所需的开销。虽然 10 倍差距听起来可能很多,但是它比任何新建操作或删除操作的开销要小得多。只要执行后台需要新建或删除字符串、向量或列表操作,信号和槽开销就只占整个函数调用开销的很小一部分。 信号和槽机制的简单性和灵活性非常值得这些开销,用户甚至不会注意到这些开销。