参考资料[2]已经对如何结合QTreeView和QStandardItemModel创建一个树形结构做了很详细的说明,本文不再赘言。本文的主要目的是描述在编程过程中遇到的问题。
1 问题
在参考资料[2]中,为QStandardItemModel添加Item时,使用到下面的代码片段:
QList<QStandardItem *> items;
for (int i = 0; i < 3; ++i)
{
QStandardItem *item = new QStandardItem(QString("item %0").arg(i));
if (0 == i)
item->setCheckable(true);
items.push_back(item);
}
goodsModel->appendRow(items);
从上述的代码中可见,每创建一个item,就需要使用new分配一次内存。(1)那么,在销毁QStandardItemModel的对象之前,是不是需要使用delete先将其各个item所占用的内存释放掉呢?正如参考资料[6]所提出的疑问那样。(2)另外,参考资料[8]也提出疑问,调用 clear()之后是否会释放QStandardItemModel所有Item的内存呢?(3)调用remoeRow()删除一行时,是否也会同时释放这一行所占用的内存呢?
2 实验
根据参考资料[7]的指出,QStandardItemModel会获取Item的所有权,因此在销毁时同时释放各个Item的内存, 然而View并没有获取QStandardItemModel的所有权(因为同一个Model可以被多个View使用),因此需要程序员手工释放QStandardItemModel对象内存;参考资料[8]则指出,当调用clear()之后,所有item的内存都会被释放。
上述的说法是否正确呢?下面做一个测试。
根据参考资料[2]创建一个棵简单的树
<pre name="code" class="cpp">MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
goodsModel = new QStandardItemModel(0, 3, this);
ui->treeView->setColumnWidth(0, 50);
ui->treeView->setColumnWidth(1, 200);
ui->treeView->setColumnWidth(2, 200);
ui->treeView->setColumnWidth(3, 200);
goodsModel->setHeaderData(0, Qt::Horizontal, tr("No"));
goodsModel->setHeaderData(1, Qt::Horizontal, tr("name"));
goodsModel->setHeaderData(2, Qt::Horizontal, tr("value1"));
ui->treeView->setModel( goodsModel );
QList<QStandardItem *> items;
for (int i = 0; i < 3; ++i) {
QStandardItem *item = new QStandardItem(QString("item %0").arg(i));
if (0 == i)
item->setCheckable(true);
items.push_back( item );
}
goodsModel->appendRow( items );
// 释放Items内存
// delete goodsModel;
// goodsModel = NULL; // 避免在析构函数中再次释放内存
// goodsModel->removeRow(0); // 删除第0行
// goodsModel->clear(); // 清空Model
// 尝试访问Items的内存
for (int i = 0; i < items.length(); i++) {
qDebug() << items.at(i)->column();
}
}
效果如下图(证明创建树成功):
图 1
若是将上述代码中下述语句的前面两行或者第3第4行的其中一行
// 释放Items内存
// delete goodsModel;
// goodsModel = NULL; // 避免在析构函数中再次释放内存
// goodsModel->removeRow(0); // 删除第0行
// goodsModel->clear(); // 清空Model
这时候再编译运行则提示:
程序异常结束。
出现这个异常,主要是因为delete goodsModel,removeRow(0)或者clear()都会导致Moel释放Item的内存,下面语句尝试继续访问Item的内存时,产生非法内存访问
// 尝试访问Items的内存
for (int i = 0; i < items.length(); i++) {
qDebug() << items.at(i)->column();
}
如果将上面的代码片段注释掉,则不会出现
程序异常结束的情况,但是所创建的树是空的
图 2 goodsModel->removeRow(0)的结果
图3 goodsModel->clear() / delete goodsModel的结果
上述实验验证了参考资料[7][8]的说法是正确的。同时也回答了第1章的第3个问题:removeRow()也会导致内存的释放。
然而,正如参考资料[8]所指出的那样,QList是不会自动释放添加到其中的指针变量的(详细释放方法见参考资料[11]),唯有将各个Item添加到QStandardItemModel之后,QStandardItemModel才为它们管理内存。
参考资料
[2]Qt TreeView
[3]Qt中如何自定义ListView/TreeView单个item的显示和响应
[5]Qt树形控件QTreeView使用1——节点的添加删除操作 复选框的设置
[6]Qt: Does this constitute a memory leak or not?
[8]QStandardItemModel with QStandardItem usage
[9]Qt: QStandardItemModel的极品提示