Qt :: QAbstractItemModel :: beginRemoveRows()崩溃

时间:2015-01-05 12:35:30

标签: c++ qt qabstractitemmodel

我对QAbstractItemModel的自定义模型(基类为QTreeView)的实现存在问题。

简而言之,在调用beginRemoveRows()之后,QAbstractItemModel调用我parent()的{​​{1}}实现,该QModelIndex返回来自QModelIndex::internalPointer()的悬空指针。这是调用堆栈: Call stack

好的,尽可能发布代码的一小部分..

我正在使用下一棵树:

Tree view

以下是包含包含文件的项目的解决方案。


模型包含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()

好的,当我尝试删除任何文件项目的子项目)和任何时,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
    ...
}

1 个答案:

答案 0 :(得分:0)

您正在显示的调用堆栈表明您的代码断言失败,因此您应该知道问题是什么!

检查FileItem::validateItem行nr 316中的代码,看看问题是什么。 (再次发生崩溃时,只需在调用堆栈视图中单击正确的(第二个)项目)

<小时/> 参考更新:

  

UPDATE:validateItem()只检查指针值以捕获   来自QModelView :: internalPointer()

的dandling指针的问题      

Q_ASSERT((reinterpret_cast(this)&gt; 0x1000)

WTF?谁告诉你,这是检测它的正确方法?悬空指针并不意味着指针有一些特定的值!