Search code examples
c++qtexceptionqtreeviewqabstractitemmodel

QTreeView/QAbstractItemModel - delete causes exception


I've written a class that derives from QAbstractModelItem. It is used on a QTreeView. Sadly the official documentation example does not show how to add or remove items on a model. I thought this would be easy to do, so I hacked my way through it. The problem is that deleting the selected object causes an exception. Example:

The user clicks a row on the QTreeView and wants to delete it with all its children (if any). This is the code that gets executed:

MyModel.cpp:

// This gets called with the QModelIndex of the currently selected row.
void MyModel::remove(const QModelIndex &index) {
    if (!index.isValid()) 
        return;
    MyItem * selectedItem = static_cast<MyItem*>(index.internalPointer());
    beginRemoveRows(index, index.row(), index.row());
    // Call the method on the selected row object that will delete itself and all its children:
    selectedItem->destroy();
    endRemoveRows();
}

MyItem.h:

// A pointer list with child items.
std::vector<MyItem*> children;

MyItem.cpp:

// Deletes all children of this object and itself.
void MyItem::destroy(bool isFirst) {
    if (children.size() > 0) {
        // Each child needs to run this function, to ensure that all nested objects are properly deleted:
        for each (auto child in children)
            child->destroy(false);
        // Now that the children have been deleted from heap memory  clear the child pointer list:
        children.clear();
    }
    // This boolean determines wether this object is the selected row/highest object in the row hierachy. Remove this object from the parent's pointer list:
    if(isFirst)
        parent->removeChild(this);
    // And lastly delete this instance:
    if(!isFirst) // This will cause a memory leak, but it is necessary
        delete this; // <- because this throws an exception if I run this on the first object.
}

// Removes a single child reference from this instance's pointer list.
void MyItem::removeChild(MyItem * child)
{
    auto it = std::find(children.begin(), children.end(), child);
    children.erase(it);
}

Now this works just fine if we don't mind the minor memory leak. ^^

But if I try to run the delete command on the first/selected row object - then one of two exceptions occur:

  1. The row has children: Exception thrown: read access violation. this was 0xDDDDDDDD.
  2. The row has no children: Exception thrown: write access violation. _Parent_proxy was 0x64F30630.

I kept the code short but hopefully it contains my error. Or does somebody know a good QTreeView/QAbstractItemModel example that shows how to add/remove items?

Kind regards Sora


Solution

  • I think there is an error in the MyModel::remove method. The beginRemoveRows takes as first parameter the parent index, not the index itself. Yuo have to replace the line with this:

    beginRemoveRows(index.parent(), index.row(), index.row());