When can a QList NOT be replaced by a QVector

255 Views Asked by At

After reading that the QVector should be the first choice (over QList) for containers in Qt, I merrily changed some of the QList in my code to QVector and went on with my business, pretty much forgetting about those changes.... until I started getting strange crashes which did not happen before.

The crashes happened in my local copy of the QQuickTreeModelAdaptor1 class implementation, that I renamed TreeModelAdaptor, which is used to display tree-like C++ models in a QML view.

In the header, I replaced:

    QList<TreeItem> m_items;
    QList<TreeItem *> m_itemsToExpand;

by

    QVector<TreeItem> m_items;
    QVector<TreeItem *> m_itemsToExpand;

and in the method TreeModelAdaptor::modelRowsAboutToBeMoved()

const QList<TreeItem> &buffer = m_items.mid(startIndex, totalMovedCount);

by:

const QVector<TreeItem> &buffer = m_items.mid(startIndex, totalMovedCount);

I couldn't figure out why the crashes happened, and then I read the following note on the QVector doc page:

Note: Iterators into a QLinkedList and references into heap-allocating QLists 
remain valid as long as the referenced items remain in the container. This is 
not true for iterators and references into a QVector and non-heap-allocating QLists.

That's when I remembered the changes I made, and reverted to QList. After that, the crashes seemed to be gone.

Can the crashes I experienced be understood in light of the above Note? The source code is a bit too much to post here, but I would appreciate it if someone could point out where and how in the code the above Note would be pertinent?

Also, in light of the above, what about the unification between QList and QVector that has been introduced in Qt6, since it says:

QVector is used as the underlying implementation.

Does that mean that above code would crash in Qt6 regardless?

EDIT: As @jarman rightfully suggested, the crashes happen at the following ASSERT, almost randomly. If I expand an item, then do some other expansions on other branches, etc, and toggle expansion on/off a while like that is usually when it happens.

void TreeModelAdaptor::expandPendingRows(bool doInsertRows)
{
    while (!m_itemsToExpand.isEmpty()) {
        TreeItem *item = m_itemsToExpand.takeFirst();

        Q_ASSERT(item->expanded);

        const QModelIndex &index = item->index;
        int childrenCount = m_model->rowCount(index);
        if (childrenCount == 0) {
            if (m_model->hasChildren(index) && m_model->canFetchMore(index))
                m_model->fetchMore(index);
            continue;
        }
        // TODO Pre-compute the total number of items made visible
        // so that we only call a single beginInsertRows()/endInsertRows()
        // pair per expansion (same as we do for collapsing).
        showModelChildItems(*item, 0, childrenCount - 1, doInsertRows, false);
    }
}
0

There are 0 best solutions below