【问题标题】:Memory leak in Qt5? How to get QMimeData deleted?Qt5中的内存泄漏?如何删除 QMimeData?
【发布时间】:2016-10-17 05:48:27
【问题描述】:

我刚刚为this 问题提供了一个答案,并想提供一个工作示例,因为我注意到QListModel::mimeData() 返回的新创建的QMimeData 实例在应用程序终止之前不会被删除。

所以这不是真正的内存泄漏,因为 Qt 会在关闭时处理所有 QMimeData 实例,但您只需拖放足够长的时间并将正确的内容放入您的 mime 数据中以让内存运行已满。

我错过了什么吗?有没有办法告诉 Qt 在不再需要 QMimeData 实例时立即删除它们?

请注意:

我知道QMimeData 的每个实例都会在程序终止时被Qt 自动删除。 我的问题不是valgrindcppcheck 报告的真正的内存泄漏,但看起来多个且可能非常大的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

析构函数被调用之前仅当drop get被接受时关闭应用程序。

顺便说一句。我使用的是国产 Qt 5.6 @1fcdb6cafcf - 在一台计算机上和在另一台计算机上预编译的 5.6.0-19.fc23 Fedora 23 上。所以我怀疑这只是一个暂时的发展状态。

【问题讨论】:

  • QMimeData 继承 QObject 并在内部将其数据存储为 QVector 字符串和 QVariant 对。它具有与任何其他QObject 派生类体验相同的内存管理限制。
  • cppcheck 在此示例中找不到任何内容,因为 QMimeData 实例在最后被删除。但在那之前,我可能已经在数千个新的QMimeData 实例中存储了千兆字节的数据——我承认——这不是典型的内存泄漏。
  • 您是否真的看到过多的内存消耗,或者只是担心它可能会发生?
  • 这就是,女士们,先生们,您如何通过一个独立的测试用例获得一个当之无愧的甜蜜、甜蜜的业力。应该是每个问题被否决和/或关闭的人的案例研究。我认为如果值得评论缺点,也值得评论优点,以加强良好的行为。干得好。
  • @Jon:我很确定有内存丢失,但首先我想跟踪QMimeDataQDrag 实例的生命周期,以了解拖放操作何时中止。目前我无法知道例如何时隐藏一些“按需放置小部件”。

标签: c++ qt memory-leaks ownership


【解决方案1】:

只有当您忘记删除mimeData() 返回的指针时才会出现内存泄漏。您必须像使用任何指针一样管理所有权。

例如,如果您使用setMimeData()mimeData() 返回的指针传递给QDrag 对象,他将获得该对象的所有权,并在拖动操作结束时将其删除。在这种情况下没有内存泄漏。

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

【讨论】:

  • 我使用派生自QAbstractItemViewQListWidget 并创建/处理QDrag 对象。我只创建了QMimeData,然后它归我无权访问的QDrag 对象所有。
  • 是的,但是文档说 QDrag::setMimeData 将所有权传递给 QDrag 对象。这意味着当 QDrag 被删除时,QMimeData 对象将被删除
  • 好的,但是 QDrag 对象没有被及时删除 - 我会在我的问题中更正这个细节,但问题是一样的。
  • 正如我在评论中所说的 - 我不会手动创建 QDrag - QListWidget 为我这样做。我会尽快添加一个工作示例!
  • 这不是一个非常有用的泄漏定义。我可以根据需要启动/中止任意数量的拖动操作,并且底层的 QDragQMimeData 实例将永远在程序生命周期内被清理,尽管它们不再被使用。对我来说,这是一个泄漏。
【解决方案2】:

它应该会发生,如果没有发生,那么它就是一个错误 - 除了尚未报告的情况下报告它,您无能为力。

我无法在带有 Qt 5.6 的 OS X 上重现它。它可能是特定于平台的,或者是旧 Qt 版本中已经修复的错误。一旦我在拖动结束时松开鼠标按钮,mime 数据就会被删除。析构函数执行时的调用堆栈有QDrag 析构函数通过deleteLater 某处从事件循环调用。我已经逐字使用了你的代码。

侧边栏:如果您真的希望它尽可能短,它可以做得更小。这就是我得到的,虽然我同意它主要是分裂头发。通过提供一个工作示例,您的问题胜过 99% 的其他问题 - 非常感谢!为了简洁起见,我认为重要的是:

  1. 包含整个所需的 Qt 模块。
  2. 使用qDebug 代替std::cout & al,它节省了一个包含并且更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();
}

【讨论】:

  • 不幸的是,我没有看到来自 qDebug 的任何输出——无论是在终端上还是在 QtCreator 中。但是cout 的输出证明没有调用析构函数(在我的情况下)
  • @frans Weird... 那是什么平台和 Qt 版本?这绝对是一个错误。如果尚未报告,请报告。
猜你喜欢
  • 2015-05-30
  • 1970-01-01
  • 2011-09-17
  • 1970-01-01
  • 1970-01-01
  • 2010-11-22
  • 2015-10-14
  • 1970-01-01
  • 2019-10-06
相关资源
最近更新 更多