5

我想知道emit。它会重复数据吗?

如果我必须通过它传递一个 1MB 字节数组,那么内存中将存在多少个该字节数组的副本?

4

3 回答 3

6

这取决于您的信号如何连接到插槽。

如果您使用默认连接,Qt::DirectConnection并且两个QObjects 在同一个线程中,则根据您定义参数的方式(按引用传递或按传递),参数将被视为以通常方式调用函数-价值)。

如果您使用连接Qt::QueuedConnection或在线程之间连接,则参数参数将被复制并移交给特殊的QEvent并添加到接收线程的事件队列中。然后,当它有机会时,这将由接收线程处理。

于 2013-09-10T11:46:30.300 回答
4

这取决于如何建立连接以及如何传递参数。

  1. 按值传递(即signals: void foo(Bar);

    • 一个普通的 C++ 函数调用会生成一个副本(将调用者复制Bar到被调用者)。
    • 直接连接的信号/插槽调用将生成两个副本:调用生成的 emit 函数moc(一个副本,与上面的相同),“打包”参数并调用QMetaObject::activate最终调用您的类的qt_static_metacall调用插槽 (作为正常的函数调用),产生第二个副本。
    • 队列连接的信号/槽调用将生成三个副本。在调用上下文中,emit 函数如上所述调用自身,并将参数复制到事件中。在接收者上下文中,qt_static_metacall槽的调用相同。
  2. 通过 const 引用传递(即signals: void foo(Bar const&);

    • 普通 C++ 函数调用:无副本。
    • 直连信号/槽调用:无副本。
    • 队列连接的信号/槽调用:一份,一份放入上述事件对象
  3. 通过(非常量)引用(即signals: void foo(Bar&);

    • 普通 C++ 函数调用:无副本。
    • 直连信号/槽调用:无副本。
    • 队列连接的信号/插槽调用:不可能(AFAIK)。

文档中提到了复制到事件中:

对于排队连接,参数必须是 Qt 元对象系统已知的类型,因为Qt 需要复制参数以将它们存储在幕后的事件中。

现在真正的问题是:这重要吗?

如果您使用的是使用隐式共享的 Qt 容器类,则在通常情况下并不重要 - 除非需要,否则不会复制有效负载。因此,副本通常不会对整体性能产生重大影响。

如果您不使用隐式共享,那么按值传递大量对象可能不是正确的选择,但插槽调用会比普通函数调用更昂贵。直接连接的槽和 pass-by-const-ref 的行为与普通函数调用相同,但排队的槽会更昂贵。

于 2013-09-10T11:45:54.483 回答
0

All Qt containers implement "copy on write" pattern. So when you are passing container by value nothing is copied until you will use none-const method of this container.

So bottom line is: it is safe (it will not double memory consumption) to pass large qt containers through signals and slots.

于 2013-09-10T11:53:28.710 回答