使用发射与调用信号,就好像它是Qt中的常规函数​​一样


97

假设我有这个信号:

signals:
    void progressNotification(int progress);

我直到最近才了解到Qt中的generate关键字。到目前为止,我以前只是通过像常规函数一样调用信号来执行信号。所以代替:

emit progressNotification(1000 * seconds);

我会写:

progressNotification(1000 * seconds);

像这样调用它们似乎可行,并且所有连接的插槽都将执行,所以使用send关键字是否会导致不同的行为,或者仅仅是语法糖?


17
+1从不知道emit是不需要的。但是,很奇怪,您emit在直接调用信号后不久就了解了,因为信号插槽系统是有关Qt的第一个要学习的知识。
Christian Rau 2012年

Answers:


88

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_keywordsQMake config选项将Qt与具有冲突名称的其他框架一起使用。)


14
您是否知道是否曾经实施过一项(或计划中的)实施emit,但实际上做的比什么都不做?我发现在这种情况下使用“语法糖”只会使新手感到困惑(或者至少当我是Qt新手时我感到困惑)- emit伪关键字似乎在发生什么不可思议的或重要的事情,而它却什么也没做全部-所有魔术都在moc创建的常规旧函数中发生(moc是Qt信号和插槽的魔术)。emit是不必要的装饰,什么也没做,但看起来很重要。
Michael Burr '04年5

12
发射不是 “只是装饰”。emit告诉阅读呼叫的人魔术即将发生(即,这将触发该类可能从未听说过的对象中的代码,并且这些调用可能是同步的或异步的),如果省略关键字,则基本上完全会丢失。用它。它是自动文档。“新手”应该阅读文档和教程,并且emit始终存在(无论如何在官方文档中)。发现您可以调用该函数应该在“见识了”之后发生-此时您不再是新手。
2012年

19
嗯,我不确定我是否同意emit“关键字”的价值。我认为如果需要明确指出函数调用是信号,那么我宁愿使用命名约定。
Michael Burr

2
好吧,我完全不同意:)强制命名约定是您可以在项目/工作场所中做的事情,Qt并不能阻止这一点。Qt不会强迫您使用“关键字”,甚至允许您在与代码其他部分冲突的情况下将其关闭。在我看来,关键字方法更好-编译器无法帮助您实施命名策略,但会遇到拼写错误的错误emit
2012年

15
需要明确的是-我不是在提倡使用命名约定-只是如果emit psuedo-keyword-comment 的原因是要明确表明正在调用信号,那么命名约定可以做到这一点,而没有神秘并具有类似的好处。Qt不能强制执行命名约定(实际上,moc可以强制执行它-但我不主张二者之一),但是Qt不能强制使用emit两者。而且,emit如果发生名称冲突,您可以“关闭” ,但是如果您有一堆正在使用它的源文件(不必要地引导),那将无济于事。
Michael Burr '04年5

2

18个月后...我从@Mat的回答开始评论,然后很快就没了空间。因此答案。

IMO emit既不是语法糖也不是简单的关键字,

  1. 它生成代码(如上面@Mat所述),
  2. 它有助于该connect机制识别出确实是一个signal,并且
  3. 它使您的信号成为“更大”系统的一部分,其中信号和响应(插槽)可以同步或异步执行,也可以排队,具体取决于发出信号的位置和方式。这是信号/插槽系统的一个非常有用的功能。

整个信号/插槽系统是与简单函数调用不同的惯用法。我认为这源于观察者模式。a signal和a 之间还有一个主要区别slot不一定要实现信号,而插槽必须是!。

您正走在街上,看到一栋房屋着火(信号)。您拨打911(将火警信号与911响应槽连接)。仅发出信号,而该插槽由消防部门实施。可能不精确,但您明白了。让我们看一下OP的示例。

一些后端对象知道已经取得了多少进展。因此它可以简单地emit progressNotification(...)发出信号。由显示实际进度条的类决定如何拾取此信号并对其执行。但是视图如何连接到该信号?欢迎使用Qt的信号/插槽系统。现在,可以执行一个经理类(通常是各种小部件),它可以由视图对象和数据计算对象(都为QObjects)组成connect (m_myDataEngine, &DataEngine::progressNotification, m_myViewObj, &SimpleView::displayProgress)

让我们不进入管理器类的设计方面,而足以说这是信号/插槽系统的亮点。我可以专注于为我的应用程序设计一个非常干净的体系结构。我发现并非总是,但经常,我只是发出信号而实现slot

如果可以在不发出信号的情况下使用/调用信号方法,则必然意味着您根本不需要使用该函数作为信号


6
不,emit确实只是一个空宏,并且是完全可选的。不是这样的关键字signal以及slot其中由商务部进行处理。signal用于提供功能的实现,slot用于创建元对象条目,以便通过SLOT(MySlot())宏或在QML中找到它。emit是句法建议。如果您写东西emit i++;(但也许是您的同事),也不会抱怨,但您仍然无法连接到i++
derM '18 -4-3

-5

第二种选择意味着您总是知道函数名称和函数参数是什么,并且将其发送到的对象被该特定函数所知道。这两种情况并不总是正确的,所以这就是为什么要制作时隙和信号的两个主要问题。“内部”信号和插槽机制只是一个表,其中包含指向所连接的每个功能的指针。

另外,请参阅此pdf文件,该文件非常清楚地说明了信号和时隙机制的性质:http//www.elpauer.org/stuff/a_deeper_look_at_signals_and_slots.pdf


两种方法都需要知道信号名称及其参数-您正在发射它,如何发射您不知道的东西?两者也具有相同的语义,它们是相同的。
2012年

1
也许您将直接呼叫与信号呼叫搞混了?但是我不得不承认,起初我也想知道问题的标题,因为我不知道emit这只是一个禁忌。但是即使在这种情况下,阅读问题正文也应清除所有内容,因此为-1。
Christian Rau 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.