对于 Qt 中的信号和槽,delete 和 deleteLater 如何工作?

发布于 2024-10-15 21:17:45 字数 703 浏览 6 评论 0原文

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

有时我想摆脱这两个物体。他们不再发出任何信号或任何东西。我希望他们消失。 好吧,我想,我会用

delete obj1; delete obj2;

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

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

什么是“待处理事件”? 这是否意味着,当我调用 delete 时,已经有一些“待处理事件”需要传递,它们可能会导致崩溃,而我无法真正检查是否有任何事件?

假设我打电话:

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

为了安全。

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

There is an object of class QNetworkReply. There is a slot (in some other object) connected to its finished() signal. Signals are synchronous (the default ones). There is only one thread.

At some moment of time I want to get rid of both of the objects. No more signals or anything from them. I want them gone.
Well, I thought, I'll use

delete obj1; delete obj2;

But can I really?
The specs for ~QObject say:

Deleting a QObject while pending events are waiting to be delivered can cause a crash.

What are the 'pending events'?
Could that mean that while I'm calling my delete, there are already some 'pending events' to be delivered and that they may cause a crash and I cannot really check if there are any?

So let's say I call:

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

To be safe.

But, am I really safe? The deleteLater adds an event that will be handled in the main loop when control gets there. Can there be some pending events (signals) for obj1 or obj2 already there, waiting to be handled in the main loop before deleteLater will be handled? That would be very unfortunate. I don't want to write code checking for 'somewhat deleted' status and ignoring the incoming signal in all of my slots.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

若沐 2024-10-22 21:17:45

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

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

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

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

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

Deleting QObjects is usually safe (i.e. in normal practice; there might be pathological cases I am not aware of atm), if you follow two basic rules:

  • Never delete an object in a slot or method that is called directly or indirectly by a (synchronous, connection type "direct") signal from the object to be deleted.
    E.g. if you have a class Operation with a signal Operation::finished() and a slot Manager::operationFinished(), you don't want delete the operation object that emitted the signal in that slot. The method emitting the finished() signal might continue accessing "this" after the emit (e.g. accessing a member), and then operate on an invalid "this" pointer.

  • Likewise, never delete an object in code that is called synchronously from the object's event handler. E.g. don't delete a SomeWidget in its SomeWidget::fooEvent() or in methods/slots you call from there. The event system will continue operating on the already deleted object -> Crash.

Both can be tricky to track down, as the backtraces usually look strange (Like crash while accessing a POD member variable), especially when you have complicated signal/slot chains where a deletion might occur several steps down originally initiated by a signal or event from the object that is deleted.

Such cases are the most common use case for deleteLater(). It makes sure that the current event can be completed before the control returns to the event loop, which then deletes the object. Another, I find often better way is defer the whole action by using a queued connection/QMetaObject::invokeMethod( ..., Qt::QueuedConnection ).

意中人 2024-10-22 21:17:45

您引用的文档的接下来两行给出了答案。

来自 ~QObject

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

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

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

The next two lines of your referred docs says the answer.

From ~QObject,

Deleting a QObject while pending events are waiting to be delivered can cause a crash. You must not delete the QObject directly if it exists in a different thread than the one currently executing. Use deleteLater() instead, which will cause the event loop to delete the object after all pending events have been delivered to it.

It specifically says us to not to delete from other threads. Since you have a single threaded application, it is safe to delete QObject.

Else, if you have to delete it in a multi-threaded environment, use deleteLater() which will delete your QObject once the processing of all the events have been done.

心房的律动 2024-10-22 21:17:45

您可以阅读有关Delta 对象规则之一的内容来找到问题的答案其中指出:

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

分段:

QObject 的核心是支持
发信号时删除。为了
利用它你只需要
确保你的对象不会尝试
之后访问其自己的任何成员
正在被删除。然而,大多数 Qt
对象不是这样写的,并且
没有要求他们必须是
任何一个。正因如此,
建议您随时致电
如果您需要删除一个,请使用deleteLater()
对象在其信号之一期间,
因为“删除”很可能会
只是让应用程序崩溃。

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

一般来说,我认为 Delta 对象规则是每个 Qt 开发人员必读的内容。这是极好的阅读材料。

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.

仙女 2024-10-22 21:17:45

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

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

As far as I know, this is mainly an issue if the objects exist in different threads. Or maybe while you are actually processing the signals.

Otherwise deleting a QObject will first disconnect all signals and slots and remove all pending events. As a call to disconnect() would do.

爺獨霸怡葒院 2024-10-22 21:17:45

即使 deleteLater 可能还不够。

想象一下,您的主线程事件循环当前正在使用对象 X 为某些事件/插槽提供服务,但它会返回到事件循环(当使用 Qt 脏接口发出信号/等待/睡眠时可能会发生这种情况)。主线程继续服务另一个在 X 的父级上删除或请求 deleteLater 的事件。如果是 deleteLater,主线程仍然有可能返回到事件循环,然后删除父级X 的

父级被删除时,它会删除 X。这将导致主线程返回到它正在服务的早期事件/槽,并在尝试使用已删除的对象 X 时崩溃

deleteLater 还不够。

Even deleteLater may not be sufficient.

Imagine that your main thread event loop is currently servicing some event/slot using object X, but it goes back to the event loop (This can happen when emitting signal/wait/sleep with Qt dirty interface). The main thread continues to service another event that delete or request the deleteLater on the parent of X. If it is deleteLater, it is still possible that the main thread will return to the event loop then delete the parent of X.

When parent of X is deleted, it would delete X. This would cause the main thread to return back to the earlier event/slot that it was servicing part way, and crash when trying to use the deleted object X.

Just pointing out the deleteLater is not sufficient.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文