Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How C++/Qt - Memory allocation works?

I recently started investigating Qt for myself and have the following question:

Suppose I have some QTreeWidget* widget. At some moment I want to add some items to it and this is done via the following call:

QList<QTreeWidgetItem*> items;

// Prepare the items
QTreeWidgetItem* item1 = new QTreeWidgetItem(...);
QTreeWidgetItem* item2 = new QTreeWidgetItem(...);
items.append(item1);
items.append(item2);

widget->addTopLevelItems(items);

So far it looks ok, but I don't actually understand who should control the objects' lifetime. I should explain this with an example:

Let's say, another function calls widget->clear();. I don't know what happens beneath this call but I do think that memory allocated for item1 and item2 doesn't get disposed here, because their ownage wasn't actually transfered. And, bang, we have a memory leak.

The question is the following - does Qt have something to offer for this kind of situation? I could use boost::shared_ptr or any other smart pointer and write something like

shared_ptr<QTreeWidgetItem> ptr(new QTreeWidgetItem(...));
items.append(ptr.get());

but I don't know if the Qt itself would try to make explicit delete calls on my pointers (which would be disastrous since I state them as shared_ptr-managed).

How would you solve this problem? Maybe everything is evident and I miss something really simple?

like image 825
Yippie-Ki-Yay Avatar asked Feb 04 '23 04:02

Yippie-Ki-Yay


2 Answers

A quick peek into qtreewidget.cpp shows this:

void QTreeWidget::clear()
{
   Q_D(QTreeWidget);
   selectionModel()->clear();
   d->treeModel()->clear();
}

void QTreeModel::clear()
{
   SkipSorting skipSorting(this);
   for (int i = 0; i < rootItem->childCount(); ++i) {
       QTreeWidgetItem *item = rootItem->children.at(i);
       item->par = 0;
       item->view = 0;
       delete item;     //   <<----- Aha!
   }
   rootItem->children.clear();
   sortPendingTimer.stop();
   reset();
}

So it would appear that your call to widget->addTopLevelItems() does indeed cause the QTreeWidget to take ownership of the QTreeWidgetItems. So you shouldn't delete them yourself, or hold them in a shared_ptr, or you'll end up with a double-delete problem.

like image 134
Jeremy Friesner Avatar answered Feb 05 '23 17:02

Jeremy Friesner


Qt has its own smart pointers, take a look at http://doc.qt.io/archives/4.6/qsharedpointer.html . Normally, it is though advisable to use the standard ownership hierarchy of Qt whenever possible. That concept is described here: http://doc.qt.io/archives/4.6/objecttrees.html

For your concrete example, this means that you should pass a pointer to the container (i.e. the QTreeWidget) to the constructor of the child objects. Every QWidget subclass constructor takes a pointer to a QWidget for that purpose. When you pass your child pointer to the container, the container takes over the responsibility to clean up the children. This is how you need to modify your example:

QTreeWidgetItem* item1 = new QTreeWidgetItem(..., widget);
QTreeWidgetItem* item2 = new QTreeWidgetItem(..., widget);

(I don't know what the ... are in your example, but the important thing for Qt memory management is the last argument).

Your example of using a smart pointer

shared_ptr<QTreeWidgetItem> ptr(new QTreeWidgetItem(...));
items.append(ptr.get());

is not a good idea, because you break the golden rule of smart pointers: Never use the raw pointer of a smart pointer managed object directly.

like image 31
theDmi Avatar answered Feb 05 '23 19:02

theDmi