81

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

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

delete obj1; delete obj2;

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

在挂起的事件等待交付时删除 QObject 可能会导致崩溃。

什么是“未决事件”?这是否意味着当我打电话给 mydelete时,已经有一些“待处理的事件”要交付,它们可能会导致崩溃,我无法真正检查是否有任何事件?

所以假设我打电话:

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

为了安全。

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

4

4 回答 4

74

如果您遵循两个基本规则,删除 QObjects 通常是安全的(即在正常实践中;可能存在我不知道 atm 的病理情况):

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

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

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

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

于 2011-02-03T17:22:29.927 回答
23

您引用的文档的下两行说明了答案。

~QObject

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

它特别告诉我们不要从其他线程中删除。由于您有一个单线程应用程序,因此删除QObject.

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

于 2011-02-04T05:17:53.713 回答
14

You can find answer to your question reading about one of the Delta Object Rules which states this:

Signal Safe (SS).
It must be safe to call methods on the object, including the destructor, from within a slot being called by one of its signals.

Fragment:

At its core, QObject supports being deleted while signaling. In order to take advantage of it you just have to be sure your object does not try to access any of its own members after being deleted. However, most Qt objects are not written this way, and there is no requirement for them to be either. For this reason, it is recommended that you always call deleteLater() if you need to delete an object during one of its signals, because odds are that ‘delete’ will just crash the application.

Unfortunately, it is not always clear when you should use ‘delete’ vs deleteLater(). That is, it is not always obvious that a code path has a signal source. Often, you might have a block of code that uses ‘delete’ on some objects that is safe today, but at some point in the future this same block of code ends up getting invoked from a signal source and now suddenly your application is crashing. The only general solution to this problem is to use deleteLater() all the time, even if at a glance it seems unnecessary.

Generally I regard Delta Object Rules as obligatory read for every Qt developer. It's excellent reading material.

于 2011-02-03T20:55:03.293 回答
4

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

否则删除 QObject 将首先断开所有信号和插槽并删除所有未决事件。正如对 disconnect() 的调用那样。

于 2011-02-03T17:27:34.293 回答