我正在学习Qt,而且我对signal-slotpattern有很多乐趣。我的问题是,信号和槽只是事件监听器和处理程序的语法糖或后台发生的事情是不同的性质?如果以后,有什么根本区别?
答案 0 :(得分:3)
这不仅仅是语法糖。在Qt信号/插槽的背景下有一个真正的工作。这项工作由MOC(元对象编译器)完成。这就是所有C ++头文件都有一个进程的原因,它包含带有Q_OBJECT宏的类。
信号/插槽的“硬部分”是指处于多线程环境中。实际上,connect()函数的Qt :: ConnectionType参数是单线程环境中的Direct(例如函数的直接调用),如果发送方和发射方不在同一个线程中则排队。在这种情况下,信号必须由Qt事件循环处理。
答案 1 :(得分:2)
信号和插槽是语法糖还是它们还有更多?我的问题是,信号和槽只是事件监听器/处理程序的语法糖
不,它们存在的平均原因是解耦的排放和处理。
或背景中发生的事情具有不同的性质?
这个想法是你将排放与某个"事件"的处理分开。如果你考虑直接函数调用作为替代,我想指出,对于它们,响应发射的代码需要知道实际处理"信号"的代码。那是两个部分彼此太紧了。
在不更改负责信号发射的代码的情况下,无法为信号动态添加另一个处理程序。想象一下这样的例子:
代码的某些部分发出信号"水果到达"
此代码会直接调用"洗水果"方法
如果有人想添加一种计算水果数量的方法怎么办?
使用信号槽机制,您无需触摸原始代码。您可以简单地将新插槽连接到来自完全不同代码的现有信号。这被称为良好的设计和自由。
当您拥有像Qt这样的库可以在不事先了解Qt应用程序的情况下发出信号时,这一点尤其有用。由Qt应用程序决定如何自己处理信号。
此外,此模式还使应用程序响应更快,阻塞更少,这就是方向函数调用的情况。这是因为存在Qt信号槽机制的事件循环。当然,您也可以使用直接调用线程,但它比理想世界中的必要工作更难以维护。
因此,部分已经触及,后台有一个QtEventLoop排队这些事件进行处理,虽然也可以执行"直接调用&#34 ;.
可以在那里找到真正的后台内部实现代码,并在moc(元对象编译器)中找到。 Moc基本上是为你没有定义主体的信号创建一个函数,所以你只需要在QObject子类中声明它们。
您可以在此处阅读有关该主题的更多信息,但我认为我的解释可以帮助您:
QtDD13 - Olivier Goffart - Signals and Slots in Qt 5
答案 2 :(得分:2)
信号和插槽是一种将方法调用与被调用方法分离的方法。 它们根本不是语法糖,因为它们没有为C ++语言添加新语法。信号发射是方法调用。 slot是一种普通的旧实例方法。链接这两者的代码是普通的旧C ++。这里没什么新东西 - 没有任何糖。
你所谓的大多数"语法糖"类似于注释 - 这些是空的定义(Q_SLOT
,Q_SIGNAL
,signals
,slots
),用于标记元对象编译器(moc)处理的方法。 Moc基于声明的正常C ++语法生成内省信息和信号实现(有一些限制)。
我声称这不是语法糖,因为moc理解常规C ++并且不基于任何语法糖生成内省数据,而是基于通常的实例方法声明。 "糖"是否可以避免让moc在类的声明中为所有生成元数据而过早悲观化。它还允许moc忽略方法定义 - 否则它需要解析它们,并假设没有定义的方法是信号。
emit
宏仅供人类使用,仅表示方法调用实际上是信号发射。它没有被moc使用。它被定义为空的。
Q_OBJECT
和Q_GADGET
宏声明了一些用于访问元数据的类成员。可以说,它们是真正的糖和糖的唯一部分。因为它可以使您免于输入几行声明。
有很多代码可能会让它成功运作。
信号:
是一个实例方法,其实现由moc生成,
有关于其名称和参数的完整内省信息。这可以作为QMetaMethod
。
一个插槽:
是您提供的实现方法,
同样具有完整的内省信息。
元信息在运行时可用,并且可以通过代码来枚举和使用,这些代码既不知道信号,也不知道插槽的签名。
发出信号时,只需调用moc生成的方法即可。此方法调用Qt库代码,该代码获取相关的互斥锁,迭代附加的插槽列表,并执行调用,在此过程中根据需要获取其他互斥锁。正确地执行此操作需要小心,因为发送方和接收方对象可以驻留在不同的线程中。必须避免向不存在的对象传递插槽调用。哦,你也不想要死锁。这需要一些深谋远虑。
由于信号和插槽都只是方法,你当然可以将信号连接到其他信号 - 底层机制并不关心被调用的内容,它只是一种可调用的方法。不可调用的方法是那些没有元数据的方法。
发出相关信号时调用插槽。信号发射只是对生成的信号体的方法调用。这与事件监听器模式不同,因为时隙调用可以是立即(所谓的直接连接),也可以是延迟到事件循环(所谓的排队连接)。通过复制参数并将它们捆绑在QMetaCallEvent
中来实现排队的插槽调用。此事件被转换为#34;回到QObject::event
的方法调用。当事件循环将事件传递给目标对象时会发生这种情况。
元数据不仅包含信号和插槽签名。它还允许您默认和复制构造信号/插槽参数类型 - 这是实现排队调用所必需的。它还包含枚举的键值对 - 这使得Qt更容易编写脚本。传递给Qt方法的所有枚举值都可以在运行时按名称查找!