Reputation: 423
Who owns the items in a Qt collection such as QList or QContiguousCache? Also, Qt collections seem to take a reference instead of a pointer, does it internally clone the object using copy constructor or do I need to make sure the object is not destroyed?
I've been getting weird behavior with QContiguousCache and I can't seem to find the ownership information anywhere. If it helps, I'm trying to put some QString into the QContiguousCache from within one function and access them in another. I'm constantly getting null pointers in this case.
Upvotes: 2
Views: 511
Reputation: 20141
Qt containers are comparable to std
containers – they just store values (which might be QObject*
or QWidget*
) but they don't take ownership in the sense of how Qt manages ownership (e.g. for child widgets of widgets).
That said, I had a look into the doc. of QList to understrike this:
A common requirement is to remove an item from a list and do something with it. For this, QList provides takeAt(), takeFirst(), and takeLast(). Here's a loop that removes the items from a list one at a time and calls delete on them:
QList<QWidget *> list; ... while (!list.isEmpty()) delete list.takeFirst();
Though, mentioning takeAt()
made me uncertain for a second. takeAt()
sounds like “stealing ownership”. Could I be wrong?
Hence, I made a small sample to illustrate the issue:
For this, I made a thin-wrapper Label
for QLabel
to report construction and destruction.
testQListOwnership.cc
:
// Qt header:
#include <QtWidgets>
struct Label: public QLabel {
Label(const QString &text): QLabel(text)
{
qDebug() << "Label::Label(" << text << ")";
}
virtual ~Label()
{
qDebug() << "Label::~Label(" << text() << ")";
}
Label(const Label&) = delete;
Label& operator=(const Label&) = delete;
};
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("Test QList Ownership");
QVBoxLayout qVBox;
QList<QLabel*> pQLbls;
for (int i = 1; i <= 3; ++i) {
QLabel *pQLbl = new Label(QString("Label ") + QString().number(i));
qDebug() << "pQLbls.append(pQLbl);";
pQLbls.append(pQLbl);
qDebug() << "Label(" << pQLbl->text() << ").parent():" << pQLbl->parent();
qDebug() << "qVBox.addWidget(pQLbl);";
qVBox.addWidget(pQLbl);
qDebug() << "Label(" << pQLbl->text() << ").parent():" << pQLbl->parent();
}
qDebug() << "&qWinMain:" << &qWinMain;
qDebug() << "qWinMain.setLayout(&qVBox);";
qWinMain.setLayout(&qVBox);
for (QLabel *pQLbl : pQLbls) {
qDebug() << "Label(" << pQLbl->text() << ").parent():" << pQLbl->parent();
}
QPushButton qBtnRemoveLabels("Remove Labels");
qVBox.addWidget(&qBtnRemoveLabels);
qWinMain.show();
// install signal handlers
QObject::connect(&qBtnRemoveLabels, &QPushButton::clicked,
[&]() {
for (QLabel *pQLbl : pQLbls) {
qDebug() << "pQLbl->setParent(nullptr);";
pQLbl->setParent(nullptr);
qDebug() << "Label(" << pQLbl->text() << ").parent():" << pQLbl->parent();
}
qDebug() << "pQLbls.clear();";
pQLbls.clear();
});
// runtime loop
return app.exec();
}
and the Qt project to build testQListOwnership.pro
:
SOURCES = testQListOwnership.cc
QT += widgets
Output:
Qt Version: 5.13.0
Label::Label( "Label 1" )
pQLbls.append(pQLbl);
Label( "Label 1" ).parent(): QObject(0x0)
qVBox.addWidget(pQLbl);
Label( "Label 1" ).parent(): QObject(0x0)
Label::Label( "Label 2" )
pQLbls.append(pQLbl);
Label( "Label 2" ).parent(): QObject(0x0)
qVBox.addWidget(pQLbl);
Label( "Label 2" ).parent(): QObject(0x0)
Label::Label( "Label 3" )
pQLbls.append(pQLbl);
Label( "Label 3" ).parent(): QObject(0x0)
qVBox.addWidget(pQLbl);
Label( "Label 3" ).parent(): QObject(0x0)
&qWinMain: QWidget(0x2332b5f818)
qWinMain.setLayout(&qVBox);
Label( "Label 1" ).parent(): QWidget(0x2332b5f818)
Label( "Label 2" ).parent(): QWidget(0x2332b5f818)
Label( "Label 3" ).parent(): QWidget(0x2332b5f818)
QWindowsWindow::setGeometry: Unable to set geometry 102x102+960+460 on QWidgetWindow/'QWidgetClassWindow'. Resulting geometry: 120x102+960+460 (frame: 8, 31, 8, 8, custom margin: 0, 0, 0, 0, minimum size: 102x102, maximum size: 16777215x16777215).
The QList<QLabel*> pQLbls
doesn't take ownership.
Even the QVBoxLayout qVBox
doesn't take ownership.
Ownership is taken by QWidget qWinMain
after the qVBox
is set as layout of it.
Output after clicking on ×:
Label::~Label( "Label 1" )
Label::~Label( "Label 2" )
Label::~Label( "Label 3" )
The QWidget qWinMain
ensured that the Label
instances are deleted when it is destroyed itself.
Now, what happens when the Label
s are “stolen” from qWinMain
:
Output after clicking on Remove Labels:
pQLbl->setParent(nullptr);
Label( "Label 1" ).parent(): QObject(0x0)
pQLbl->setParent(nullptr);
Label( "Label 2" ).parent(): QObject(0x0)
pQLbl->setParent(nullptr);
Label( "Label 3" ).parent(): QObject(0x0)
pQLbls.clear();
Oops! By clicking on Remove Labels, I produced some memory leaks.
Output after clicking on ×:
… in words: nothing.
Memory of leaked Label
instances is surely freed by OS (like anything else) but no proper destruction happens anymore.
Further reading (found when searching the Web):
Upvotes: 1