0

I was in #Qt irc channel, and I showed a small snippet of my code in a style that I heavily rely upon. It looks like this:

/* Get Reply from Server */
QPointer<QNetworkReply> reply;
{
    QEventLoop waitForFile; // A QObject 
    connect(&m_NetworkAccessManager, &QNetworkAccessManager::finished, &waitForFile, &QEventLoop::quit);
    reply = m_NetworkAccessManager.get(request);
    waitForFile.exec();
    disconnect(&m_NetworkAccessManager, &QNetworkAccessManager::finished, &waitForFile, &QEventLoop::quit);
}

I was told that the code was pretty strange, which does not surprise me. I tend to rely heavily on the stack, and am very keen to control lifecycle with { and } -- Basically my working principle has been

Delete all objects ASAP using scope.

In this case, the QEventLoop does not need to exist beyond this scope, and its usefulness is only relative to QPointer<QNetworkReply> reply; which is an added benefit for readability.

In any case, that code prompted the declaration:

You should always allocate qobjects on the heap

We had a bit of a discussion about this. Frankly I was shocked, as I operated under the idea that:

Fear QObject pointers, and only use them for very specific usecases such as:

  1. If a parameter is designed to be able to accept NULL values.
  2. If the object being passed to a function is large, and a reference can not be used.
  3. If the object will be added to a QList.
  4. If you are developing a GUI, where parent/child relationships are paramount
  5. If you are working with special types, like QFile or QThread, where copying instances of these objects is conceptually null. (You wouldnt want two QFile's of the same file.)

After giving my reasons, they stuck fairly hard to working under the principle of Always allocate QObjects to the heap. I think under the idea that while you could do it fairly safely using the stack, there is no good reason why you would not want a QObject on the heap, given how easy QObject Lifecycle is in comparison to just run of the mill pointers.

I think the only relevant context to mention is that these developers were mainly accustomed to GUI development, where as I use Qt almost exclusively for console applications, which results in very linear coding requirements.

And despite all this, I do not have a good answer.

Are they correct? Are there any good reasons NOT to put a QObject on the heap, even though it is designed to safely work there?

Thanks.

2

The short answer is you should allocate your objects as necessitated by their logical lifetimes. When it comes to asynchronous code, this will be the heap, since multiple functions need to access the value. There is no universal rule for QObjects vs not-QObjects.


It is a terrible idea to wait on local event loops to get around writing asynchronous code. It will surely kill the performance of single threaded applications that could do other work while waiting on communication. You can exploit parallelism even in single threaded applications by using a non-blocking model, as you respond opportunistically to network requests that can complete in any order.

From Qt's Wiki:

[...] you should never ever block the event loop [...] With the event delivery stuck, widgets won't update themselves (QPaintEvent objects will sit in the queue), no further interaction with widgets is possible (for the same reason), timers won't fire and networking communications will slow down and stop. Moreover, many window managers will detect that your application is not handling events any more and tell the user that your application isn't responding. That's why is so important to quickly react to events and return to the event loop as soon as possible!

Asynchronous IO is much more efficient than synchronous blocking. As soon as you try to parallelize that code, you'll run into trouble. Because async io requires dividing your logic into several functions (in C++), you need to allocate persistent objects on the heap. This is not a big issue at all. There's no reason to fear the heap. The lifetime of the objects in both cases is the length of the network transaction. Don't confuse this with the length of a block's scope in lines of code.


[...] these developers were mainly accustomed to GUI development, where as I use Qt almost exclusively for console applications, which results in very linear coding requirements.

Network code is similar to GUI code in that it has to respond to asynchronous events from the outside world. Qt provides a console-application-oriented event loop QCoreApplication that you should use to structure your networking applications. Your download manager should be its own object with the core application as its parent.


Lastly, the credo of

Delete all objects ASAP using scope.

doesn't make much sense. As soon as you have to make a second network request, you pay the startup and teardown cost of a QEventLoop unnecessarily. If you have to make many requests this can add up. It's usually better to reuse an instance of a complex object if that's possible, and it's always possible in the case of an event loop.

4

The motivation for not putting QObjects on the stack is the Qt ownership model. If an object is destroyed all of its child objects are automatically deleted. If one of those child objects is allocated on the stack instead of via new, bad things will happen.

In a GUI application, the code needs to handle an event quickly and then return to the event loop. So you can't keep an object alive for any period of time on the stack. That's why GUI programmers will use Qt's ownership model instead. This allows them to tie the ownership of an object to some other object such as a window or application.

The rule actually ought to be that you should only create parentless QObjects on the stack. If the QObject has a parent, its parent ought to be in control of destroying it, not the stack. If it has no parent, then it is fine for the stack to be responsible for destroying it.

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.