Нужно освободить содержимое QList?

У меня есть полный список объектов, созданных динамически. Перед прекращением моей программы я вызываю myqlist.clear()

Мой вопрос: это также удаляет (бесплатно) объекты, которые содержатся в списке? Valgrind дает мне несколько потерянных блоков, и мне интересно, правильно ли я понял, как работает метод qlist clear.

Или мне нужно перебрать qlist и удалить каждый объект?


Обновление: я могу подтвердить, что mylist.erase (итератор) удаляет элемент из списка, но НЕ освобождает динамически размещенный объект. (Объект является динамически создаваемым классом). Очень странно! Я переключился с Qlist на QLinkedList, но результаты те же. Помните, мой QLinkedList - это QLinkedList, а не 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 является потерянным блоком!

Я подозреваю, что это как-то связано со стиранием внутри цикла с помощью итератора... но я не могу с этим разобраться!


См. Подробное решение ниже... проблема заключалась в том, что функция добавления создает копию и добавляет ее в список... Я думал, что она добавляла реальный объект (а не копию)... поэтому проблема заключалась в "новой" копии была утечка.

3 ответа

Решение

Вы пропускаете экземпляр, на который указывает 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.

  • Если вы храните QSharedPointer или же std::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;
}
qDeleteAll(list.begin(), list.end());

Это зависит от ваших объектов. В Qt есть понятие владения объектами, которое организует объекты в деревьях. Родитель дерева удаляет все его дочерние элементы, как только выходит из области видимости.

Если ваши объекты не управляются родительскими объектами, вы должны убедиться, что их освободили самостоятельно. Qt также поставляется с набором умных указателей, которые упрощают это.

Другие вопросы по тегам