【问题标题】:Need to free QList contents?需要释放 QList 内容吗?
【发布时间】:2014-02-18 14:41:57
【问题描述】:

我有一个充满动态创建的对象的 Qlist。在终止我的程序之前,我调用了 myqlist.clear()

我的问题是:这是否也会删除(释放)列表中包含的对象? Valgrind 给了我一些丢失的块,我想知道我是否误解了 qlist clear 方法的工作原理。

或者,我是否需要遍历 qlist 并删除每个对象?


更新:我可以确认 mylist.erase(iterator) 正在从列表中删除项目,但没有释放动态分配的对象。 (对象是一个动态实例化的类)。很奇怪!我从 Qlist 切换到 QLinkedList 但结果相同。请记住,我的 QLinkedList 是 QLinkedList 而不是 QLinkedList

这是实际代码,以防有人发现我做错了什么:

// Here I define a couple important items.  Note that AMISendMessageFormat is a class
typedef QLinkedList<AMISendMessageFormat> TSentMessageQueue;
TSentMessageQueue m_sentMessageQueue;

// Here I create the message and append to my QLinkedList
AMISendMessageFormat *newMessage = new AMISendMessageFormat(messageToSend);
m_sentMessageQueue.append(*newMessage); 

// Here I delete
for (TSentMessageQueue::Iterator sMessagePtr = m_sentMessageQueue.begin(); sMessagePtr != m_sentMessageQueue.end(); )
{
    sMessagePtr = m_sentMessageQueue.erase(sMessagePtr);  
    qDebug() << "Sent size after erase: " << m_sentMessageQueue.size();  // Confirmed linked list is shrinking in size
}

在遍历列表并擦除之后,valgrind 显示每个 AMISendMessageFormat 对象都是丢失的块!

我怀疑这与使用迭代器在循环内擦除有关...但我无法理解这一点!


请参阅下面的详细解决方案...问题是 append 函数创建一个副本并将其添加到列表中...我虽然它正在添加实际对象(不是副本)...所以问题是'新的副本被泄露了。

【问题讨论】:

  • 一个更好的习惯用法可能是在你的QList 中存储智能指针而不是原始指针,这样你就不必明确地处理它。它还使谁拥有这些对象的所有权更加明确。
  • @ereOn 总的来说这是个好建议,但并不真正适用于 Qt。
  • @pmr: 在docs 中没有任何地方声明QList 拥有指针的所有权,我相信Qt 有一个QSharedPointer,所以我不确定这如何不适用。我在这里错过了什么吗?
  • @ereOn 是的,但是如果 OP 正在使用 QObjects,这很有可能(因为这是一个 Qt 问题)。我只是觉得跳过这个话题的答案并不完整。
  • @Michelle:你在附加一个对象,而不是一个指向对象的指针,所以append 没有办法接受它。它不知道涉及指针。在append 看到它之前取消引用指针。您还必须学会不要无缘无故地在堆上分配东西。如果它适合堆栈,请使用堆栈。

标签: c++ qt memory delete-operator qlist


【解决方案1】:

这取决于您的对象。 Qt 有一个object ownership 的概念,它将对象组织成树。树的父级在超出范围后立即删除其所有子级。

如果您的对象不是由父对象管理的,您需要确保自己解除分配它们。 Qt 还附带了一组smart pointers 来简化此操作。

【讨论】:

  • 没有办法将QObject 实例存储在QList 中,他的列表是对象列表(不是QObjects)。
  • @KubaOber 您可以存储QObject*,并且您的对象可以从中派生,在 OP 添加他的实际代码之前,听起来他会存储指针,因为他说他分配了对象......
【解决方案2】:

您正在泄漏newMessage 指向的实例。这与列表无关!您没有从列表中泄漏。解决方案:

// Best

m_sentMessageQueue << AMISendMessageFormat(messageToSend);

// Same, more writing

AMISendMessageFormat newMessage(messageToSend);
m_sentMessageQueue << newMessage;

// Rather pointless allocation on the heap

QScopedPointer<AMISendMessageFormat> newMessage(new AMISendMessageFormat(messageToSend));
m_sentMessageQueue << *newMessage; 

请注意,在每种情况下,您都将对象的副本存储到列表中。 重要提示:您必须验证 AMISendMessageFormat 是一个行为正确的 C++ 类,可以安全地复制构造和分配给它,而不会泄漏资源。

如果您没有定义复制构造函数和赋值运算符,那么您在此类中使用的所有数据成员都必须可以安全地复制和分配,而不会泄漏。所有 Qt 和 C++ 标准库类都不会在这种使用下编译,或者会正常运行。如果你使用裸指针,你已经在脚上开枪了,所以至少使用正确的QSharedPointer

在编辑之前,你没有说你的对象是什么。

  • 如果您将原始指针存储在列表中,那么当您执行clear() 时,您肯定会泄漏内存。 QList 将这些指针视为整数,并且不会对它们做任何特殊处理。在 C++ 中,原始指针的销毁,就像整数的销毁一样,是一个 NO-OP。

  • 如果您将QSharedPointerstd::shared_ptr 存储在列表中,那么您在执行clear() 时不会泄漏内存。以这种方式调用智能指针是有原因的 :)

  • 如果您自己存储对象,并且它们是行为正确的 C++ 类,那么一切都很好。

您不能将QObject 直接存储在QList 中,因此您的“对象”不能是 QObjects - 它不会编译。

这工作得很好并且行为正常:

QList<QString> stringList1;
QList<QSharedPointer<QString> > stringList2;

stringList1 << "Foo" << "Bar" << "Baz";
stringList2 << new QString("Foo") << new QString("Bar") << new QString("Baz");

Q_ASSERT(stringList1.at(0) == *stringList2.at(0));
stringList1.clear();
stringList2.clear(); // no memory leaks

这会泄漏内存,你几乎不需要编写这样的代码:

QList<QString*> stringList3;
stringList3 << new QString("Foo") << new QString("Bar") << new QString("Baz");
stringList3.clear();

还要注意QList 和所有像样的 C++ 容器类型都是 RAII。这意味着他们将释放他们在销毁时使用的资源。这意味着您不需要在列表上调用clear(),除非您真的希望清除列表。此代码不会泄漏资源。列表的析构函数会在main()返回之前被调用,列表的析构函数会销毁所有的字符串,它们都会正确的释放它们分配的堆内存。

int main() {
    QList<QString> stringList1;
    stringList1 << "Foo" << "Bar" << "Baz";
    return 0;
}

【讨论】:

  • 哇...我已经盯着这段代码看了好几天了,还是想不通。谢谢
  • @Michelle 你真的需要阅读 Strostrup 的“C++ 编程语言,第 4 版”并阅读第 1 章到第 12 章。理解
【解决方案3】:
qDeleteAll(list.begin(), list.end());

【讨论】:

猜你喜欢
  • 2011-01-17
  • 1970-01-01
  • 2015-11-02
  • 1970-01-01
  • 1970-01-01
  • 2010-09-08
  • 2012-01-22
  • 2013-07-31
  • 1970-01-01
相关资源
最近更新 更多