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-plugins

    I suggested to cross-post here, since in this forum the question is more visible to Qt experts. Hopefully I'm right.


  • Lifetime Qt Champion

    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


  • Qt Champions 2016

    @Jakob
    Just deleting the widget doesn't work? In principle Qt handles QObject 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 handle QObject 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 the QObject it's referencing gets deleted. You could create your widget in your plugin, keep a QPointer 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 the destroyed 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 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, because internally all these pointers are wrapped in QPointer?

    Is this guaranteed always to be true? One thing that springs to mind for instance, is how Qt internally also wraps QWidget pointers in a QLayoutItem instance when adding the widget to some layout. Is it true that when I delete the QWidget, automatically the wrapping QLayoutItem is also deleted and the layout is notified?


  • Qt Champions 2016

    @Jakob

    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 of QPointer, actually it's not really done through the destroyed signal also. Qt will use the QMetaObject instance attached to each of the QObjects 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 QObjects, 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 the QObject 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 a QObject 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 wrapping QLayoutItem 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 the QWidgets destructor? Obviously you'll run into tons of trouble, because when the stack's unwinding the objects will be freed and then, when the QWidget'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.



  • @kshegunov Loads and loads of gratitude for your elaborate and detailed answer and the effort to look things up. We have certainly gained a lot of new insight now that will help moving forwards. Thanks again!


  • Qt Champions 2016

    @Jakob

    You're welcome. I was a bit concerned that I'm not making my explanation clear enough, but happily that doesn't seem to be the case. Good luck!

    Kind regards.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.