How can I ensure proper resource management (RAII) when using Qt Plugins?
-
My collegue posted the following on StackOverflow:
http://stackoverflow.com/questions/34312928/qt-object-management-with-qt-pluginsI suggested to cross-post here, since in this forum the question is more visible to Qt experts. Hopefully I'm right.
-
Hi,
When you add a widget like he does, he gets back an action. Removing the action from the toolbar and then deleting it should also delete the associated widget.
Hope it helps
-
@Jakob
Just deleting the widget doesn't work? In principle Qt handlesQObject
destruction through signals and slots. For example there is the QObject::destroyed signal that you can subscribe to. This way Qt ensures that at any point if an object is deleted the parent is notified and will remove it from the child list. This technique also resolves the stack vs heap allocation problem. Additionally you can handleQObject
pointers through the QPointer class that can be used to safely ascertain that an object does or doesn't exist.QPointer
will be automatically set to point to NULL if theQObject
it's referencing gets deleted. You could create your widget in your plugin, keep aQPointer
to handle it, and when Qt deletes it you'll know, or you could delete it and Qt will know that this was done, because of thedestroyed
signal. If you're working with the container classes, there is not much of an issue, since they are implicitly shared anyway, and will free the memory when all the referencing objects are freed.Kind regards.
-
@kshegunov Thank you for an elaborate answer. Just to make sure I understand correctly what you are saying: even if I only use plain pointers to
QObject
s all across my code, and also pass these objects as plain pointers to Qt, then still Qt will always know I deleted them and handle this gracefully, because internally all these pointers are wrapped inQPointer
?Is this guaranteed always to be true? One thing that springs to mind for instance, is how Qt internally also wraps
QWidget
pointers in aQLayoutItem
instance when adding the widget to some layout. Is it true that when I delete theQWidget
, automatically the wrappingQLayoutItem
is also deleted and the layout is notified? -
Just to make sure I understand correctly what you are saying: even if I only use plain pointers to QObjects all across my code, and also pass these objects as plain pointers to Qt, then still Qt will always know I deleted them and handle this gracefully,
Yes, this is correct.
because internally all these pointers are wrapped in QPointer?
No this is not true. Qt's management of objects is pretty involved. The
QObject
instance is notified by it's children when they are deleted but this is not done by means ofQPointer
, actually it's not really done through thedestroyed
signal also. Qt will use theQMetaObject
instance attached to each of theQObject
s it's handling to do that.void QObjectPrivate::setParent_helper(QObject *o) { Q_Q(QObject); if (o == parent) return; if (parent) { QObjectPrivate *parentD = parent->d_func(); if (parentD->isDeletingChildren && wasDeleted && parentD->currentChildBeingDeleted == q) { // don't do anything since QObjectPrivate::deleteChildren() already // cleared our entry in parentD->children. } else { const int index = parentD->children.indexOf(q); if (parentD->isDeletingChildren) { parentD->children[index] = 0; } else { parentD->children.removeAt(index); if (sendChildEvents && parentD->receiveChildEvents) { QChildEvent e(QEvent::ChildRemoved, q); QCoreApplication::sendEvent(parent, &e); } } } } parent = o; if (parent) { // object hierarchies are constrained to a single thread if (threadData != parent->d_func()->threadData) { qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread"); parent = 0; return; } parent->d_func()->children.append(q); if(sendChildEvents && parent->d_func()->receiveChildEvents) { if (!isWidget) { QChildEvent e(QEvent::ChildAdded, q); QCoreApplication::sendEvent(parent, &e); } } } if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged) QAbstractDeclarativeData::parentChanged(declarativeData, q, o); }
That is an excerpt from the Qt source relevant to reparenting the
QObject
s, but how exactly the parent is notified by its children is really beside the point. You only need to know that this is done, and you can safely delete theQObject
pointer at any time (even if it's a plain pointer).QPointer
is a convenience class for us (user programmers) so we can know when aQObject
is deleted (usually by Qt) when we don't own it. Just bear in mind that ownership of objects is weakly defined and is relevant more to the object hierarchy than it is to actual ownership of the objects themselves.Is it true that when I delete the
QWidget
, automatically the wrappingQLayoutItem
is also deleted and the layout is notified?I can't say for sure that the layout item will be deleted, but the layout holding the widget will be notified and it's going to adjust (sizes) accordingly. I'm pretty sure that if you use
QLayout::addWidget
to insert your widgets into layouts, the Qt's internal workings will take care of the layout item in case you delete the widget.To be very clear, consider the following code:
int main() { // ... QWidget window; QVBoxLayout layout(&window); QPushButton button(&window); layout.addWidget(&button); window.setLayout(&layout); window.show(); QApplication::exec(); }
You see that here the layout and button are created after the widget that contains them, but they're created on the stack. Now consider what will happen if you just simply
delete
the children in theQWidget
s destructor? Obviously you'll run into tons of trouble, because when the stack's unwinding the objects will be freed and then, when theQWidget
's destructor executes the layout and button objects will be deleted once again(!), you'll get a nasty segmentation fault. This was the stack vs heap allocation problem I was referring in my previous post. Qt's object management (by notifying the parent for the child's destruction), allows this construct to be valid (and I think better than allocating everything in the heap).I hope this clears things up.
Kind regards.