Creating and Deallocating a Qt Widget Object

Creating and deallocating a Qt widget object

There's no magic involved. Simply put, a QObject automatically deletes its children in its destructor. So, as long as your widget has a parent and that you destroy that parent, you don't have to worry about the children. So if you wondered what was that QObject * parent parameter, well, that's what it's there for.

Also, from the doc:

All child objects are deleted. If any of these objects are on the stack or global, sooner or later your program will crash.

So, avoid giving parents to objects that are stack-allocated.

How does Qt delete objects ? And what is the best way to store QObjects?

The QObject implementation of the Composite Design Pattern has been tried and tested through the many versions of Qt.

The pattern requires that the composite object takes ownership of the children so, as long as the parenting has been done, you can be assured that the child QObjects will be destroyed when the parent is destroyed.

Standard practice is to create child objects in heap memory and parent them immediately. If you don't parent immediately, you can explicitly parent using the setParent() function, or else parenting will be done automatically when you add the widget to a parent widget, either using addWidget() or addLayout().

QLayout objects are size and layout managers of other QLayouts and of QWidgets. They don't own the objects they manage. The parent is actually the QWidget that the QLayout is the child of.

You have a choice to create the root parent in stack memory or in heap memory.

If you feel more comfortable with smart pointers, there are two classes that are specifically for QObjects: QPointer and QSharedPointer. Each has their pros and cons.

#include <QApplication>
#include <QLabel>
#include <QHBoxLayout>
#include <QWidget>

int main(int argc, char **argv)
{
QApplication app(argc, argv);

QWidget widget; // Root parent so can create as a auto-deleting object on the stack
QHBoxLayout *layout = new QHBoxLayout(&widget); // Create on the heap and parent immediately
QLabel *label = new QLabel("label", &widget); // Create on the heap and parent immediately

layout->addWidget(label); // widget remains label's parent
widget.setLayout(layout); // widget is changed to layout's parent if necessary, as well
// as any widgets that layout manages
widget.show();
return app.exec();

// layout and label are destroyed when widget is destroyed
}

About deleting, removing widgets and layouts in Qt 4

Ok. I got it working.
Let me explain how this Removing, keeping widgets works.

A widget is known by its parent layout. And you remove it through the layout. By doing:

layout()->removeAt(widget);
delete widget;

If you use takeAt(index) in a QLayout (or its children), it gives you a QLayoutItem. To access the widget inside, just use widget(). But there's no way to remove the widget without deleting it. So this approach is non valid.

In the Docs it tells a way to delete the elements:

QLayoutItem *child;
while ((child = layout->takeAt(0)) != 0) {
...
delete child;
}

A special thing to note in Qt is the following:
If you have a hierarchy tree of layouts, added with addLayout() inside layouts, no matter how deep your widget is inserted, you can remove it from the child layouts or any of the parent layouts, if the tree path from the layout and this item is built from child layouts.

The easiest thing is to keep a list of pointers to all the items, in a custom table. When clearing the table to reconstruct it, just do this inside your widget:

  CustomTableItem* item;
while ( !items_.isEmpty() && ( (item = items_.takeFirst()) != 0 ) ){
layout()->removeWidget(item);
delete item; // It works no matter where the item is
}

items_.clear(); // clear the list afterwards.

And it works perfectly, updates the layout too by itself.
If you want to keep the elements, just skip the "delete item;" and use them afterwards.

An important thing to note is that different "remove" functions work differently (as i understand on Qt Docs) in QList or similar widgets, and in a QLayout.

In the QList, removeAt actually removes the object.

(Qt 4.7 QList Docs)"Removes the item at index position i. i must be a valid index position in the list (i.e., 0 <= i < size())."

In a QLayout, removeWidget or removeItem don't remove the item/widget, you have the responsability to delete it, as I did before.

(Qt 4.7 QLayout Docs) "Removes the widget widget from the layout. After this call, it is the
caller's responsibility to give the widget a reasonable geometry or to
put the widget back into a layout."

Hope it helps. If you see any error, you could tell me and I will edit the answer!

More on deleting here:
Other stackoverflow post

Issue regarding setting parent of widget in Qt

Does every widget necessary needed/required a parent?

If you want them to be deleted automatically - yes. But...

If no, what are the exceptions? If yes, what happens to widgets without parent?

