Деревья объектов и владение
Обзор
QObjects организуются в деревья объектов. Когда вы создаёте QObject с другим объектом в качестве родителя, он добавляется в список children() родительского объекта и удаляется при удалении родителя. Этот подход отлично подходит для GUI-объектов. Например, QShortcut (горячая клавиша) является дочерним объектом соответствующего окна, поэтому при закрытии окна горячая клавиша также удаляется.
QQuickItem, базовый визуальный элемент модуля Qt Quick, наследуется от QObject, но имеет понятие визуального родителя, отличающееся от понятия родителя QObject. Визуальный родитель элемента необязательно совпадает с его родительским объектом. Более подробная информация представлена в разделе Концепции — Визуальный родитель в Qt Quick.
QWidget, базовый класс модуля Qt Widgets, расширяет отношения родитель-потомок. Дочерний объект обычно также становится дочерним виджетом, т. е. он отображается в системе координат родителя и графически ограничен границами родителя. Например, когда приложение удаляет сообщение об ошибке после его закрытия, кнопки и метка сообщения также удаляются, как и ожидалось, поскольку кнопки и метка являются дочерними объектами сообщения об ошибке.
Вы также можете самостоятельно удалить дочерние объекты, и они удалят себя из своих родителей. Например, когда пользователь удаляет панель инструментов, это может привести к удалению приложения одного из объектов QToolBar, в этом случае родитель QMainWindow панели инструментов обнаружит изменение и соответствующим образом переконфигурирует своё экранное пространство.
Функции отладки QObject::dumpObjectTree() и QObject::dumpObjectInfo() часто полезны, когда приложение выглядит или ведёт себя необычно.
Порядок создания/удаления QObjects
Когда QObjects создаются в куче (т. е. создаются с помощью new), дерево может быть построено из них в любом порядке, и позже объекты в дереве могут быть удалены в любом порядке. При удалении любого QObject в дереве, если у объекта есть родитель, деструктор автоматически удаляет объект из его родителя. Если у объекта есть дочерние объекты, деструктор автоматически удаляет каждый дочерний объект. Ни один QObject не удаляется дважды, независимо от порядка удаления.
Когда QObjects создаются на стеке, применяется то же поведение. Обычно порядок удаления всё же не представляет проблемы. Рассмотрим следующий фрагмент:
int main()
{
QWidget window;
QPushButton quit("Quit", &window);
...
} Родитель, window, и дочерний объект, quit, оба являются QObjects, поскольку QPushButton наследует QWidget, а QWidget наследует QObject. Этот код корректен: деструктор quit не вызывается дважды, поскольку стандарт языка C++ (ISO/IEC 14882:2003) указывает, что деструкторы локальных объектов вызываются в обратном порядке их конструкторов. Таким образом, деструктор дочернего объекта, quit, вызывается первым, и он удаляет себя из своего родителя, window, перед вызовом деструктора window.
Но теперь рассмотрим, что произойдёт, если мы поменяем порядок создания, как показано во втором фрагменте:
int main()
{
QPushButton quit("Quit");
QWidget window;
quit.setParent(&window);
...
} В этом случае порядок удаления вызывает проблему. Деструктор родителя вызывается первым, потому что он был создан последним. Затем он вызывает деструктор своего дочернего объекта, quit, что неверно, поскольку quit является локальной переменной. Когда quit выходит из области видимости, его деструктор вызывается снова, на этот раз правильно, но ущерб уже нанесён.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.0/objecttrees.html