假设我有这个信号:
signals:
void progressNotification(int progress);
我最近才知道Qt中的emit关键字。到目前为止,我过去常常通过像常规函数一样调用信号来执行信号。所以代替:
emit progressNotification(1000 * seconds);
我会写:
progressNotification(1000 * seconds);
像这样调用它们似乎有效,并且所有连接的插槽都会执行,那么使用 emit 关键字会导致不同的行为,还是只是语法糖?
emit
只是语法糖。如果你看一下发出信号的函数的预处理输出,你会发现emit
刚刚消失。
"魔术"发生在为信号发射函数生成的代码中,您可以通过检查 moc 生成的C++代码来查看。
例如,没有参数的foo
信号生成此成员函数:
void W::foo()
{
QMetaObject::activate(this, &staticMetaObject, 0, 0);
}
代码emit foo();
经过预处理,只需foo();
emit
在 Qt/qobjectdefs.h
中定义(无论如何都是源代码的开源风格),如下所示:
#ifndef QT_NO_EMIT
# define emit
#endif
(定义保护是允许您通过no_keywords
QMake配置选项将Qt与其他具有冲突名称的框架一起使用。
18 个月后...我从@Mat的回答下的评论开始,很快就用完了空间。因此答案。
IMO emit
既不是句法糖也不是一个简单的关键字,
- 它生成代码(如上面的@Mat所述),
- 它有助于
connect
机制认识到它确实是一个signal
,并且 - 它使您的信号成为"更大"系统的一部分,其中信号和响应(插槽)可以同步或异步执行,也可以排队,具体取决于信号的发射位置和方式。这是信号/时隙系统非常有用的功能。
整个信号/时隙系统与简单的函数调用是不同的习语。我相信它源于观察者模式。signal
和slot
之间还有一个主要区别:信号不必实现,而插槽必须实现!
你走在街上,看到一所房子着火了(信号)。您拨打 911(将火灾信号与 911 响应插槽连接)。信号仅发出,而插槽由消防部门实施。可能不精确,但你明白了。让我们看一下 OP 的例子。
一些后端对象知道已经取得了多少进展。所以它可以简单地emit progressNotification(...)
信号。由显示实际进度条的类来获取此信号并在其上执行。但是视图如何连接到这个信号呢?欢迎来到Qt的信号/插槽系统。现在可以设想一个管理器类(通常是各种小部件),它由视图对象和数据计算对象(两者都QObjects
)组成,可以执行connect (m_myDataEngine, &DataEngine::progressNotification, m_myViewObj, &SimpleView::displayProgress)
。
我们先不讨论经理类的设计方面,但只要说这就是信号/插槽系统大放异彩的地方就足够了。我可以专注于为我的应用程序设计一个非常干净的体系结构。并非总是如此,但很多时候,我发现我只是发出信号,而是实现插槽。
如果可以在不发射信号的情况下使用/调用信号方法,那么这必然意味着您从一开始就不需要将该函数作为信号。
第二个选项意味着您始终知道函数名称和函数参数是什么,并且您将其发送到的对象由该特定函数知道。这两种情况并不总是正确的,因此这是制作插槽和信号的两个主要原因。"引擎盖下"信号和插槽机制只是一个表格,其中包含指向连接的每个功能的指针。
另外,看看这个pdf,它非常清楚地解释了信号和插槽机制的性质:http://www.elpauer.org/stuff/a_deeper_look_at_signals_and_slots.pdf