You do not need to provide a parent to widget if you attach it to layout using QLayout::addWidget. If you look into source code, you'll see that when you do so, it automatically attaches layout's parent as widget's parent(unless you didn't attach layout to any widget).

But if you leave the widget created with new without parent and do not attach to anything - it is leaking memory. You must delete it either using delete or QObject::deleteLater. The last option is recommended when object has any connections.

Does a widget without parent guarantee to cause a memory leak (or something similar) when the widget which it stays in (not necessary its parent) is deleted? Or if it's removed from a layout without any deletion happening?

As I already mention QLayout::addWidget sets parent for added widget, so the answer is no. Also note, that when you call QLayout::removeWidget, it removes only QLayoutItem from layout, but widget's parent stays the same as it was after calling QLayout::addWidget.

How to deallocate memory when a Qt window closes?

You can do that in e.g. your closeEvent(). Alternatively, if you use Qt::WA_DeleteOnClose for your widget attributes, the widget will be deleted when it is closed, which means you can place some clean-up routines in the destructor.

Qt - How a QObject with a parent on the stack might get deleted twice?

Sorry, downvoting the accepted answer. Both versions as currently explained are wrong, and especially the conclusion is very wrong.

Parent/child for memory management

Amongst other things, Qt uses the parent/child concept to manage memory. When an object is parented to another one, then

  • deleting the parent will also delete (via operator delete) all of its children. Of course, this works recursively;
  • deleting a child will un-parent it, so that the parent will not attempt a double deletion. deleteLater is not required for this to work -- any deletion will un-parent it.

This allows you to build a tree of QObjects via repeated dynamic allocations through operator new, and not having the problem of having to manually delete all the allocated objects. Just give them parents, and you'll only have to delete the root of the tree. You can also delete a child (i.e. a subtree) at any time, and that will do the right thing™.

In the end, you will have no leaks and no double deletions.

This is why in constructors you'll see things like:

class MyWidget : public QWidget // a QObject subclass
{
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr);

// default destructor is fine!

private:
// raw pointers:
// we won't own these objects through these pointers.
// we just need them to access the pointees
QTimer *m_timer;
QPushButton *m_button;
};

void MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
// don't need to save the pointer to this child. because reasons
auto lineEdit = new QLineEdit(this);
auto validator = new QIntValidator(lineEdit); // a nephew

// but let's save the pointers to these children
m_timer = new QTimer(this);
m_button = new QPushButton(this);
// ...
}

The default destructor will properly delete the entire tree, although we allocated the children objects through calls to operator new, and we didn't even bother to save in members the pointers to some children.

QObjects on the stack

You are allowed (and in certain contexts it's actually a good idea) to give parents to objects allocated on the stack.

The typical example is of QDialog subclasses:

void MyWidget::showOptionsDialog()
{
// OptionsDialog is a QDialog subclass;
// create an instance as a child of "this" object
OptionsDialog d(this);

// exec the dialog (i.e. show it as a modal dialog)
conts auto result = d.exec();

if (result == QDialog::Accept) {
// apply the options
}

// d gets destroyed here
// => it will remove itself as a child of this
}

The purpose of passing this as the dialog's parent is to allow the dialog to be centered upon the parent's widget, share the task tray entry, and be modal against it. This is explained in QDialog docs. Also, ultimately, d does only need to live in that function, so it's a good idea to declare it as an automatic variable (i.e. allocated on the stack).

There you go: you've got a QObject, allocated on the stack, with a parent.


So what's the danger of QObjects on the stack? Consider this code:

QObject *parent = new QObject;
QObject child(parent);
delete parent;

As explained before, parent here will attempt to call operator delete on child, an object which was not allocated using new (it's on the stack). That's illegal (and a likely crash).

Obviously, nobody writes code like this, but consider the dialog example above again. What if, during the call d.exec(), we manage somehow to delete this, that is, the dialog's parent? That could happen for a variety of reasons very, very difficult to track down -- for instance, data arrives on a socket which causes widgets in your UI to change, creating some and destroying others. Ultimately, you would delete a stack variable, crashing (and having an extremely hard time trying to reproduce the crash).

Hence the suggestion to avoid creating code like that in the first place. It's not illegal, it may work, but it may also not work, and nobody likes fragile code.



Related Topics



Leave a reply



Submit