Is qobject_cast a valid way to test destruction state?
-
Hi there,
I've got a class deriving from QObject, so
QObject -> QClass1
and some other class
QOtherClass
Now in the QOtherClass destructor, it does stuff in an QClass1 instance. This is obviously okay, if QOtherClass is usually destructed manually before QClass1 destruction, which is the normal case. In some situations however, QOtherClass is direct child of a QObject, which leads to the situation that on program shutdown, the QOtherClass dtor is called (via QObject memory management) after the (also QObject-managed) QClass1 was torn down to the level of a QObject already. So calling QClass1 methods on that instance will cause segfaults.
Currently, I check in the QOtherClass dtor:
@
QClass1 theClass1Instance;
...
if (qobject_cast<QClass1>(theClass1Instance))
theClass1Instance->doStuff();@Which seems to work, i.e. it lets me know whether theClass1Instance is already partly torn down.
Is that a valid use case for qobject_cast, can I depend on it?
-
I'm pretty sure this doesn't work. You can use a QPointer to create a safe pointer to a QObject instance:
@
QPointer<QClass1> safe = new QClass1();
// ...
if (!safe.isNull()) {
safe->doStuff();
}
@ -
[quote author="Arnold Konrad" date="1367781898"]I'm pretty sure this doesn't work.[/quote]
Well the question really isn't if it works, because it actually does. The question is more whether it's intended and dependable behaviour for future versions to come.Anyone can test it with the following code:
mainwindow.h:
@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QDebug>namespace Ui {
class MainWindow;
}class QClass1 : public QObject
{
Q_OBJECT
public:
QClass1(QObject *parent) : QObject(parent) {}
void doStuff() { qDebug() << Q_FUNC_INFO; }
};class QOtherClass : public QObject
{
Q_OBJECT
public:
QOtherClass(QClass1* parent) : QObject(parent), mClass1(parent) {}
~QOtherClass()
{
qDebug() << static_cast<bool>(qobject_cast<QClass1*>(mClass1));
mClass1->doStuff();
}private:
QClass1 *mClass1;
};class MainWindow : public QMainWindow
{
Q_OBJECTpublic:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();QClass1 *class1;
QOtherClass *otherClass;private:
Ui::MainWindow *ui;
};#endif // MAINWINDOW_H
@
mainwindow.cpp:
@
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);class1 = new QClass1(this);
otherClass = new QOtherClass(class1);
}MainWindow::~MainWindow()
{
delete otherClass;
delete ui;
}
@Now comment out the "delete otherClass" in ~MainWindow and you'll see the debug output of ~QOtherClass changes from true to false – because then QObject-memory-management deletes otherClass after QClass1 has been torn down. While when you delete otherClass in ~MainWindow, the QClass1 still exists and the qobject_cast test returns true.
--
I'm aware of smart pointers but I'd like to avoid them in this special case, thanks for bringing it up nevertheless.
-
I suspect that it will do what you want for a long time because the implementation of qobject_cast is unlikely to change within the lifetime of Qt 5, but it's probably just a happy coincidence. qobject_cast wasn't designed to be a deletion detector; it was designed to do be a dynamic_cast for QObjects that doesn't require RTTI, and it just happens to report a failure if the object has already been deleted (at least in the cases that you've tried).
You're entering undefined territory, so tread carefully :)
P.S. Other people have "used this trick too":http://www.gamedev.net/topic/262517-dynamic_cast-not-failing-on-bad-pointers/#entry2604314 with dynamic_cast, but the consensus seems to be "don't do it"
-
Well it's not really a test whether QObject has been deleted (like QPointer<QObject> would), right? The moment I'm interested in is in the teardown process where QObject still exists but the part of the instance that is defined by the subclass isn't. That's why I had hoped qobject_cast would be safe there, since the QObject is fine at that time.
Well I guess I'll have to manually delete all the QObject children of QClass1 in its dtor. Not the cleanest method since that's duplicating code of the QObject dtor, but there seems to be no nice way to handle it.
Thanks for your suggestions (further ideas always welcome, of course ;)