C++/Qt - 内存分配是如何工作的?

9

最近我开始研究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(); 我不知道这个调用下面会发生什么,但我认为为item1item2分配的内存在这里不会被释放,因为它们的所有权实际上并没有转移。 然后,我们就会出现内存泄漏。 问题是 - Qt对这种情况有什么提供吗? 我可以使用boost::shared_ptr或任何其他智能指针,并编写类似于:
shared_ptr<QTreeWidgetItem> ptr(new QTreeWidgetItem(...));
items.append(ptr.get());

但我不知道Qt本身是否会尝试对我的指针进行显式的delete调用(这将是灾难性的,因为我声明它们是由shared_ptr管理的)。

你会如何解决这个问题?也许一切都很明显,我错过了某些非常简单的东西吗?

3个回答

7
快速查看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

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

这不是一个好主意,因为你违反了智能指针的黄金法则:永远不要直接使用智能指针管理对象的原始指针。


那么,如果我按照你所写的将子项与父部件进行“绑定”,我可以确定clear()调用会释放该内存吗? - Yippie-Ki-Yay
可以的。只要你使用Qt的内存管理,Qt会确保在其父对象被删除时所有对象都被删除(因此,Qt在父子内存管理方面严格遵循基于树形结构的操作)。 - theDmi
如果您使用append()、addWidget()或类似的函数,即使不显式地在构造函数中设置父对象也不会有问题。 - rubenvb

1
许多Qt类实例可以使用父QObject*构造。该父对象控制所构造子对象的生命周期。通过阅读Qt文档,发现QTreeWidgetItem似乎可以链接到父窗口小部件,通常使用QTreeWidget(对于顶级项)或QTreeWidgetItem(对于树的较低级别上的项)作为父项构造项。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接