【问题标题】:Qt QList - removeAll deallocating memoryQt QList - removeAll 释放内存
【发布时间】:2025-11-23 11:05:01
【问题描述】:

我有一个 QListMyClass 指针,如下所示:

QList<MyClass*> myList;

当我调用myList.removeAll(someObjPtr); 时,它不仅会从QList 中删除指针,它还会在内部对它们调用delete。有没有办法解决这个问题,或者是否有替代的 QList 方法可以删除元素而不释放它们?

编辑: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;
}

如您所见,它调用了 node_destruct,它执行以下操作:

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();
}

如您所见,调用了一个删除操作。

【问题讨论】:

  • 当然,删除是在内部节点上调用的,而不是在您的指针上。在这种情况下,T* n-&gt;v 是由QList 分配的。

标签: c++ qt qlist


【解决方案1】:

QList与Qt 3的QPtrList无关。

QList 方法都没有以特殊方式解释存储的指针。你一定是在错误地测试它。例如,下面的代码很高兴地泄漏了两个 C 实例并且从不删除它们。

也许你从QtAlgorithms 想到了qDeleteAll?这个删除实例。

请注意,QList 的实现可能会分配每个项目的内存来存储您的实例,并会在适当的时候释放该内存。在任何情况下,QList 实现都不会删除您存储在列表中的指针:QList 解释存储数据的唯一方法是通过它的类型,然后它只是决定这些项目是否是内存可移动的,以及它们是否适合void* 还是需要单独分配。实际上,所有指针类型都存储在QList中,就好像它是QVector一样,在开头和结尾添加了一点空间,使push_frontpush_back有摊销 O(1) 成本。

#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);
}

【讨论】:

    【解决方案2】:

    node_destruct 不会删除指针的 QLists 所指向的对象。如果仔细观察,它只会删除大型和静态类型的 n-&gt;v。 QList 内部在堆上分配大的和静态的类型,所以删除是必要的。

    对于指针类型,QTypeInfo&lt;T&gt; 特化是:

    template <typename T>
    class QTypeInfo<T*>
    {
    public:
        enum {
            isPointer = true,
            isComplex = false,
            isStatic = false,
            isLarge = false,
            isDummy = false
        };
    };
    

    如你所见,指针既不是大的也不是静态的,所以不会被删除

    【讨论】:

    • 类型信息无关紧要。即使您使用Q_DECLARE_TYPEINFO 使指针类型变得复杂或很大,它也只会存储在堆上的块中,并且该块将被删除在任何情况下都不会删除您的指针。它将被destructed,这样的破坏是微不足道的,但那是因为我们不能忘记指针是一种可以构造和破坏的值类型,就像任何其他值类型一样。只是这样的构造和破坏与指针指向的内容有关。就像intptr_t
    • @KubaOber 这就是我的观点。我认为 OP 误解了 delete reinterpret_cast&lt;T*&gt;(n-&gt;v) 行,这意味着指针的 QLists 删除了指向的对象,但事实并非如此!我厌倦了提供更多内部 Qt 代码来证明这一事实。也许答案毕竟不是那么有用......
    • 在答案中,您声称类型信息在某种程度上很重要。它没有。即使指针 很大或者是静态的,它们也不会被删除。
    • 你是对的,QList 不可能删除指向的对象。我试图指出混乱的根源,并解释为什么其中一个假设是错误的,仅基于node_destruct 的实现。 (第二个错误假设是n-&gt;v 是传递给QList&lt;T*&gt; 的原始指针,尽管它是T**