Утечка памяти в Qt5? Как удалить QMimeData?

Я только что предоставил ответ на этот вопрос и хотел предоставить рабочий пример, когда заметил, что вновь созданный экземпляр QMimeData возвращается QListModel::mimeData() не будет удален, пока приложение не будет завершено.

Таким образом, это не настоящая утечка памяти, поскольку Qt обрабатывает все экземпляры QMimeData при выключении, но вам нужно только перетаскивать и вставлять нужное содержимое в ваши MIME-данные, чтобы память заполнялась.

Я что-то пропустил? Есть ли способ сказать Qt удалить экземпляры QMimeData, как только они больше не нужны?

Обратите внимание:

Я знаю, что каждый экземпляр QMimeData автоматически удаляется Qt при завершении программы. Моя проблема здесь не в настоящей утечке памяти, о которой сообщают valgrind или cppcheck, но похоже, что множественные и потенциально очень большие экземпляры QMimeData не очищаются во время выполнения, что также увеличивает потребление памяти.

Пример кода:

#include <QtWidgets>
#include <iostream>

struct TrackedMimeData : public QMimeData {
   TrackedMimeData(const QString & text) {
      std::cout << this << std::endl;
      setText(text);
   }
   ~TrackedMimeData() {
       std::cout << "~" << this << std::endl;
   }
};

struct MyListWidget : QListWidget {
   MyListWidget() {
      setDragEnabled(true);
      addItem("item1");
      addItem("item2");
   }
   QMimeData * mimeData(const QList<QListWidgetItem *>) const override {
      return new TrackedMimeData("hello");
   }
};

int main(int argsc, char *argsv[]) {
   QApplication application(argsc, argsv);
   MyListWidget gui;
   gui.show();
   return application.exec();
}

Пример вывода выглядит следующим образом:

0xa58750
0xa4e0f0
~0xa4e0f0
0xa3c6c0
~0xa3c6c0
0xa51880
0xa5ecd0
0xa31f50
0xa57db0
0xa5afc0
~0xa5afc0
0xa5aa70
~0xa5aa70
------ CLOSE WINDOW
~0xa58750
~0xa51880
~0xa5ecd0
~0xa31f50
~0xa57db0

Деструкторы вызываются перед закрытием приложения только после принятия удаления.

Кстати. Я использую доморощенный Qt 5.6 @1fcdb6cafcf — на одном компьютере и на 5.6.0-19.fc23 Fedora 23, предварительно скомпилированном на другом. Так что я сомневаюсь, что это просто временное состояние развития.


person frans    schedule 15.06.2016    source источник
comment
QMimeData наследует QObject и хранит свои данные внутри как QVector строк и QVariant пар. У него такие же ограничения на управление памятью, как и у любого другого класса, производного от QObject.   -  person anonymous    schedule 16.06.2016
comment
cppcheck ничего не найдет в этом примере, потому что экземпляр QMimeData в конце удаляется. Но до тех пор я мог бы хранить гигабайты данных в тысячах экземпляров новых QMimeData экземпляров, что, я признаю, не является классической утечкой памяти.   -  person frans    schedule 16.06.2016
comment
Вы действительно наблюдаете чрезмерное потребление памяти или просто обеспокоены тем, что это может произойти?   -  person anonymous    schedule 16.06.2016
comment
И вот, дамы и господа, как вы пожинаете заслуженную сладкую, сладкую карму за то, что у вас есть четкий вопрос с самодостаточным тестовым случаем. Должно быть тематическое исследование для всех, чьи вопросы были отклонены и / или закрыты. Я полагаю, что если стоит комментировать недостатки, стоит также комментировать и достоинства, чтобы подкрепить хорошее поведение. Отличная работа.   -  person Kuba hasn't forgotten Monica    schedule 16.06.2016
comment
@Jon: я почти уверен, что есть потеря памяти, но в первую очередь я хотел отслеживать время жизни экземпляра QMimeData или QDrag, чтобы знать, когда операция перетаскивания прерывается. В настоящее время я не могу узнать, например. когда скрыть некоторые виджеты по требованию.   -  person frans    schedule 16.06.2016
comment
Утечка возможна только в том случае, если вы не передадите QMimeData соответствующему обработчику. Это похоже на случай потенциальной утечки, если вновь созданный объект QMimeData не получает родителя от создавшего его объекта (или если вы не удаляете его напрямую).   -  person anonymous    schedule 17.06.2016


Ответы (2)


Утечка памяти происходит только в том случае, если вы забываете удалить указатель, возвращаемый mimeData(). Вы должны управлять владением, как и с любым указателем.

