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



假设我有这个信号:

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();

emitQt/qobjectdefs.h 中定义(无论如何都是源代码的开源风格),如下所示:

#ifndef QT_NO_EMIT
# define emit
#endif

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

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

IMO emit既不是句法糖也不是一个简单的关键字,

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

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

你走在街上,看到一所房子着火了(信号)。您拨打 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

最新更新