Another programming tidbit

February 4th 2009 06:13 pm

Now that Super Bowl weekend is over (Go Steelers!) it’s time to get back to fixing bugs and typing up code. That brings me to my helpful tidbit for today.

A few JuK bugs have been related to this issue, and I just saw a second kdelibs bug today which I think is related to this (the actual bug is unimportant) so I think it’s important to get out the following message: QObjects do not free you up from having to write proper destructors.

Here’s a more detailed explanation of the problem. Let’s say you have a widget, which has a private d pointer. Your widget itself contains several sub widgets which are children of your widget. One or a few of these widgets rely on an object contained in your d-pointer. It may look something like this:

A simple class diagram

Of course, graphically illustrated the problem might seem obvious here, especially if you know how QObject’s destructor works: If you delete the d pointer while the View is still using it then you run the very real risk of a crash. This can occur if your destructor looks like this, for instance:

Widget::~Widget() {
  delete d;
}

After your ~Widget has run then your d pointer and its DataStore are no more, but your View and Button are still alive. This is usually OK, as the QObject destructor will delete them and there will be no memory leak. However, if anything happens to the View which would cause it to access the DataStore object before the program gets to that point, then you have a crash on your hands. The race window is very small here if you’re racing with an event handler for instance. The problem is a lot easier if View uses DataStore in its destructor since it will pretty much always crash there.

If DataStore had somehow been a child of View there wouldn’t have been an issue (since QObject deletes children first), but that unnecessarily fouls up the class design and besides, it’s usually either not possible or desirable. But when you have situations where you have children that depend on siblings for their functionality, you must delete them manually in your destructor or else the effectively random order that is chosen by QObject is what you’ll get, which can lead to hard-to-reproduce crashes. In this case, we would delete View before we deleted the d pointer. We don’t have to delete the Button manually since it does not depend on any of its siblings, although it doesn’t hurt either.

Posted by mpyne under C++ & KDE & Programming & Tutorial | 4 Comments »

4 Responses to “Another programming tidbit”

  1. atomopawn Identicon Icon atomopawn responded on 04 Feb 2009 at 22:02 #

    Is deleteLater an alternative to this or will the destructor wipe out the object before freeing the “d” object?

  2. mpyne Identicon Icon mpyne responded on 04 Feb 2009 at 22:12 #

    atomopawn: deleteLater is more for trying to safely delete an object in response to an event, by ensuring that the event was fully handled before deleting the object. In addition it only works on QObjects, (I’m assuming you’re referring to deleteLater()’ing the d pointer here which is typically not a QObject).

    Assuming dptr is a QObject, the biggest point against deleteLater is that Widget::~Widget() *will* delete all of its children even if you’ve already called deleteLater on one. So if you don’t delete them in the order you want you’re leaving it up to Qt to do it essentially randomly.

  3. Jucato Identicon Icon Jucato responded on 04 Feb 2009 at 22:13 #

    Thanks for these tidbits. Need to save them as I’m sure they will be extremely useful to me in the (hopefully near) future.

    I must note that deleting dynamically allocated objects, in tandem with Qt’s object hierarchy, is one thing I’ve been confused with, as a beginner/intermediate C++/Qt coder.

    On one hand, in “plain” C++ books, you are advised to delete everything you allocate, since C++ doesn’t have garbage allocation. On the other hand, most introductory text in Qt I’ve encountered almost create a blanket “don’t worry, Qt will delete all those children cleanly for you” tip, only to find out stuff like these a lot later, usually after some headdesking.

    I guess this is why I look at any form of automatic “garbage collection” with unhealthy skepticism, even if I haven’t actually used a “real” GC aside from Qt’s. I would have appreciated it if those Qt texts were upfront about these kinds of things rather than giving beginners a sort of false or only partial assurance.

  4. mpyne Identicon Icon mpyne responded on 04 Feb 2009 at 22:19 #

    Jucato: On the other hand the problem with doing it yourself is that you may screw it up where it would have worked perfectly leaving it to Qt. The issue is that it depends on your ownership / dependence model.

    If an object owns as a child everything that it also depends on, letting Qt delete for you is no issue. When an object starts depending on its siblings, parents, or unrelated objects (like singletons) then the tree-based ownership style of Qt can’t help you and you need to do it yourself.

Trackback URI | Comments RSS

Leave a Reply