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 → 不满足条件 → 跳过删除
相关推荐
娇娇yyyyyy39 分钟前
QT编程(15): Qt 按键事件和定时器事件
开发语言·qt
GIS阵地5 小时前
QgsDataSourceUri解析
数据库·c++·qt·开源软件·qgis
载数而行5206 小时前
Qt事件常用类,QLabel的QFrame交互
qt
娇娇yyyyyy8 小时前
QT编程(16): Qt Model
开发语言·qt
qq_466302459 小时前
vs2022 与Qt版本兼容 带来的警告
c++·qt
娇娇yyyyyy1 天前
QT编程(13): Qt 事件机制eventfilter
开发语言·qt
带娃的IT创业者1 天前
工具状态失踪之谜:EventBus事件漏接与asyncio.Lock并发陷阱双线诊断
qt·websocket·并发控制·eventbus·事件驱动架构·pwa·asyncio.lock
不想看见4041 天前
C++/Qt 代码规范指南
开发语言·qt
li星野1 天前
QT模拟题:QT项目实践与架构设计(120分钟)
开发语言·qt
笑鸿的学习笔记1 天前
qt-C++语法笔记之Qt中的delete ui、ui的本质与Q_OBJECT
c++·笔记·qt