Как работает C++/Qt - выделение памяти?

недавно я начал исследовать Qt для себя и имею следующий вопрос:

Предположим, у меня есть несколько QTreeWidget* widget. В какой-то момент я хочу добавить к нему некоторые элементы, и это делается с помощью следующего вызова:

QList<QTreeWidgetItem*> items;

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

widget->addTopLevelItems(items);

пока все выглядит нормально, но я на самом деле не понимаю, кто должен контролировать время жизни объектов. Я должен объяснить это с помощью пример:

допустим, другая функция вызывает widget->clear();. я не знаю, что происходит под этим вызовом, но я думаю, что память выделена для item1 и item2 не утилизируется здесь, потому что их собственность фактически не была передана. и, бах, у нас утечка памяти.

вопрос заключается в следующем - тут Qt есть что предложить в подобной ситуации? я мог бы использовать boost::shared_ptr или любой другой умный указатель и написать что-то вроде

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

но я не знаю, будет ли сам Qt пытаться сделать явным delete призывает мои указатели (что было бы катастрофическим, так как я заявляю их как shared_ptr-управляемые).

как бы вы решили эту проблему? может быть, все очевидно, и я пропустил что-то очень простое?

3 ответов


быстрый взгляд в qtreewidget.УОЗ показывает это:

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();
}

таким образом, кажется, что ваш вызов widget->addTopLevelItems() действительно заставляет QTreeWidget владеть QTreeWidgetItems. Таким образом, вы не должны удалять их самостоятельно или держать их в shared_ptr, или вы получите проблему двойного удаления.


Qt имеет свои собственные умные указатели, взгляните наhttp://doc.qt.io/archives/4.6/qsharedpointer.html . Как правило, рекомендуется использовать стандартную иерархию владения Qt, когда это возможно. Эта концепция описана здесь: http://doc.qt.io/archives/4.6/objecttrees.html

для вашего конкретного примера, это означает, что вы должны передать указатель на контейнер (т. е. QTreeWidget) в конструкторе дочерних объектов. Каждый Метод QWidget конструктор подкласса принимает указатель на QWidget для этой цели. Когда вы передаете свой дочерний указатель на контейнер, контейнер берет на себя ответственность за очистку детей. Вот как вам нужно изменить свой пример:

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

(Я не знаю, что ... в вашем примере, но важным для управления памятью Qt является последний аргумент).

ваш пример использования смарт-указателя

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

это не хорошая идея, потому что вы нарушьте золотое правило интеллектуальных указателей: никогда не используйте необработанный указатель управляемого объекта smart pointer напрямую.


многие экземпляры класса Qt могут быть построены с родительским QObject*. Этот родитель управляет временем жизни сконструированного ребенка. Посмотрите, можно ли qtreewidgetitem связать с родительским виджетом, что кажется так, читая Qt docs:

элементы обычно строятся с помощью родитель, который является QTreeWidget (для пунктов верхнего уровня) или QTreeWidgetItem (для элементов на нижнем уровни дерева).