[Qt] 信号与槽机制
Qt 提供了信号和槽机制用于完成界面操作的响应,是完成任意两个 Qt 对象之间的通信机制.
其中信号会再某个特定情况或动作下被触发,槽是等同于接受并处理信号的函数.
每个 Qt 对象都包含若干个预定义的信号和若干个预定义的槽,当某个特定事件发生时,一个信号被发送,与信号相关联的槽则会响应信号并作出相应的处理.当一个类被继承,该类的信号也同时被继承,也可以根据需要自定义信号和槽. (概念像响应式编程,类似于iOS 的 RxSwift 与 ReactiveCocoa.)
信号与槽机制的连接方式
-
- 信号一对一连接
# 对象1的信号可以与对象2的信号直接相连
connect(Object1,SINGNAL(signal1), Object2,SIGNAL(signal1))
-
- 单一信号与多个槽连接
connect(Object1,SINGNAL(signal2), Object2,SIGNAL(slot2))
connect(Object1,SINGNAL(signal2), Object3,SIGNAL(slot1))
-
- 同一个槽响应多个信号
connect(Object1,SINGNAL(signal2), Object2,SIGNAL(slot2))
connect(Object3,SINGNAL(signal2), Object2,SIGNAL(slot2))
但是,常见的连接方式是:
connect(Object1,SINGNAL(signal), Object2,SIGNAL(slot))
其中, signal
为对象Object1
发出的信号, slot
为对象Object2
的槽.
SIGNAL()
和 SLOT()
是 Qt
的两个宏定义,他们返回其参数的 C
语言风格的字符串(const char *)
. 也就是说下面两个写法是等同的.
connect(button,SIGNAL(clicked()),this,SLOT(showArea()));
connect(button,"clicked()",this,showArea());
信号与槽机制的优点
- 类型安全. 需要关联的信号和槽的签名必须是等同的,即信号的参数类型和参数个数与接受该信号的槽的参数个数相同.不过,一个槽的参数个数是可以少于信号的参数个数的, 但是缺少的参数必须是信号参数的最后一个或几个参数. 如果信号和槽的签名不符,编译器就会报错.
- 松散耦合. 信号和槽机制减少了 Qt 对象的耦合度.激发信号的 Qt 对象无需知道是哪个对象的哪个槽需要接收它发出的信号,它只需要做的是在适当的时候发送适当的信号就可以了, 而不需要知道也不需要关心它的信号有没有被接收到, 更不需要知道是哪个对象的哪个槽接收到了信号. 同样,对象的槽也不知道是哪些信号关联了自己,而一旦关联信号和槽, Qt 保证了适当的槽得到了调用.即使关联的对象在运行时被删除,应用程序也不会崩溃.
一个类若要支持信号和槽,就必须从 QObject 或 QObject 的子类继承. 注意, Qt 信号和槽机制不支持对模板的使用.
信号与槽机制的效率
信号与槽机制增强了对象间通信的灵活性,然而这也损失了一些性能. 同回调函数相比,信号与槽机制运行速度有些慢. 通常,通过传递一个信号来调用槽函数将会比直接调用非虚函数运行速度慢10倍.
原因如下:
- 需要定位接收信号的对象
- 安全地遍历所有的关联
- 编组(marshal)/解组(unmarshal)传递的参数.
- 多线程的时候,信号可能需要排队等待.
然而,与创建堆对象的 new 操作及删除堆对象的 delete 操作相比,信号与槽的运行代价只是它的一部分. 信号与槽机制导致的这点性能损失,对实际应用程序是可以忽略的; 同信号和槽提供的灵活性和简便性相比,这点性能损耗也是值得的.