【问题标题】:Qt using boost::shared_ptr in a signal/slotQt 在信号/槽中使用 boost::shared_ptr
【发布时间】:2011-06-16 00:02:56
【问题描述】:

是否有可能,如果可以,我如何在 Qt 中创建一个信号/插槽,它是对 shared_ptr 的 const 引用?我想要一个看起来像这样的信号:

void signal( shared_ptr<SomeClass> const & )

我知道如何在没有常量引用的情况下执行此操作,这只是 shared_ptr<SomeClass> 类型,但出于效率* 的原因,我想避免复制。引用类型的相同语法不起作用:

Q_DECLARE_METATYPE(shared_ptr<SomeClass> const &)
qRegisterMetaType<shared_ptr<SomeClass> const&>();

许多标准 API 都有 QString const &amp;,所以我认为这从根本上是可能的,但我无法弄清楚语法。


**性能的最大问题不是复制时间,而是当对象被复制到每个接收器时互斥锁/解锁的数量——它们很多。由于多个线程使用该对象,这会引入明显的减速/瓶颈。如果 shared_ptr 实际上只使用原子操作,那么这个成本也是微不足道的,但是关于信号中 const 引用的一般问题仍然存在。*

【问题讨论】:

  • 不将 shared_ptr 作为 const ref 传递是否首先否定了 shared_ptr 的意义?只需将 const ref / 指针传递给相关对象即可。
  • @messenger,const 引用是为了避免在调用函数时复制 shared_ptr 的优化。这是一种相当常见的模式。如果你总是按值传递(信号或普通函数)shared_ptr 与普通指针相比变得相当昂贵。此处不能使用普通指针,因为不能保证“发射”端的对象的生命周期。
  • @edA-qa mort-ora-y:boost shared_ptr 是否使用互斥体???为什么???你需要的不是原子增量/减量吗?
  • @edA-qa,如果不能保证生命周期,那么您为什么尝试使用引用而不是复制指针对象?如果对象在另一端被销毁,您不会得到一个悬空引用吗?
  • @edA-qa,哦,现在我明白了。您只想制作一个通过引用传递给每个连接的插槽的副本。现在这是一个好主意,但不幸的是 Qt 以不同的方式做事。我想你可以通过首先将指针的副本传递给接收线程中的某个代理对象来解决它,这反过来会使用直接连接向所有接收器发出信号,但我担心双重信号开销会破坏非常避免不必要的复制。

标签: c++ qt boost


【解决方案1】:

到目前为止,我发现我可以简单地做到这一点:

Q_DECLARE_METATYPE(shared_ptr<SomeClass>)
qRegisterMetaType<shared_ptr<SomeClass> >();
qRegisterMetaType<shared_ptr<SomeClass> >("std::shared_ptr<SomeClass>const&");

我现在正在尝试验证这是否真的可以正常工作。此处的文档不清楚实际发生的情况。似乎信号/插槽中的 const 引用类型将被编组为普通的shared_ptr&lt;SomeClass&gt;,在这里完全可以。但是,有某种保证这应该可以正常工作会很好。

我觉得简单的shared_ptr&lt;SomeClass&gt; 版本就足够了,而干扰信号的是boost 命名空间。第二个版本似乎只是为了在全局命名空间中注册信号以便于使用。


通过测试,我可以确认const &amp; 部分在排队连接中被完全忽略。每个连接的插槽都会获得该对象的一个​​新副本。这是非常不幸的。 :(

进一步的测试表明,&amp; 用于插槽,但以一种不同寻常的方式。对象的副本仍然为排队连接创建,但如果您不使用引用,将为调用创建另一个副本。

因此,尽管每个connect 最终都会复制排队连接的数据,但引用仍然有点帮助。此外,如果您确实在本地发送了一些信号(相同的线程),您可能会避免更多的复制。

【讨论】:

  • 1) 您应该可以完全省略 qRegisterMetaType() 中的字符串参数。 2)信号/插槽和元类型的东西永远不需要 const ref,只是普通类型。还有常量参考。和按值传递等价于信号/槽机制:您可以通过 SIGNAL(signal(const T&)) 或 SIGNAL(signal(T)) 连接一个信号 void signal( const T& ),签名归一化为“signal( T)”无论如何。当编组时,例如对于排队的连接,值是通过复制而不是通过存储 const ref 来存储的。
  • @Frank,对于排队,我可以复印一份。我关心发送给接收者的问题。如果我使用引用将许多插槽连接到一个信号,是否会在所有情况下都使用对同一对象的引用(即同一线程中的所有插槽)?
  • qa,我刚刚检查了源代码,看起来 Qt 为每个排队的连接(即每个接收器)创建了一个副本。如果您愿意,请检查循环发送者的 QMetaObject::activate() 和执行实际复制的 QMetaObject::queued_activate(),所有这些都在 qobject.cpp 中。
  • 您必须在字符串中添加std:: 命名空间,因为using namespace 不会影响。
【解决方案2】:

根据此问题Argument type for Qt signal and slot, does const reference qualifiers matters? 中的答案之一,对于排队连接,无论您如何连接信号和插槽,都会复制对象。

由于您使用多个线程,因此连接已排队。如果您担心互斥量成本,请尝试直接使用 SomeClass *。

【讨论】:

  • 跨线程复制完全没问题,只要它只是使用复制构造函数(或 operator=)。它在一个线程中,我不想复制到所有接收者。
【解决方案3】:

为了完整起见,我认为应该提到 Qt 有自己的共享指针实现,QSharedPointer,其行为与 std::shared_ptr 完全相同。最终你仍然需要注册你的参数类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-11
    • 2021-02-12
    • 2011-10-29
    • 1970-01-01
    相关资源
    最近更新 更多