Например, если вы передадите возвращаемый указатель mimeData() объекту QDrag с помощью setMimeData(), он станет владельцем объекта и позаботится об его удалении в конце операции перетаскивания. В этом случае утечки памяти нет.

См.: http://doc.qt.io/qt-5/qdrag.html#setMimeData

person galinette    schedule 16.06.2016
comment
Я использую QListWidget, который является производным от QAbstractItemView и создает/обрабатывает объект QDrag. Я создаю только QMimeData, который затем становится владельцем объекта QDrag, к которому у меня нет доступа. - person frans; 16.06.2016
comment
Да, но в документации сказано, что QDrag::setMimeData передает право собственности объекту QDrag. Это означает, что объект QMimeData будет удален при удалении QDrag. - person galinette; 16.06.2016
comment
Хорошо, но тогда объект QDrag не удаляется вовремя - я исправлю эту деталь в своем вопросе, но проблема та же. - person frans; 16.06.2016
comment
Как я уже сказал в своем комментарии, я не создаю QDrag вручную - QListWidget делает это за меня. Я добавлю рабочий пример как можно скорее! - person frans; 16.06.2016
comment
Если вы позволите виджету обрабатывать QDrag, в документации указано, что он будет уничтожен вовремя. Вы действительно наблюдаете фактическую утечку памяти, связанную с QDrag? - person galinette; 16.06.2016
comment
Я добавил пример, который воспроизводит описанное поведение. Я не уверен, что это утечка памяти, но я не знаю, почему этого не должно быть. - person frans; 16.06.2016
comment
Объекты QDrag внутренне управляются частным одноэлементным классом QDragManager. Таким образом, никакой утечки. - person anonymous; 17.06.2016
comment
Это не очень полезное определение утечки. Я могу запустить/прервать столько операций перетаскивания, сколько захочу, а базовые экземпляры QDrag и QMimeData никогда не будут очищены за время существования программы, несмотря на то, что они больше не используются. Для меня это утечка. - person frans; 17.06.2016

Это должно произойти, если это не так, то это ошибка - вы ничего не можете с этим поделать, кроме как сообщить об этом, если об этом еще не сообщалось.

Я не могу воспроизвести его на OS X с Qt 5.6. Это может быть связано с платформой или уже исправленной ошибкой в ​​старой версии Qt. Как только я отпускаю кнопку мыши в конце перетаскивания, данные пантомимы удаляются. В стеке вызовов при выполнении деструктора деструктор QDrag вызывается из цикла событий через deleteLater где-то. Я использовал ваш код дословно.

Боковая панель: ее можно было бы сделать немного более минималистичной, если бы вы действительно хотели, чтобы она была как можно короче. Вот что я получил, хотя я согласен, что это в основном расщепление волос. Ваш вопрос превосходит 99 % других вопросов, поскольку предоставляет работающий пример — большое спасибо за это! Вещи, которые я думаю, имеют значение для краткости:

  1. Включение всех необходимых модулей Qt.
  2. Использование qDebug вместо std::cout и др., это экономит включение и больше похоже на стиль Qt.
  3. Спецификаторы доступа не имеют значения, если вы все равно используете struct.
  4. Вообще говоря, деструкторы либо виртуальны в общедоступном базовом классе, либо никогда не могут быть таковыми, не поддаваясь нарезке. Так что вам не нужно расшифровывать это.

У меня есть пример кода, где я, конечно, этого не делаю. Мне нравится называть это устаревшим кодом :)

#include <QtWidgets>

struct TrackedMimeData : QMimeData {
   TrackedMimeData(const QString & text) {
      qDebug() << this;
      setText(text);
   }
   ~TrackedMimeData() {
      qDebug() << "~" << this;
   }
};

struct MyListWidget : QListWidget {
   MyListWidget() {
      setDragEnabled(true);
      addItem("item1");
      addItem("item2");
   }
   QMimeData * mimeData(const QList<QListWidgetItem *>) const override {
      return new TrackedMimeData("hello");
   }
};

int main(int argsc, char *argsv[]) {
   QApplication application(argsc, argsv);
   MyListWidget gui;
   gui.show();
   return application.exec();
}
person Kuba hasn't forgotten Monica    schedule 16.06.2016
comment
к сожалению, я не вижу никакого вывода из qDebug - ни на терминале, ни в QtCreator. Но вывод с cout доказывает, что деструктор не вызывается (в моем случае) - person frans; 16.06.2016
comment
@frans Странно ... На какой платформе и версии Qt это? Это определенно ошибка. Пожалуйста, сообщите, если еще не сообщили. - person Kuba hasn't forgotten Monica; 16.06.2016