问题
I just provided an answer for this question and wanted to provide a working example when I noticed that newly created QMimeData
instance returned by QListModel::mimeData()
won't get deleted until the application gets terminated.
So this is not a real memory leak since Qt handles all QMimeData
instances on shutdown but you only have to drag&drop long enough and put the right content into your mime data to let the memory run full.
Did I miss something? Is there a way to tell Qt to delete the QMimeData
instances as soon as they are not needed any more?
Please note:
I know that every instance of QMimeData
gets deleted by Qt automatically on program termination.
My problem here is not a real memory leak as reported by valgrind
or cppcheck
but it looks like the multiple and potentially very large QMimeData
instances get not cleaned up at runtime which blows up memory consumption as well.
Example Code:
#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();
}
Example output looks like this:
0xa58750
0xa4e0f0
~0xa4e0f0
0xa3c6c0
~0xa3c6c0
0xa51880
0xa5ecd0
0xa31f50
0xa57db0
0xa5afc0
~0xa5afc0
0xa5aa70
~0xa5aa70
------ CLOSE WINDOW
~0xa58750
~0xa51880
~0xa5ecd0
~0xa31f50
~0xa57db0
The destructors get called before closing the application only when the drop get's accepted.
Btw. I'm on a home-grown Qt 5.6 @1fcdb6cafcf - on one computer and on 5.6.0-19.fc23 Fedora 23 precompiled on another. So I doubt it's just a temporary development state.
回答1:
There is a memory leak only if you forget to delete the pointer returned by mimeData()
. You have to manage the ownership as with any pointer.
For instance, if you pass the mimeData()
returned pointer to a QDrag
object using setMimeData()
, he will take ownership of it, and will take care of deleting it at the end of the drag operation. There is no memory leak in this case.
See : http://doc.qt.io/qt-5/qdrag.html#setMimeData
回答2:
It should be happening, if it isn't, then it's a bug - there's nothing you can do about it, other than reporting it if it hasn't been reported yet.
I can't reproduce it on OS X with Qt 5.6. It may be platform-specific, or an already fixed bug in an old Qt version. As soon as I release the mouse button at the end of the drag, the mime data gets deleted. The call stack when the destructor executes has the QDrag
destructor called from the event loop via deleteLater
somewhere. I've used your code verbatim.
Sidebar: It could be made just a bit more minimal, if you really wanted it as short as possible. Here's what I got, although I agree it's mostly splitting hair. Your question beats 99% other questions by providing a working example - big kudos for that! The things that I think matter for brevity are:
- Inclusion of the entire needed Qt module(s).
- Use of
qDebug
instead ofstd::cout
& al, it saves an include and is more Qt style. - Access specifiers don't matter if you use
struct
anyway. - Generally speaking, destructors are either virtual in the public base class, or they can never be without succumbing to slicing. So you don't have to spell that out.
I have example code where I don't follow this, of course. I like to call it legacy code :)
#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();
}
来源:https://stackoverflow.com/questions/37846843/memory-leak-in-qt5-how-to-get-qmimedata-deleted