c++qtdestructorsmart-pointersraii

Is Qt incompatible with RAII design patterns?


An important rule of thumb for good coding practices is

every new must be matched by a delete

discussed in this previous question. Usually, we encapsulate this rule in an RAII class like std::unique_ptr. However, Qt appears to break this pattern. For example, to create a QTimer, a user may do something like this:

QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, QOverload<>::of(&AnalogClock::update));
timer->start(1000);

The above example is found here. The QTimer constructor is passed the this pointer, which becomes the parent of timer. It seems that Qt then sets up *this to destruct QTimer when the destructor for *this is called. In effect, there is no need to call delete timer.

  1. Can I call delete timer; anyway, such as in the destructor of a unique_ptr? Will that call the destructor twice?
  2. What if timer was allocated on the stack? Does *this know that?

Solution

  • Can I call delete timer anyway

    If you explicitly call delete timer, the QTimer's destructor (well, to be exact, the destructor of the QTimer's QObject base-class) will remove the QTimer from its parent-object's children-list, so that later on when the parent-object's own destructor is called, the QTimer will not be deleted a second time. So that's okay.

    such as in the destructor of a unique_ptr

    With unique_ptr (or shared_ptr), OTOH, you will run into a problem -- while the QObject classes are smart enough to update their parent-QObject when they are destroyed, they do not know how to update a unique_ptr or shared_ptr that is pointing to them. So if you assign a unique_ptr or shared_ptr to point to your QTimer object, and also give the QTimer-object a non-NULL parent (as in your example), then you likely will have a double-delete problem: once when the Qt mechanism deletes the QTimer, and a second time when the unique_ptr tries to delete the (now-dangling) pointer that it contains. (Note that there is a QPointer class that behaves as a weak-pointer to a QObject, which can be useful).

    Because of that, it's better not to try to mix Qt's object-tree system and C++ smart-pointers. That is, if you're going to give your QTimer a non-NULL parent, then you should rely on that parent to handle the QTimer's deletion, and not also try to manage it with a unique_ptr.

    What if timer was allocated on the stack? Does *this know that?

    No, this's destructor won't know the object is on the stack, and so it will try to delete it, leading to undefined behavior. So if you're going to allocate the QTimer on the stack, you should not specify a non-NULL parent-pointer for it.