Как работает C ++ / Qt - выделение памяти? - PullRequest
9 голосов
/ 09 июня 2010

Я недавно начал исследовать 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 ]

7 голосов
/ 09 июня 2010

Быстрый просмотр qtreewidget.cpp показывает это:

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, иначе у вас возникнет проблема двойного удаления.

4 голосов
/ 09 июня 2010

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

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

1 голос
/ 09 июня 2010

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

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

...