何时使用deleteLater

时间:2014-03-13 10:52:40

标签: qt message-queue

假设我有以下代码段,在qto的析构函数中调用deleteLater是否可以安全地管理它可能管理的其他QT对象?

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MyQTObject qto;
    qto.show();
    return a.exec();
}

因为我已使用检漏器分析了类似的相似代码以及调用了deleteLater的所有对象,所以除非我用正常删除替换了呼叫,否则无法正确解除分配。 如果我已正确理解这一点,则deleteLater仅在QT消息​​队列中注册删除事件。这可能是在主要范围结束时调用qto的析构函数的问题,而QT消息循环已经从a.exec的返回结束了吗?因此,永远不会处理删除事件,实际上甚至没有被推入消息队列,因为没有?

5 个答案:

答案 0 :(得分:12)

这篇文章相当陈旧,但我希望在我自己提出这个问题时添加一些我希望遇到的答案。

gallery_media与异步操作结合使用非常有用。我认为,它最近可能会将信号连接到lambda函数。

假设您有一些deleteLater()要异步执行(在多线程意义上,就事件循环中的调度执行而言)。你可以这样做:

longComputation()

其中void MyClass::deferLongComputation() { QTimer* timer = new QTimer(); connect(timer, &QTimer::timeout, [this, timer](){this->longComputiation(); timer->deleteLater();}); timer->setSingleShot(true); timer->start(); } 负责在{1}}执行其职责后安全处置deleteLater(),并避免人们原本会发生的内存泄漏。

使用QTimer多线程可以使用相同的模式。

答案 1 :(得分:8)

据我了解,当您需要在插槽调用中删除对象时,最常使用deleteLater。如果在这种情况下使用delete并且从插槽返回时引用了对象,则会发生对未初始化内存的引用。

因此,deleteLater通过在事件循环上放置一条消息来请求删除该对象,该消息在某个时刻处理,从插槽返回时可以安全删除。

我希望在析构函数中使用deleteLater意味着对象可能超出范围,在其托管对象上调用deleteLater,但在事件循环有机会删除对象之前退出,如退出来自QApplication :: exec()将终止事件循环。

答案 2 :(得分:3)

这个问题已经过时了,但是我会把它留给下一代) 标记为答案的答复是正确的,但却很奇怪。 实际上你的问题包含一个正确的答案:

  

消息循环已经从a.exec返回结束?就这样   删除事件永远不会被处理,实际上甚至没有被推入   消息队列,因为没有。

这正是发生的事情。 deleteLater()所做的只是将删除事件发布到outter事件循环中。当事件被处理时 - 对象被删除。但是如果没有outter事件循环并且在执行流程中稍后没有遇到事件循环 - 事件将永远不会被发布,因此永远不会删除对象。

如果在对象的析构函数中调用deleteLater()并将对象放在堆栈上 - 当对象超出范围时调用deleteLater()。在您的示例中,当遇到main()函数的右括号时,会发生“超出范围”。但是,到那时,a.exec()(代表Qt App的主要事件循环)已经返回 - > 没有事件循环 - > deleteLater()被调用,但无法发布删除事件 - > 对象应该是“deletedLater”永远不会被删除...

关于“何时使用deleteLater()”部分:

Kuba Ober 回答:

  

一般来说,存在一系列狭隘的情况   应该使用deleteLater。很可能你根本就不应该使用   它...

不要听,整个答案绝对不正确。在阅读this article之后,你应该做些什么以及不应该做什么。虽然,它主要是关于Qt线程,但文章还讲述了ascynchronous编程(并且,正如 Emerald Weapon 所提到的,它正是为其创建的deleteLater())。

此外,智能指针和QObject父级所有权与使用deleteLater()的删除计划无关。这两种技术实际上都是在引擎盖下使用简单的delete操作。正如文章所示,并且翡翠武器的答案证明:delete无法解决问题deleteLater()。因此,如果您需要删除使用delete的对象,如果您需要安排删除对象,请使用deleteLater()

顺便说一句,如果你想使用deleteLater()智能指针,你可以指定删除器:

// Shared Pointer
QSharedPointer<MyObject> obj = 
        QSharedPointer<MyObject>(new MyObject, &QObject::deleteLater);
// Scoped Pointer
QScopedPointer<MyObject, QScopedPointerDeleteLater> customPointer(new MyObject);

最后,对于非子对象,在deleteLater()的析构函数中使用QObject是一个 NOT 错误。

答案 3 :(得分:1)

您是正确的deleteLater()命令仅由事件循环执行。

来自QObject的{​​{3}}:

  

安排此对象删除。

     

当控件返回事件时,对象将被删除   环。如果此函数是事件循环未运行   调用(例如,之前在对象上调用deleteLater()   QCoreApplication :: exec()),该对象将被删除一次   事件循环开始。如果在主事件循环之后调用deleteLater()   已停止,该对象将不会被删除。   从Qt 4.8开始,如果在一个居住在的对象上调用deleteLater()   线程没有运行事件循环,当对象被销毁时   线程完成。

     

注意进入和离开新事件循环(例如,通过打开模态   对话框)将不执行延迟删除;对于对象   删除后,控件必须返回事件循环   deleteLater()被调用。

     

注意:多次调用此函数是安全的;当。。。的时候   第一个延期删除事件已发送,任何待处理事件   对象将从事件队列中删除。

如果您希望在删除QObjects时删除所有子qto,请确保将qto作为父级创建。

答案 4 :(得分:1)

一般来说,应该使用deleteLater的一小部分情况。很可能你根本就不应该使用它。

对于非子对象,在QObject的析构函数中使用它是错误的。正如您所发现的,如果没有事件循环,QObject可能会被破坏。例如,在deleteLater Qt模块的对象析构函数中没有qtbase个调用。

这里必须要小心:例如,~QTcpServer()调用close()调用d->socketEngine->deleteLater(),但套接字引擎已经是服务器的子代,将被{{1删除无论如何

据我所知,~QObject()应该执行以下操作之一:

  • 使用智能指针,例如MyQTObjectQScopedPointer
  • 将对象作为常规(非指针)成员,
  • 使用原始指针并让对象成为它的子对象。