在Qt的众多与众不同的特点中,信号(Signal)/槽(Slot)机制是Qt的一个中心特征并且也许是Qt与其它工具包的最大不相同的部分。信号和槽主要用于对象之间的通讯。
信号与插槽机制提供了对象间通信机制,它易于理解和使用,并完全被Qt图形设计器所支持。
图形用户接口的应用需要对用户的动作做出响应。例如,当用户点击了一个菜单项或是工具栏的按钮时,应用程序会执行某些代码。大部分情况下,我们希望不同类型的对象之间能够进行通信。程序员必须把事件和相关代码联系起来,这样才能对事件做出响应。以前的工具开发包使用的事件响应机制是已崩溃的,不够健壮的,同时也不是面向对象的。Trolltech已经创立了一种新的机制,叫做“信号与插槽”。信号与插槽是一种强有力的对象间通信机制,它完全可以取代原始的回调和消息映射机制;信号与插槽是迅速的,类型安全的,健壮的,完全面向对象并用c++来实现的一种机制。在以前,当我们使用回调函数机制来把某段响应代码和一个按钮的动作相关联时,我们通常把那段响应代码写成一个函数,然后把这个函数的地址指针传给按钮,当那个按钮被按下时,这个函数就会被执行。对于这种方式,以前的开发包不能够确保回调函数被执行时所传递进来的函数参数就是正确的类型,因此容易造成进程崩溃,另外一个问题是,回调这种方式紧紧的绑定了图形用户接口的功能元素,因而很难把开发进行独立的分类。Qt的信号与插槽机制是不同的。Qt的窗口在事件发生后会激发信号。例如一个按钮被点击时会激发一个“clicked”信号。程序员通过建立一个函数(称作一个插槽),然后调用connect函数把这个插槽和一个信号连接起来,这样就完成了一个事件和响应代码的连接。信号与插槽机制并不要求类之间互相知道细节,这样就可以相对容易的开发出代码可重用的类。信号与插槽机制是类型安全的,它以警告的方式报告类型错误,而不会使系统产生崩溃。例如,如果一个退出按钮的clicked()信号被链接到了一个应用的退出函数-插槽quit()。那么一个用户点击退出键俺就的clicke()信号被连接到一个应用的退出函数-插槽quit()。那么一个用户点击退出键将使应用程序终止运行。上述的链接过程用代码写出来就是这样
connect(button,SIGNAL(clicke()),qApp,SLOT(quit()))
我们可以在Qt应用程序的执行过程中增加或是减少信号与插槽的链接。信号与插槽的实现扩展了C++的语法,同时也完全利用了C++面向对象的特征。信号与插槽可以被重载或者重新实现,他们可以定义为类的公有,私有或是保护成员。
信号:当对象的内部状态发生改变,信号就被发射,在某些方面对于对象代理或者所有者也许是很有趣的。只有定义了一个信号的类和他的子类才能发射这个信号。
例如,一个列表框同时发射highlighted()和activated()这两个信号。绝大多数对象也许只对activated这个信号感兴趣,但是有时想知道列表框中的那个条目在当前是高亮的。如果两个不同的类对同一个信号感兴趣,你可以把这个信号和这两个对象链接起来。当一个信号被发射,它所连接的槽会被立即执行,就像一个普通函数调用一样。信号/槽机制完全不依赖与任何一种图形用户界面的事件回路。当所有的槽都返回后emit也就返回。
如果几个槽被链接到一个信号,当信号被发射时,这些槽就会被任意顺序一个接一个地执行。
槽:当一个和槽链接的信号被发射的时候,这个槽被调用。槽也是普通的C++函数并且可以像他们一样被调用:他们唯一的特点就是他们可以被信号链接。槽的参数不能含有默认值,并且和信号一样,为了槽的参数而使用自己特定的类型是很不明智的。
因为槽就是普通成员函数,但却有一点非常有意思的东西,他们也和普通成员函数一样有访问权限。一个槽的访问权限决定了谁可以和它相连:
一个publicslots:包含了任何信号都可以相连的槽。这对于组件编程来说非常有用:你生成了许多对象,它们互相并不知道,把他们的信号与槽链接起来,这样信息就可以正确地传递,并且就像一个铁路模型,把它打开然后让他跑起来。
一个protectedslots:包含了之后这个类和他的子类的信号才能链接的槽。这就是说这些槽只是类的一部分,而不是它和外界的接口
一个privateslots:包含了之后这个类本身的信号可以链接的槽。这就是说它和这个类是非常紧密的,甚至它的子类都没有获得链接权利这样的新人
你也可以把槽定义为虚的,这在实践中被发现也是非常有用的。
connect(object1,signal1,object2,slot1)
connect(object1,signal1,object2,slot2)
object1 object2
signal1----------------------------------------------------- signal1
signal2------------------------------------------- | signal2
slot1 | |----------------slot1
slot2 |---------------------------slot2
信号与槽的机制是非常有效的,但是它不像“真正的”回调那样快。信号与槽稍微有些慢,这是因为他们所提供的灵活性,尽管在实际应用中这些不同可以被忽略。通常,发射一个和槽相连的信号,大约只比直接调用那些非虚函数调用的接受器慢十倍。这是定位链接对象所需的开销,可以安全地重复所有的链接(例如在发射期间检查并发接收器是否被破坏)并且可以按一般的方式安排任何参数。当是个非虚函数调用听起来很多时,举个例子来说,时间开销只不过比任何一个“new”或者"delete"操作要少些。当你执行一个字符串、矢量或者列表操作时,需要“new”或者“delete”,信号和槽仅仅对一个完整函数调用的时间开销中的一个非常小的部分负责。无论何时你在一个槽中使用一个系统调用和间接地调用超过十个函数的时间是相同的。在一台i585-500机器上,你每秒钟可以法神2,000,000个左右连接到一个接收器上的信号,或者发射1,200,000个左右链接到两个接受器的信号。信号和槽机制的简单性和灵活性对于时间的开销来说是非常值得的,你的用户甚至察觉不出来。