emit会复制其参数吗?

时间:2013-09-10 11:31:41

标签: qt

我想知道emit。它是否重复数据?

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

3 个答案:

答案 0 :(得分:5)

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

如果您使用默认连接Qt::DirectConnection,并且两个QObject在同一个线程中,那么参数将被视为您按照常规方式调用函数定义参数(按引用传递或按值传递)。

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

答案 1 :(得分:3)

这取决于连接的方式以及传递参数的方式。

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

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

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

    • 普通C ++函数调用:无副本。
    • 直接连接信号/插槽调用:无副本。
    • 队列连接信号/插槽调用:不可能(AFAIK)。
  4. the docs中提到了事件的副本:

      

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

    现在真正的问题是:这有关系吗?

    如果您正在使用使用implicit sharing的Qt容器类,那么在通常情况下它确实无关紧要 - 除非需要,否则无法复制有效负载。因此,副本一般不会对整体表现产生重大影响。

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

答案 2 :(得分:0)

所有Qt容器都实现“copy on write”模式。因此,当您按值传递容器时,不会复制任何内容,直到您将使用此容器的非const方法。

所以底线是:通过信号和插槽传递大型qt容器是安全的(不会使内存消耗量增加一倍)。