Владение предметом контейнера Qt? - PullRequest
2 голосов
/ 24 февраля 2020

Кто владеет элементами в коллекции Qt, такими как QList или QContiguousCache? Кроме того, коллекции Qt, кажется, используют ссылку вместо указателя, внутренне клонирует объект, используя конструктор копирования, или мне нужно убедиться, что объект не уничтожен?

Я получаю странное поведение с QContiguousCache и я не могу найти информацию о владельце где-либо. Если это помогает, я пытаюсь поместить некоторую QString в QContiguousCache из одной функции и получить к ним доступ в другой. В этом случае я постоянно получаю нулевые указатели.

1 Ответ

1 голос
/ 25 февраля 2020

Контейнеры 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).

Snapshot of testQListOwnership

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 , я произвел некоторые утечки памяти.

Snapshot of testQListOwnership (after clicking on button “Remove Labels”)

Вывод после нажатия на × :


… на словах: ничего.

Память об утечке Label экземпляров, безусловно, освобождается ОС (как и все остальное), но надлежащего уничтожения больше не происходит.


Дополнительные сведения (найдено при поиске в Интернете):

Понимать контейнеры Qt

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...