我对QAbstractItemModel
的自定义模型(基类为QTreeView
)的实现存在问题。
简而言之,在调用beginRemoveRows()
之后,QAbstractItemModel
调用我parent()
的{{1}}实现,该QModelIndex
返回来自QModelIndex::internalPointer()
的悬空指针。这是调用堆栈:
好的,尽可能发布代码的一小部分..
我正在使用下一棵树:
以下是包含包含文件的项目的解决方案。
模型包含FileItem
类的实例:
class FileItem;
typedef QSharedPointer<FileItem> FileItemPtr;
class FileItem
{
public:
explicit FileItem(File* file, FileItem* parentItem = nullptr);
...
FileItem* child(int row); // returns @row element of @childItems_ vector
FileItem* parentItem() const; // returns @parentItem_
void removeChild(int row); // removes @row element from @childItems_ vector
private:
QVector<FileItemPtr> childItems_;
FileItem* parentItem_;
...
};
以下是模型&#39; s(FilesModel
)主要功能:
class FilesModel :
public QAbstractItemModel
{
...
private:
QModelIndex index(
int row,
int column = 0,
const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex& index) const Q_DECL_OVERRIDE;
// returns corresponding childCount() for valid @parent's internalPointer()
int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE
{
Q_UNUSED(parent);
return 1;
}
private:
QVector<FileItemPtr> items_;
}
QModelIndex
的创作:
QModelIndex FilesModel::index(
int row,
int column /*= 0*/,
const QModelIndex& parent /*= QModelIndex()*/) const
{
if(!hasIndex(row, column, parent))
return invalidIndex();
if(!parent.isValid()) // If, parent is invalid, then @row is our items_[row]
{
if(row < items_.size())
{
FileItem* item = items_.value(row).data();
// Pass @item as QModelIndex's internal pointer
return createIndex(row, column, item);
}
return invalidIndex();
}
FileItem* parentItem = static_cast<FileItem*>(parent.internalPointer());
FileItem* childItem = parentItem->child(row);
if(childItem)
// Pass @childItem as QModelIndex's internal pointer
return createIndex(row, column, childItem);
else
return invalidIndex();
}
获取项目的父级:
QModelIndex FilesModel::parent(const QModelIndex& index) const
{
if(!index.isValid())
return invalidIndex();
FileItem* childItem = static_cast<FileItem*>(index.internalPointer());
///////////////////////////////////////////////////////////////////////////
//
// @childItem is dandling poiner after call of beginRemoveRows()
// for root item. See FilesModel::removeRows()
//
FileItem* parentItem = childItem->parentItem();
if(parentItem == nullptr)
return invalidIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
好的,当我尝试删除任何文件(项目的子项目)和任何时,FilesModel::removeRows()
的下一个impl工作正常项目(解决方案的子项)。但是,当我删除解决方案项目(在视图的根元素上)时,我遇到了问题开头所描述的情况: QAbstractItemModel
调用parent()
函数QModelIndex
返回来自QModelIndex::internalPointer()
的悬空指针,但我将有效指针传递给createIndex()
,并且从不使用QModelIndex::internalPointer()
的显式删除。
好的,这是项目删除:
bool FilesModel::removeRows(int row, int count, const QModelIndex& parent /*= QModelIndex()*/)
{
Q_ASSERT(count > 0);
Q_ASSERT(row >= 0);
if(parent.isValid()) // Child items of @items_
{
// This branch works fine
for(int r = row; r < (row + count); ++r)
{
QModelIndex idxRemove = parent.child(r, 0);
Q_ASSERT(idxRemove.isValid());
FileItem* fiRemove = static_cast<FileItem*>(idxRemove.internalPointer());
Q_ASSERT(fiRemove);
if(idxRemove.child(0, 0).isValid()) // Has childrens
{
bool childRemoved = removeRows(0, fiRemove->childCount(), idxRemove);
Q_ASSERT(childRemoved);
}
}
FileItem* fiParent = static_cast<FileItem*>(parent.internalPointer());
Q_ASSERT(fiParent->childCount() >= (row + count - 1));
beginRemoveRows(parent, row, row + count - 1);
int childToRemove = row;
for(int r = row; r < (row + count); ++r)
fiParent->removeChild(childToRemove);
endRemoveRows();
return true;
}
else // Removing @items_
{
// Here is problem branch
Q_ASSERT(rowCount() >= (row + count - 1));
for(int r = row; r < (row + count); ++r)
{
FileItem* slnItem = items_.value(r).data();
bool projectRemoved = removeRows(0, slnItem->childCount(), index(r));
Q_ASSERT(projectRemoved);
}
///////////////////////////////////////////////////////////////////////
// This call to beginRemoveRows() cause call of parent() function
// with invalid QModelIndex::internalPointer()
//
beginRemoveRows(QModelIndex(), row, row + count - 1);
int slnRemove = row;
for(int r = row; r < (row + count); ++r)
items_.remove(slnRemove);
endRemoveRows();
return true;
}
return false;
}
任何人都知道这是什么问题?
我使用一个GUI线程处理此模型。我在Windows上有Qt 5.4.0。 感谢
更新
validateItem()
只检查指针值以通过QModelView::internalPointer()
void FileItem::validateItem() const
{
Q_ASSERT((reinterpret_cast<size_t>(this) > 0x1000)
&& "Invalid item");
for(const FileItemPtr& child : childItems_)
child->validateItem();
}
因此,为了找到问题,我的FilesModel::parent()
看起来像这样:
QModelIndex FilesModel::parent(const QModelIndex& index) const
{
if(!index.isValid())
return invalidIndex();
FileItem* childItem = static_cast<FileItem*>(index.internalPointer());
#if !defined(NDEBUG)
// Try to understand, if @childItem is OK
childItem->validateItem();
#endif
...
}
答案 0 :(得分:0)
您正在显示的调用堆栈表明您的代码断言失败,因此您应该知道问题是什么!
检查FileItem::validateItem
行nr 316中的代码,看看问题是什么。 (再次发生崩溃时,只需在调用堆栈视图中单击正确的(第二个)项目)
<小时/> 参考更新:
UPDATE:validateItem()只检查指针值以捕获 来自QModelView :: internalPointer()
的dandling指针的问题Q_ASSERT((reinterpret_cast(this)&gt; 0x1000)
WTF?谁告诉你,这是检测它的正确方法?悬空指针并不意味着指针有一些特定的值!