Контейнеры Qt сравнимы с std
контейнерами - они просто хранят значения (которые могут быть QObject*
или QWidget*
), но они не принимают владение в смысле того, как Qt управляет владением (например, для дочерних виджетов widgets).
Тем не менее, я посмотрел в do c. QList , чтобы подчеркнуть это:
Обычное требование - удалить элемент из списка и что-то с ним сделать. Для этого QList предоставляет takeAt (), takeFirst () и takeLast (). Вот al oop, который удаляет элементы из списка по одному и вызывает удаление для них:
QList<QWidget *> list;
...
while (!list.isEmpty())
delete list.takeFirst();
Хотя упоминание takeAt()
не давало мне уверенности на секунду. takeAt()
звучит как «кража собственности». Могу ли я ошибаться?
Поэтому я сделал небольшой пример, чтобы проиллюстрировать проблему:
Для этого я сделал тонкую оболочку Label
для QLabel
, чтобы сообщить о строительстве и разрушении. .
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();
}
и проект Qt для сборки testQListOwnership.pro
:
SOURCES = testQListOwnership.cc
QT += widgets
Вывод:
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).

QList<QLabel*> pQLbls
не вступает во владение.
Даже QVBoxLayout qVBox
не вступает во владение.
Право собственности принимается QWidget qWinMain
после того, как qVBox
установлено в качестве его макета.
Вывод после нажатия × :
Label::~Label( "Label 1" )
Label::~Label( "Label 2" )
Label::~Label( "Label 3" )
QWidget qWinMain
гарантирует, что Label
экземпляры удаляются, когда он сам уничтожается.
Теперь, что происходит, когда Label
s «украдены» из qWinMain
:
Вывод после нажатия на Удалить ярлыки :
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();
Упс! Нажав на Remove Labels , я произвел некоторые утечки памяти.

Вывод после нажатия на × :
… на словах: ничего.
Память об утечке Label
экземпляров, безусловно, освобождается ОС (как и все остальное), но надлежащего уничтожения больше не происходит.
Дополнительные сведения (найдено при поиске в Интернете):
Понимать контейнеры Qt