如何删除和deleteLater关于Qt中的信号和插槽工作?

有一个类QNetworkReply的对象。 有一个插槽(在其他一些对象)连接到它的完成()信号。 信号是同步的(默认的)。 只有一个线程。

在某个时候,我想摆脱这两个对象。 没有更多的信号或任何从他们。 我想让他们走了。 好吧,我想,我会用

delete obj1; delete obj2; 

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

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

什么是“未决事件”? 这是否意味着我打电话给我的delete ,已经有一些“待处理的事件”被传递,并可能导致崩溃,我不能真正检查是否有任何?

所以我想说我打电话给:

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

为了安全起见

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

如果你遵循两条基本规则,删除QObject通常是安全的(即在正常的实践中,可能有病理情况我不知道atm)

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

  • 同样,绝对不要删除从对象的事件处理程序中同步调用的代码中的对象。 例如,不要在其SomeWidget :: fooEvent()中删除SomeWidget,也不要在您从那里调用的方法/插槽中删除SomeWidget。 事件系统将继续在已经删除的对象上运行 – >崩溃。

跟踪往往很棘手,因为回溯通常看起来很奇怪(就像访问POD成员variables时的崩溃一样),特别是当你有复杂的信号/时隙链时,可能会发生一些信号或事件被删除的对象。

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

接下来的两行文章就是这样回答的。

从〜QObject ,

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

它具体说我们不要从其他线程删除。 由于你有一个单线程应用程序,所以删除QObject是安全的。

否则,如果您必须在multithreading环境中删除它,请使用deleteLater() ,一旦处理完所有事件,它将删除您的QObject

您可以在阅读关于Delta对象规则之一的问题中find答案:

信号安全(SS)。
调用对象上的方法(包括析构函数)从一个信号调用的某个槽中调用方法必须是安全的。

分段:

在它的核心,QObject支持信号被删除。 为了利用它,你只需要确保你的对象在删除后不会尝试访问它自己的任何成员。 但是,大多数Qt对象不是这样写的,也不需要它们。 因为这个原因,如果你需要在其中一个信号中删除一个对象的话,build议你总是调用deleteLater(),因为“delete”只会使应用程序崩溃。

不幸的是,当你使用'delete'和deleteLater()时,并不总是很清楚。 也就是说,代码path有一个信号源并不总是显而易见的。 通常情况下,您可能会有一段代码在当前安全的某些对象上使用“删除”,但是在将来的某个时刻,同一代码块最终会从信号源调用,现在突然您的应用程序崩溃了。 这个问题的唯一的一般解决scheme是使用deleteLater(),即使一眼看上去似乎没有必要。

一般来说,我认为Delta对象规则是每个Qt开发者都必须阅读的。 这是优秀的阅读材料。

据我所知,如果对象存在于不同的线程中,这主要是一个问题。 或者,也许你正在处理信号。

否则,删除一个QObject将首先断开所有的信号和插槽,并删除所有未决的事件。 作为disconnect()的调用会做。