Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use deleteLater

Assuming I have the following snippet, is it safe to call deleteLater in qto's destructor for other QT objects it might administer?

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

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

Because I've analyzed similar code like this with a leak detector and all the objects for which deleteLater was called, weren't deallocated correctly unless I replaced the call with a normal delete. If I've understood this correctly, deleteLater only registers a deletion event in the QT message queue. Can this be the problem that qto's destructor is called at the end of main's scope whereas the QT message loop already ends with the return from a.exec? Thus the deletion event will never be processed, in fact not even pushed into a message queue since there is none?

like image 450
user1709708 Avatar asked Mar 13 '14 10:03

user1709708


4 Answers

This post is rather aged, but I would like to add the answer I would have liked to come across when I was asking this myself.

deleteLater() can be very useful in combination with asynchronous operations. It especially shines, I think, with the more recent possibility to connect signals to lambda functions.

Suppose you have some longComputation() that you want to execute asynchronously (not in the sense of multithreading, in the sense of scheduling execution in the event loop). You can do like this:

void MyClass::deferLongComputation()
{
    QTimer* timer = new QTimer();

    connect(timer, 
            &QTimer::timeout,
            [this, timer](){this->longComputiation(); timer->deleteLater();});

    timer->setSingleShot(true);
    timer->start();
}

where deleteLater() takes care of safely disposing of the QTimer once its duty has been carried out and avoid the memory leak that one would have otherwise.

The same pattern can be used in multithreading with QFutureWatcher.

like image 123
Emerald Weapon Avatar answered Nov 16 '22 00:11

Emerald Weapon


As I understand it, deleteLater is most often used when you require an object to be deleted from within the call to a slot. If delete is used in this case and the object is referenced when returning from the slot, a reference to uninitialised memory occurs.

Therefore, deleteLater requests that object to be deleted by placing a message on the event loop, which is processed at some point, on returning from the slot and it is safe to be deleted.

I expect that using deleteLater in the destructor means there's a likely chance that the object goes out of scope, calls deleteLater on its managed objects, but quits before the event loop has a chance to delete the objects, as exiting from QApplication::exec() will terminate the event loop.

like image 14
TheDarkKnight Avatar answered Nov 15 '22 22:11

TheDarkKnight


The question is old, but I'll leave this for the future generation) The reply which was marked as an answer is correct but oddly formulated. Actually your question contains a right answer:

message loop already ends with the return from a.exec? Thus the deletion event will never be processed, in fact not even pushed into a message queue since there is none.

This is exactly what is happening. Everything deleteLater() does is just posting a deletion event into the outter event loop. When event gets proccessed - object gets deleted. But if there are not outter event loop and no event loop is encountered later in the execution flow - event will never get posted, thus object is never deleted.

If you call deleteLater() in the object's destructor and put an object on the stack - deleteLater() is called when the object goes out of scope. In your example "going out of scope" is happening when closing brace of main() function is encountered. However, by that time, a.exec() (which represents the main event loop of Qt App) has already returned --> no event loop any more --> deleteLater() was called, but it's nowhere to post a deletion event --> objects were supposed to be "deletedLater" never get deleted...

Regarding the part "when to use deleteLater()":

Kuba Ober answered:

Generally speaking, there is a narrow set of circumstances where deleteLater should be used. Most likely you simply shouldn't be using it...

Don't listen to it, it is absolutely incorrect as the whole answer. What you should do and what should not you better decide after reading this article. Although, it is mainly about Qt threads, the article also tells about ascynchronous programming (and, as Emerald Weapon mentioned, it is exactly what deleteLater() was created for).

Also, smart pointers and QObject parent ownership have nothing to do with scheduling for the deletion with deleteLater(). These both techniques are actually using a simple delete operation under the hood. And as the article shows and as Emerald Weapon's answer demonstrated: delete does not solve the problems deleteLater() does. So if you need to delete object you use delete, if you need to schedule it for the deletion you use deleteLater().

BTW, if you want to use smart pointer with deleteLater() you can specify the deleter:

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

And at last, It is an NOT an error to use deleteLater() in the destructor of QObject, for non-child objects.

like image 7
WindyFields Avatar answered Nov 16 '22 00:11

WindyFields


You are correct that the deleteLater() command is only executed by an event loop.

From the Qt documentation for QObject:

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.

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.

If you want all child QObjects to be deleted when qto is deleted, make sure they are created with qto as a the parent.

like image 3
RobbieE Avatar answered Nov 16 '22 00:11

RobbieE