Qt deleteLater 延迟删除原理

deleteLater 调用

事件发送

cpp 复制代码
void QObject::deleteLater()
{
    QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
}
  • 首先该对象继承QObject
  • 调用deleteLater, 内部会发送删除事件QCoreApplication::postEvent(this, new QDeferredDeleteEvent()) 到事件循环的队列中
  • 最终事件循环会调用 reciver->event(Qevent)

事件接收

cpp 复制代码
bool QObject::event(QEvent *e)
{
    switch (e->type()) {
    case QEvent::Timer:
        timerEvent((QTimerEvent*)e);
        break;

    case QEvent::ChildAdded:
    case QEvent::ChildPolished:
    case QEvent::ChildRemoved:
        childEvent((QChildEvent*)e);
        break;

    case QEvent::DeferredDelete:
        qDeleteInEventHandler(this);
        break;
      ...
  
    return true;
}

void qDeleteInEventHandler(QObject *o)
{
    delete o;
}
  • event(QEvent *e) 通过事件类型判断,是否为延迟删除类型:QEvent::DeferredDelete
  • 如果是延迟删除类型,则会调用qDeleteInEventHandler

事件发送实现

设置删除事件的 looplevel 和 eventLevel

cpp 复制代码
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
{
    Q_TRACE_SCOPE(QCoreApplication_postEvent, receiver, event, event->type());

    if (receiver == nullptr) {
        qWarning("QCoreApplication::postEvent: Unexpected null receiver");
        delete event;
        return;
    }

    auto locker = QCoreApplicationPrivate::lockThreadPostEventList(receiver);
    if (!locker.threadData) {
        // posting during destruction? just delete the event to prevent a leak
        delete event;
        return;
    }

    QThreadData *data = locker.threadData;

    // if this is one of the compressible events, do compression
    if (receiver->d_func()->postedEvents
        && self && self->compressEvent(event, receiver, &data->postEventList)) {
        Q_TRACE(QCoreApplication_postEvent_event_compressed, receiver, event);
        return;
    }

    if (event->type() == QEvent::DeferredDelete)
        receiver->d_ptr->deleteLaterCalled = true;

    if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) {
        // remember the current running eventloop for DeferredDelete
        // events posted in the receiver's thread.

        // Events sent by non-Qt event handlers (such as glib) may not
        // have the scopeLevel set correctly. The scope level makes sure that
        // code like this:
        //     foo->deleteLater();
        //     qApp->processEvents(); // without passing QEvent::DeferredDelete
        // will not cause "foo" to be deleted before returning to the event loop.

        // If the scope level is 0 while loopLevel != 0, we are called from a
        // non-conformant code path, and our best guess is that the scope level
        // should be 1. (Loop level 0 is special: it means that no event loops
        // are running.)
        int loopLevel = data->loopLevel;
        int scopeLevel = data->scopeLevel;
        if (scopeLevel == 0 && loopLevel != 0)
            scopeLevel = 1;
        static_cast<QDeferredDeleteEvent *>(event)->level = loopLevel + scopeLevel;
    }

    // delete the event on exceptions to protect against memory leaks till the event is
    // properly owned in the postEventList
    QScopedPointer<QEvent> eventDeleter(event);
    Q_TRACE(QCoreApplication_postEvent_event_posted, receiver, event, event->type());
    data->postEventList.addEvent(QPostEvent(receiver, event, priority));
    eventDeleter.take();
    event->posted = true;
    ++receiver->d_func()->postedEvents;
    data->canWait = false;
    locker.unlock();

    QAbstractEventDispatcher* dispatcher = data->eventDispatcher.loadAcquire();
    if (dispatcher)
        dispatcher->wakeUp();
}

通过looplevel 和 eventLevel 判断 确认是是否执行延迟删除事件

