Qt World Summit: Submit your Presentation

Memory Management

  • Hi guys,

    I'm currently working on a project, and it has been a while since i've coded in C++/Qt so I'm getting familiar again with pointers and references.

    My program is currently working fine but still when i'm looking at my .hpp's and .cpp's i can feel that there is still something wrong.

    I'm manipulating both objects that inherit from QObject and object that I have created (that don't inherit from QObject) and all those elements are flowing through methods here and there. So in terms of memory management I'm still wondering how to do things right.

    I've been using mostly pointers so far, since i'm way more comfortable with them, but I still have trouble to determine where to delete them, who really owns them, how to properly use references that i can modify.

    I've been reading (quickly) about smart pointers that handle this memory management (with QSharedPointer or something like that).

    Anyway, I'm here to gather some advice on how should i pass any data from a method to an other, how do i manage their lifetime, etc...
    Any piece of advice will be welcome, I'm looking forward to improve myself regarding those subtleties.

    Thanks for reading,
    Best regards

  • That is a complicated issue and you could probably multiple pages about that issues including the possibility to start some religious wars, so I try to answer your question as general as possible.

    1.The first question you have to ask yourself is heap or stack.
    If your object is small enough and you don't need to create it dynamically where is nothing wrong with stack.

    1. Which object is the owner of your object?
      In general the owner should be a object that is high enough in the "hierarchy" that it "dies" after its consumer.
      It is bad style to just pass the pointer from a to b with "ownership change".
      So yo have one owner which is responsible for creation and deletion.
      A consumer should NOT become an owner just because he has a small job to do.

    2. Maybe you have a look at RAII since C++11 it is "bad" style to use plain new/delete.
      This depends on framework but in general it is also possible to use std::make_unique<>, std::make_shared<> in combination with Qt.
      I personally use std::smart pointer also with Qt.

    3. If the consumer is a private member function of the "owner" it can directly access the variable without any problem.
      If it is a free function and you have a unique_ptr I would give a const * or * to the function with unique_ptr<>::get() function.
      If you have a free function or another interface and a normal object I would recommend to use const ref or ref as parameter

    Nevertheless I think shared_ptr<> are only used in a multithreading scenario I can not think of one case where shared_ptr in a single thread scenario makes any sense, mostly if such things happen it is a sign that something is wrong with the architecture.

  • Moderators

    Hi and welcome to devnet.

    I'll try to be a little more general in my answer and not go into specific types. I also don't agree that shared ownership is something used only in multithreaded scenarios. They have a very important role in describing ownership.

    Lets divide this into two separate topics: managing QObject derived types and managing non QObject derived types.

    The first one is pretty easy - just give the object a parent and it will be deleted when the parent is. It's also safe to delete it earlier either via delete or calling deleteLater() on it.

    As for the second case - it's also pretty simple if you follow these rules.

    Ownership passing:

    • If you want to expose some object for inspection or modification make it explicit: return a pointer or pointer to const object (for read-only). Receiver should inspect/modify the object via that pointer and never care about ownership. This pointer should not be stored anywhere.
    • If you want to pass ownership (e.g. a factory function producing an object) make it explicit: return a unique pointer, not a raw pointer. This way it's clear that the factory doesn't own the object and it is to be managed on the caller side.
    • If you want to share ownership (e.g. a factory that manages its products) make it explicit: return a shared pointer. This way it is clear that the object will be deleted either by you or the factory, whoever is the last owner standing.

    Ownership receiving:

    • If something passes you a raw pointer it's not yours. Inspect/modify it but don't store it as it may go away at any time
    • If something passes you a unique pointer it means you are responsible for it. You decide when it will be deleted and how to expose it to others (raw, unique or shared)
    • If something passes you a shared pointer it means it invites you to share it. You may do so or just use and discard. If you don't decide to engage in that sharing it's ok. It will be deleted by someone else.
    • If something passes you a shared pointer you may want to observe its lifetime but not own it. In that case get a weak pointer from it. the object is gonna be deleted by someone else and you'll be aware of it.

    Parameter passing:

    • if the param is read-only, i.e. you're not gonna modify it inside the function pass it by const reference (except for basic types like int or double that you can just pass by value). don't store it
    • if the param is read-write then pass it by non-const reference. Don't store it
    • if the param is unique or shared pointer it is the only case you should store it, either via shared or unique pointer.
    • passing function params as pointers is worse for performance than passing references (const or not), as it prevents compiler from making important optimizations (nothing can be assumed about a raw pointer).

    These are the general guidlines. You should rarely break them and only when you really really need to.
    In the above I used terms like shared, unique, weak and raw pointer. I mean them in general terms that describe ownership, not just specific types like std::unique_ptr or QSharedPointer. For example a QPersistantModelIndex would be a case of weak pointer. Although its name does not explicitly say that.

    Shared, unique, weak and raw ownership is associated with any type. You just need to interpret it.

  • Hello,

    Both inputs are highly appreciated. I understand better how, when and why should I choose pointers over refs. I'm still reading some stuff here and there on how to produce some good code.

    Memory management has always been a doozy matter in C++ and i just can't get this Stroustrup quote out of my head : "C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off. ".

    Thanks again for replying so quickly, I will apply everything i've learned here on my work asap.
    Best regards.