2014-01-27 115 views
4

我有一个充满动态创建的对象的Qlist。在终止我的程序之前,我打电话myqlist.clear()需要释放QList内容吗?

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

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


更新:我可以证实,mylist.erase(迭代器)从列表中删除的项目,而不是释放动态分配的对象。 (该对象是一个动态实例化的类)。很奇怪!我从Qlist切换到QLinkedList,但结果相同。请记住,我的QLinkedList是QLinkedList < MyClass的>不是QLinkedList < * MyClass的>

下面是情况下,实际的代码有人能找到我做错了什么:

// 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对象都是丢失的块!

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


见下文......问题的详细解决方案是,附加功能,使副本,并将其添加到列表中...我虽然它加入实际对象(不是副本)......所以,问题是'新'副本被泄露。

+0

更好的成语可能会存储智能指针,而不是在你的'QList'原始指针,所以你不必与明确地应对。它也使得拥有这些对象的人变得更加明确。 – ereOn

+0

@ereOn这是一个很好的建议,但并不适用于Qt。 – pmr

+0

@pmr:在[docs](http://qt-project.org/doc/qt-4.8/qlist.html)内没有任何地方声明'QList'拥有指针的所有权,我相信Qt拥有'QSharedPointer '所以我不确定这不适用。我在这里错过了什么吗? – ereOn

回答

4

您正在泄漏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()泄漏内存。 A QList对待这些指针就像它们是整数一样,并且对它们没有做任何特殊的处理。在C++中,销毁一个原始指针,就像销毁一个整数一样,是一个NO-OP。

  • 如果您在列表中存储QSharedPointerstd::shared_ptr,那么当您执行clear()时,您不会泄漏内存。智能指针被称为这种方式的原因:)

  • 如果你存储的对象本身,他们正常行为的C++类,那么一切都很好。

不能存储QObject直接在QList,让你的“对象” 不能的QObject - 它不会编译。

这只是正常,并表现正常:

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; 
} 
+0

哇!我一直在盯着这个代码几天,无法弄清楚。谢谢 – TSG

+1

@Michelle你真的需要得到Strostrup的“C++编程语言,第4版”并阅读第1至12章。**了解**。 –

0

这取决于你的对象。 Qt的概念是object ownership,它在树中组织对象。树的父亲一旦超出范围,就会删除所有的孩子。

如果你的对象不是由父对象管理的,你需要确保自己释放它们。 Qt还附带一套smart pointers,可以简化这一点。

+0

无法将'QObject'实例存储在'QList'中,并且其列表是一个对象列表(而不是QObject)。 –

+0

@KubaOber你可以存储'QObject *',并且你的对象可以从它派生出来,并且在OP添加他的实际代码之前,它听起来就像存储指针一样,因为他说他分配了这些对象... – pmr

2
qDeleteAll(list.begin(), list.end()); 
+2

有点解释为什么这应该工作,会很好。 – Alexander

+0

@Alexander它将工作,因为这是qDeleteAll函数的设计目的:http://doc.qt.io/qt-4.8/qtalgorithms.html#qDeleteAll-2 –

+1

另请注意,您可以使用qDeleteAll(list); –