cpp 复制代码
 const bool allowDeferredDelete =
                (eventLevel > loopLevel
                 || (!eventLevel && loopLevel > 0)
                 || (event_type == QEvent::DeferredDelete
                     && eventLevel == loopLevel));
            if (!allowDeferredDelete) {
                // cannot send deferred delete
                if (!event_type && !receiver) {
                    // we must copy it first; we want to re-post the event
                    // with the event pointer intact, but we can't delay
                    // nulling the event ptr until after re-posting, as
                    // addEvent may invalidate pe.
                    QPostEvent pe_copy = pe;

                    // null out the event so if sendPostedEvents recurses, it
                    // will ignore this one, as it's been re-posted.
                    const_cast<QPostEvent &>(pe).event = nullptr;

                    // re-post the copied event so it isn't lost
                    data->postEventList.addEvent(pe_copy);
                }
                continue;
            }
  • 如果符合条件 调用 QCoreApplication::sendEvent(r, e);
  • 不符合条件 制空当前队列指针const_cast<QPostEvent &>(pe).event = nullptr;,将指针副本插入到队列尾部 continue 跳过``(完成一次延迟删除的机会)

最总调用event 事件:

cpp 复制代码
bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{
    // Note: when adjusting the tracepoints in here
    // consider adjusting QApplicationPrivate::notify_helper too.
    Q_TRACE(QCoreApplication_notify_entry, receiver, event, event->type());
    bool consumed = false;
    bool filtered = false;
    Q_TRACE_EXIT(QCoreApplication_notify_exit, consumed, filtered);

    // send to all application event filters (only does anything in the main thread)
    if (QCoreApplication::self
            && receiver->d_func()->threadData.loadRelaxed()->thread.loadAcquire() == mainThread()
            && QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event)) {
        filtered = true;
        return filtered;
    }
    // send to all receiver event filters
    if (sendThroughObjectEventFilters(receiver, event)) {
        filtered = true;
        return filtered;
    }

    // deliver the event
    consumed = receiver->event(event);
    return consumed;
}

延迟删除的核心(个人理解)

  • 1: 就是通过记录looplevel 和 eventlevel 然后在条件比较两者
  • 2:eventLevel 是每个事件中的一个快照相当于curentlevel, 他记录的是当前looplevel, 而looplevel 是不断变换的。相当于looplevel 是一个动态的全局或者静态的变量, eventLevel 是每个事件中的一个属性

场景1:主循环中的延迟删除

cpp 复制代码
// loopLevel = 0 (初始)
QCoreApplication::exec(); // loopLevel → 1

QObject* obj = new QObject;
obj->deleteLater(); // eventLevel = 1

// 事件处理时:
//   eventLevel=1, loopLevel=1 → 条件3满足 → 立即删除

场景2:嵌套循环中的跨级删除

cpp 复制代码
// 主循环中 (loopLevel=1)
QObject* outerObj = new QObject;
outerObj->deleteLater(); // eventLevel=1

QEventLoop nestedLoop; 
// 进入嵌套循环 (loopLevel=2)

// 处理outerObj的延迟删除事件:
//   eventLevel=1, loopLevel=2
//   1 > 2? false
//   !1 && 2>0? false
//   事件非DeferredDelete → 不满足条件 → 跳过删除
相关推荐
码农葫芦侠4 小时前
C++继承中虚函数调用时机问题及解决方案
c++·qt
玉树临风江流儿6 小时前
QT收费情况
开发语言·qt
jingjing~8 小时前
【Qt】QTime::toString(“hh:mm:ss.zzz“) 显示乱码的原因与解决方案
java·开发语言·qt
机器视觉知识推荐、就业指导20 小时前
手动开发一个TCP客户端调试工具(一):了解Qt中TCP通信原理与核心类
网络·qt·tcp/ip
归云鹤1 天前
QT信号和槽怎么传输自己定义的数据结构
开发语言·数据结构·qt
cpp_learners1 天前
QT Word模板 + QuaZIP + LibreOffice,跨平台方案实现导出.docx文件后再转为.pdf文件
qt·pdf·docx
程序员编程指南1 天前
Qt 移动应用发布与分发指南
c语言·开发语言·c++·qt
x晕x1 天前
Qt 消息弹窗 Toast
linux·c++·windows·qt·mac
程序员编程指南1 天前
Qt 与物联网(IoT)开发
c语言·开发语言·c++·qt·物联网