【Qt】QObject 的 deleteLater 函数源码 QA

一、QA:
  1. deleteLater 是线程安全的吗?
  • 线程安全的。
  1. deleteLater 是怎么实现的?
  • 它并不马上 delete 掉该对象,而是会包装成一个延迟删除事件 QDeferredDeleteEvent,然后 post 到事件循环里面去。
  • 最后在 Qt 的事件循环中,取出该事件,然后 qDeleteInEventHandler(this),从而实现最后的 delete 工作。
  1. 为什么需要 deleteLater?
  • 在 QObject 的析构函数 ~QObject() 的 cpp 实现上面,Qt 有句注释“Use deleteLater() instead, which will cause the event loop to delete the object after all pending events have been delivered to it.”,百度翻译为“改为使用deleteLater(),这将导致事件循环在所有挂起的事件都传递到对象之后删除该对象。”。
  • 因此,如果使用传统的马上 delete 该对象,那么该对象在事件队列的那些事件将无法正常传递和处理。

二、source code:
// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qobject.cpp
/*!
    \threadsafe

    Schedules this object for deletion.

    The object will be deleted when control returns to the event
    loop. If the event loop is not running when this function is
    called (e.g. deleteLater() is called on an object before
    QCoreApplication::exec()), the object will be deleted once the
    event loop is started. If deleteLater() is called after the main event loop
    has stopped, the object will not be deleted.
    Since Qt 4.8, if deleteLater() is called on an object that lives in a
    thread with no running event loop, the object will be destroyed when the
    thread finishes.

    Note that entering and leaving a new event loop (e.g., by opening a modal
    dialog) will \e not perform the deferred deletion; for the object to be
    deleted, the control must return to the event loop from which deleteLater()
    was called. This does not apply to objects deleted while a previous, nested
    event loop was still running: the Qt event loop will delete those objects
    as soon as the new nested event loop starts.

    \b{Note:} It is safe to call this function more than once; when the
    first deferred deletion event is delivered, any pending events for the
    object are removed from the event queue.

    \sa destroyed(), QPointer
*/
void QObject::deleteLater()
{
    QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
}
// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qcoreapplication.cpp
/*!
    \since 4.3

    Adds the event \a event, with the object \a receiver as the
    receiver of the event, to an event queue and returns immediately.

    The event must be allocated on the heap since the post event queue
    will take ownership of the event and delete it once it has been
    posted.  It is \e {not safe} to access the event after
    it has been posted.

    When control returns to the main event loop, all events that are
    stored in the queue will be sent using the notify() function.

    Events are sorted in descending \a priority order, i.e. events
    with a high \a priority are queued before events with a lower \a
    priority. The \a priority can be any integer value, i.e. between
    INT_MAX and INT_MIN, inclusive; see Qt::EventPriority for more
    details. Events with equal \a priority will be processed in the
    order posted.

    \threadsafe

    \sa sendEvent(), notify(), sendPostedEvents(), Qt::EventPriority
*/
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();
}
  • 关键语句:receiver->d_ptr->deleteLaterCalled = true;
  • 关键语句:data->postEventList.addEvent(QPostEvent(receiver, event, priority));
// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qcoreapplication.cpp
int QCoreApplication::exec()
{
    ...
    int returnCode = eventLoop.exec();
    ...
}

// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qeventloop.cpp
int QEventLoop::exec(ProcessEventsFlags flags)
{
   ...
   processEvents(flags | WaitForMoreEvents | EventLoopExec);
   ...
}

// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qeventdispatcher_glib.cpp
bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    ...
    bool result = g_main_context_iteration(d->mainContext, canWait);
    ...
}

// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qeventdispatcher_glib.cpp
static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
{
    ...
    QCoreApplication::sendPostedEvents();
    ...
}

// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qcoreapplication.cpp
void QCoreApplication::sendPostedEvents(QObject *receiver, int event_type)
{
    ...
    QCoreApplicationPrivate::sendPostedEvents(receiver, event_type, data);
}

// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qcoreapplication.cpp
void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
                                               QThreadData *data)
{
    ...
    while (i < data->postEventList.size()) {
        ...

        // after all that work, it's time to deliver the event.
        QCoreApplication::sendEvent(r, e);
    }
    ...
}

// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qcoreapplication.cpp
bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{
    ...
    return notifyInternal2(receiver, event);
}

// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qcoreapplication.cpp
bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{
    ...
    return self->notify(receiver, event);
}

// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qcoreapplication.cpp
bool QCoreApplication::notify(QObject *receiver, QEvent *event)
{
    ...
    return doNotify(receiver, event);
}

// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qcoreapplication.cpp
static bool doNotify(QObject *receiver, QEvent *event)
{
    ...
    return receiver->isWidgetType() ? false : QCoreApplicationPrivate::notify_helper(receiver, event);
}

// ./Qt/5.15.2/Src/qtbase/src/corelib/kernel/qobject.cpp
bool QObject::event(QEvent *e)
{
    ...
    case QEvent::DeferredDelete:
        qDeleteInEventHandler(this);
        break;
    ...
}

~TestClass()
{
    qDebug() << "TestClass dector";
}

三、demo code:
#include <QCoreApplication>
#include <QObject>
#include <QDebug>

class TestClass : public QObject
{
public:
    explicit TestClass(QObject *parent = nullptr)
        : QObject(parent)
    {}
    ~TestClass()
    {
        qDebug() << "TestClass dector";	// 打断点在这行
    }
};

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

    TestClass* t = new TestClass();
    t->deleteLater();

    return a.exec();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值