Another programming tidbit

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.