对于 Qt 中的信号和槽,delete 和 deleteLater 如何工作?
有一个 QNetworkReply 类的对象。有一个槽(在其他对象中)连接到它的 finish() 信号。信号是同步的(默认信号)。只有一个线程。
有时我想摆脱这两个物体。他们不再发出任何信号或任何东西。我希望他们消失。 好吧,我想,我会用
delete obj1; delete obj2;
但我真的可以吗? ~QObject 的规范说:
在待处理事件等待传递时删除 QObject 可能会导致崩溃。
什么是“待处理事件”? 这是否意味着,当我调用 delete
时,已经有一些“待处理事件”需要传递,它们可能会导致崩溃,而我无法真正检查是否有任何事件?
假设我打电话:
obj1->deleteLater(); obj2->deleteLater();
为了安全。
但是,我真的安全吗? deleteLater
添加一个事件,当控制权到达主循环时,该事件将在主循环中处理。是否存在 obj1
或 obj2
的一些待处理事件(信号),等待在主循环中处理 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
删除 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 ).
您引用的文档的接下来两行给出了答案。
来自 ~QObject,
它特别告诉我们不要从其他线程中删除。由于您有一个单线程应用程序,因此删除
QObject
是安全的。否则,如果您必须在多线程环境中删除它,请使用
deleteLater()
,一旦所有事件的处理完成,它将删除您的QObject
。The next two lines of your referred docs says the answer.
From ~QObject,
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 yourQObject
once the processing of all the events have been done.您可以阅读有关Delta 对象规则之一的内容来找到问题的答案其中指出:
分段:
一般来说,我认为 Delta 对象规则是每个 Qt 开发人员必读的内容。这是极好的阅读材料。
You can find answer to your question reading about one of the Delta Object Rules which states this:
Fragment:
Generally I regard Delta Object Rules as obligatory read for every Qt developer. It's excellent reading material.
据我所知,如果对象存在于不同的线程中,这主要是一个问题。或者也许当您实际处理信号时。
否则,删除 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.
即使
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.