delete和deleteLater如何处理Qt中的信号和插槽?

时间:2011-02-03 15:39:54

标签: qt qt-signals slot

有一个QNetworkReply类的对象。有一个插槽(在某些其他对象中)连接到其finished()信号。信号是同步的(默认值)。只有一个主题。

在某些时刻,我想摆脱两个对象。没有更多的信号或任何东西。我希望他们走了。 好吧,我想,我会用

delete obj1; delete obj2;

但我真的可以吗? ~QObject的规格说:

  

等待传递待处理事件时删除QObject可能会导致崩溃。

什么是“待处理事件”? 这是否意味着当我调用我的delete时,已经有一些“未决事件”要发送,并且它们可能会导致崩溃,我无法确定是否有任何事件?

所以我想说我打电话:

obj1->deleteLater(); obj2->deleteLater();

为了安全起见。

但是,我真的很安全吗? deleteLater添加一个事件,当控制到达时,将在主循环中处理。是否有obj1obj2已经存在的待处理事件(信号),等待在 deleteLater处理之前在主循环中处理?那将是非常不幸的。我不想编写“有点删除”状态的代码检查,并忽略所有插槽中的传入信号。

4 个答案:

答案 0 :(得分:66)

如果您遵循两个基本规则,删除QObject通常是安全的(即在正常情况下;可能存在我不知道的病态病例):

  • 永远不要删除由要删除的对象的(同步,连接类型“直接”)信号直接或间接调用的插槽或方法中的对象。 例如。如果你有一个带有Operation :: finished()信号的类操作和一个插槽Manager :: operationFinished(),你不希望删除在该插槽中发出信号的操作对象。发出finished()信号的方法可能会在发出后继续访问“this”(例如访问成员),然后对无效的“this”指针进行操作。

  • 同样,永远不要删除从对象的事件处理程序中同步调用的代码中的对象。例如。不要删除SomeWidget :: fooEvent()或从那里调用的方法/槽中的SomeWidget。事件系统将继续对已删除的对象进行操作 - >崩溃。

两者都很难追踪,因为回溯通常看起来很奇怪(就像访问POD成员变量时崩溃一样),尤其是当您有复杂的信号/槽链时,可能会发生删除最初由信号启动的几个步骤来自被删除对象的事件或事件。

这种情况是deleteLater()最常见的用例。它确保当前事件可以在控件返回到事件循环之前完成,然后事件循环将删除该对象。另一个,我发现通常更好的方法是使用排队连接/ QMetaObject :: invokeMethod(...,Qt :: QueuedConnection)推迟整个操作。

答案 1 :(得分:20)

您推荐文档的下两行说明了答案。

来自~QObject

  

等待传递待处理事件时删除QObject可能会导致崩溃。如果QObject存在于与当前正在执行的线程不同的线程中,则不得直接删除它。使用deleteLater()代替,这将导致事件循环在所有挂起事件都已传递后删除对象它。

它明确告诉我们不要从其他线程中删除。由于您有一个单线程应用程序,因此可以安全地删除QObject

否则,如果您必须在多线程环境中删除它,请使用deleteLater(),这将在完成所有事件的处理后删除QObject

答案 2 :(得分:13)

你可以找到你的问题的答案,阅读Delta Object Rules中的一个陈述:

  

信号安全(SS)。
  它必须是安全的   对象的调用方法,包括   析构函数,来自一个插槽   被其中一个信号调用。

片段:

  

核心,QObject支持存在   发信号时删除为了   利用它,你只需要   确保你的对象没有尝试   之后访问任何自己的成员   被删除。但是,大多数Qt   对象不是这样写的,而且   他们没有要求   无论是。因此,它是   建议你随时打电话   如果你需要删除一个deleteLater()   在其中一个信号中对象,   因为赔率是'删除'会   只是崩溃了应用程序。

     

不幸的是,它并不总是很清楚   什么时候应该使用'删除'vs   deleteLater()。也就是说,事实并非如此   总是明显的是代码路径有一个   信号源。通常,你可能有一个   使用“删除”的代码块   一些今天安全的物品,但是   在未来的某个时刻同样如此   代码块最终被调用   从一个信号源,现在突然   你的应用程序崩溃了。唯一的   这个问题的一般解决方案是   偶尔使用deleteLater()   如果一目了然,似乎没必要。

通常我认为 Delta对象规则是每个Qt开发人员必读的。这是一本很好的阅读材料。

答案 3 :(得分:2)

据我所知,如果对象存在于不同的线程中,这主要是一个问题。或者可能在您实际处理信号时。

否则删除QObject将首先断开所有信号和插槽,并删除所有待处理事件。正如调用disconnect()那样。