deleteLater: Managing the QObject Lifecycle in C++

November 29, 2022
Manage the lifecycle of QObjects in C++ using smart pointers with custom deleters that invoke deleteLater.

This post describes how to manage the lifecycle of QObjects in C++ by using smart pointers with custom deleters which invoke deleteLater.

Qt has it’s own lifecycle for QWidgets/Objects that is distinct from the usual C++ object lifecycle. Most of the time that is essentially a non-issue. Normally, QWidgets last for the lifetime of the application or have parents. But sometimes you want short lived, unparented QWidgets or QObjects that take signal/slot connections. In that case, if you delete them via standard C++ means (stack variable, smart pointer, delete operator), you can cause an intermittent bug depending on the timing of signals, the deletion, and the event loop.

To safely dispose of them, you would call deleteLater on them. There are some problems. The obvious solution is declare the QObjects/Widgets as pointers, new them up, and not manually delete them. So your code would look like its riddled with memory leaks. Also you have to remember to call deleteLater on them. For member variables, that means implementing a destructor. For scoped variables, that means calling deleteLater on all exit paths of the scope. QPointer exists, but doesn’t solve any of these issues.

What seems to be the best solution to this is to use smart pointers (std::unique_ptr, std::shared_ptr) with custom deleters. The custom deleter would call deleteLater. This lets you respect the Qt lifecycle, not have your code look like it is filled with memory leaks, and not have to implement destructors or decorate each exit path. I should point out that you can do the same thing with QSharedPointer because it can also take a custom deleter. However, I think it is better to use standard library functions. Also, I find that most of the time std::unique_ptr is more appropriate than std::shared_ptr.

Scope example:

void SomeClass::someFunction()
{
    std::unique_ptr<QObject, std::function<void(QObject*)>> temporaryQObject(new QObject(), &QObject::deleteLater);
    ...
    aCallThatCanThrow();
    ...
    if (someCondition)
        return;
    ...
    return;
}

Member variable example:

class SomeClass {
    ...
    private:
        std::unique_ptr<QObject, std::function<void(QObject*)>> memberQObject;
};

SomeClass::SomeClass()
    : memberQObject(new QObject(), &QObject::deleteLater)
{
}