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

Using emit vs calling a signal as if it's a regular function in Qt

提问人:sashoalm 提问时间:4/15/2012 最后编辑:sashoalm 更新时间:1/8/2017 访问量:88778

问:

假设我有这个信号:

signals:
    void progressNotification(int progress);

我最近才知道Qt中的emit关键字。到目前为止,我曾经通过像常规函数一样调用信号来执行信号。因此,而不是:

emit progressNotification(1000 * seconds);

我会写:

progressNotification(1000 * seconds);

这样调用它们似乎有效,并且所有连接的插槽都会执行,那么使用 emit 关键字是否会导致不同的行为,或者它只是语法糖?

C++ QT的

评论

19赞 Christian Rau 4/15/2012
+1 从来不知道是不需要的。不过,奇怪的是,你在直接调用信号很久之后才知道,因为信号槽系统是Qt最先要学习的东西之一。emitemit

答:

-3赞 Evert 4/15/2012 #1

第二个选项意味着您始终知道函数名称和函数参数是什么,并且该特定函数知道您发送到的对象。这两种情况并不总是正确的,所以这是产生插槽和信号的两个主要原因。“在引擎盖下”,信号和插槽机制只是一个表格,其中包含指向连接的每个功能的指针。

另外,请看这个pdf,它非常清楚地解释了信号和插槽机制的性质:http://www.elpauer.org/stuff/a_deeper_look_at_signals_and_slots.pdf

评论

0赞 Mat 4/15/2012
这两种方式都需要知道信号名称及其参数 - 你正在发射它,你怎么能发出你不知道的东西?两者也具有相同的语义,它们是相同的。
1赞 Christian Rau 4/15/2012
也许您正在用直接插槽呼叫搞砸信号呼叫?但我不得不承认,一开始我也想知道问题的标题,因为我从来不知道这只是一个无操作。但即使在这种情况下,阅读问题正文也应该已经弄清楚了,所以 -1。emit
105赞 Mat 4/15/2012 #2

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

(定义保护是允许您通过QMake配置选项将Qt与其他具有冲突名称的框架一起使用。no_keywords

评论

17赞 Michael Burr 4/15/2012
你知道是否曾经有过一个实际上比什么都没做过的实现(或计划中的实现)吗?我发现在这种情况下拥有“句法糖”只会让新手感到困惑(或者至少当我还是 Qt 新手用户时)——伪关键字似乎正在发生一些神奇或重要的事情,当它什么都不做时——所有的魔法都发生在一个常规的旧函数中,它创造了(是 Qt 信号和插槽的魔力)。 是不必要的装饰,除了看起来很重要之外什么也没做。emitemitmocmocemit
16赞 Mat 4/15/2012
排放不仅仅是“装饰”。 告诉读取调用的人魔术即将发生(即这将触发此类可能从未听说过的对象中的代码,并且这些调用可能是同步的或异步的),如果省略关键字,这基本上会完全丢失。使用它。它是自动记录的。“新手”应该阅读文档和教程,并且总是在那里(无论如何在官方文档中)。发现你可以调用该函数应该在你“看到曙光”之后发生 - 那时你不再是新手了。emitemit
21赞 Michael Burr 4/15/2012
嗯,我不确定我是否同意你关于“关键字”的价值。我想如果需要明确函数调用是一个信号,我更愿意使用命名约定。emit
2赞 Mat 4/15/2012
好吧,我从根本上不同意这种:)强制命名约定是你可以在项目/工作场所自己做的事情,Qt不会阻止这一点。Qt不会强迫你使用“关键字”,甚至允许你在它与代码的其他部分发生冲突时将其关闭。在我看来,关键字方法更好 - 编译器无法帮助您强制执行命名策略,但它会捕获拼写错误的 .emit
16赞 Michael Burr 4/15/2012
需要明确的是 - 我并不主张使用命名约定 - 只是如果 psuedo-keyword-comment 的原因是明确一个信号正在被调用,那么命名约定也可以做同样的事情,没有神秘感,并且有类似的好处。命名约定不能由Qt强制执行(实际上,可以强制执行 - 但我也不提倡这样做),但Qt不能强制使用任何一个。虽然如果存在名称冲突,您可以“关闭”,但如果您有一堆使用它的源文件(不必要地启动),这没有多大帮助。emitmocemitemit
4赞 NameRakes 1/8/2017 #3

18个月后......我从@Mat的回答下发表评论开始,很快就用完了。因此,答案就是这样。

IMO 既不是句法糖,也不是一个简单的关键字,从某种意义上说emit

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

整个信号/时隙系统与简单的函数调用不同。我相信它源于观察者模式。a 和 a 之间还有一个主要区别:信号不必实现,而插槽必须实现!signalslot

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

一些后端对象知道已经取得了多少进展。所以它可以简单地发出信号。由显示实际进度条的类来获取此信号并对其执行。但是视图如何连接到这个信号呢?欢迎来到Qt的信号/时隙系统。现在可以设想一个管理器类(通常是某种小部件),它由视图对象和数据计算对象(两者都是)组成,可以执行。emit progressNotification(...)QObjectsconnect (m_myDataEngine, &DataEngine::progressNotification, m_myViewObj, &SimpleView::displayProgress)

我们暂且不讨论管理器类的设计方面,但只要说这是信号/插槽系统的亮点就足够了。我可以专注于为我的应用程序设计一个非常干净的架构。并非总是如此,但很多时候,我发现我只是发出信号,但实现了插槽

如果可以使用/调用信号方法而不发出信号,那么它必然意味着您从一开始就不需要该函数作为信号

评论

10赞 derM - not here for BOT dreams 4/3/2018
不,确实只是一个空的宏,纯粹是可选的。关键字和由 moc 处理的关键字并非如此。 用于提供函数的实现,用于创建元对象条目,以便与宏或QML一起找到它。 是句法 Suggar。如果你写信(但也许是你的同事),你仍然无法连接到.emitsignalslotsignalslotSLOT(MySlot())emitemit i++;i++
0赞 FrankHB 12/19/2020
确实,该模式与 C++ 函数调用不同,但通常与函数调用没有区别。即使是 C++ 抽象机器也允许通过假如规则在后台进行异步调用,只是没有编译器在实践中隐式生成代码。(但是,提出了明确的策略,c.f. C++ 执行器。从本质上讲,Qt信号没有其他作用。更糟糕的是,调度机制完全由框架(而不是编译器)隐含,但程序员无法扩展。
0赞 FrankHB 12/19/2020
There is practically no chance to tune the implementation because both the programmer and the compiler know nothing about the implementation details hidden in the framework. If I really need to call asynchronously, I'd implement the calls in a saner way (with some homebrew metadata management), rendering the signals and slots no more useful than multicasting events or callable object containers (like boost.signal[2]). There is no room for a special anywhere, then.emit