Search code examples
c++qtqlist

Qt QList - removeAll deallocating memory


I have a QList of MyClass pointers like this:

QList<MyClass*> myList;

When I call myList.removeAll(someObjPtr); it doesn't just remove the pointers from the QList, it calls delete on them internally. Is there a way to get around this or is there an alternative QList method that would just remove the elements without deallocating them?

EDIT: Internals of removeAll:

template <typename T>
Q_OUTOFLINE_TEMPLATE int QList<T>::removeAll(const T &_t)
{
    int index = indexOf(_t);
    if (index == -1)
        return 0;

    const T t = _t;
    detach();

    Node *i = reinterpret_cast<Node *>(p.at(index));
    Node *e = reinterpret_cast<Node *>(p.end());
    Node *n = i;
    node_destruct(i);
    while (++i != e) {
        if (i->t() == t)
            node_destruct(i);
        else
            *n++ = *i;
    }

    int removedCount = e - n;
    d->end -= removedCount;
    return removedCount;
}

As you can see it calls node_destruct, which does this:

template <typename T>
Q_INLINE_TEMPLATE void QList<T>::node_destruct(Node *n)
{
    if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) delete reinterpret_cast<T*>(n->v);
    else if (QTypeInfo<T>::isComplex) reinterpret_cast<T*>(n)->~T();
}

As you see, there is a delete being called.


Solution

  • QList has nothing to do with Qt 3's QPtrList.

    None of the QList methods interpret stored pointers in a special way. You must be testing it wrongly. For example, the code below happily leaks two C instances and never deletes them.

    Perhaps you were thinking of qDeleteAll from QtAlgorithms? This one will delete the instances.

    Note that QList's implementation may allocate per-item memory to store your instances in, and will free that memory when appropriate. In no case will QList implementation delete a pointer that you store in the list: the only way QList interprets the stored data is via its type, and then it's only to decide whether the items are memory-movable, and whether they fit into a void* or do they need to be individually allocated. In fact, all pointer types are stored in QList as if it were a QVector, with a bit of room added at the beginning and the end to make push_front and push_back have amortized O(1) cost.

    #include <QtCore>
    
    struct C {
      static int ctr;
      C() { ctr ++; }
      ~C() { ctr --; qDebug() << (void*)this << "C instance destructed"; }
    };
    int C::ctr;
    
    int main() {
      auto c1 = new C, c2 = new C;
      auto list1 = QList<C*>() << c1 << c2;
      list1.removeAll(c1); // doesn't delete the pointed-to objects
      list1.removeAll(c2);
      Q_ASSERT(list1.isEmpty());
      Q_ASSERT(C::ctr == 2);
      // we'll happily leak both instances above
    
      auto list2 = QList<C*>() << new C << new C << new C;
      qDeleteAll(list2); // invokes delete on all objects
      Q_ASSERT(C::ctr == 2);
    }