对象树 & 所有权

概述

QObjects 在对象树中进行组织。当你用一个对象作为父对象创建一个 QObject 时,它会被添加到父对象的 children() 列表中,并在父对象被删除时一同被删除。这表明这种方法非常适合 GUI 对象的需求。例如,一个 QShortcut(键盘快捷键)是相关窗口的子对象,因此当用户关闭该窗口时,快捷键也会被删除。

QQuickItem 是 Qt Quick 模块的基本视觉元素,它从 QObject 继承,但有一个不同的 视觉父对象 概念。一个项的视觉父对象可能不一定与其对象父对象相同。有关更多信息,请参阅 概念 - Qt Quick 中的视觉父对象

QWidget 是 Qt Widgets 模块的基本类,它扩展了父子关系。子对象通常也是一个子小部件,即在父对象的坐标系中显示,并由父对象的边界图形地裁剪。例如,当应用在消息框关闭后删除消息框,按钮和标签也会被删除,正如我们所期望的那样,因为按钮和标签是消息框的子对象。

您也可以自己删除子对象,它们会从父对象中删除自身。例如,当用户删除工具栏时,可能导致应用删除其 QToolBar 对象之一,在这种情况下,工具栏的 QMainWindow 父对象会检测到这种变化并相应地重新配置其屏幕空间。

当应用看起来或表现得很奇怪时,调试函数 QObject::dumpObjectTree() 和 QObject::dumpObjectInfo() 通常很有用。

QObjects 的构建/销毁顺序

QObjects 在堆上创建(即使用 new 创建)时,可以从它们构建一个树,其顺序可以是任意的,稍后,树中的对象可以以任意顺序被销毁。当树中任何 QObject 被删除时,如果对象有一个父对象,析构函数会自动将对象从父对象中删除。如果对象有子对象,析构函数会自动删除每个子对象。不管销毁顺序如何,没有对象会被删除两次。

QObjects 在栈上创建时,也会出现同样的行为。通常,销毁顺序并不成问题。考虑以下代码片段

int main()
{
    QWidget window;
    QPushButton quit("Quit", &window);
    ...
}

父对象 window 和子对象 quit 都是 QObjects,因为 QPushButton 继承自 QWidget,而 QWidget 继承自 QObject。此代码是正确的:由于 C++ 语言标准 (ISO/IEC 14882:2003) 规定局部对象的析构顺序与其构造顺序相反,因此 quit 的析构函数不会调用两次。因此,首先是调用子对象 quit 的析构函数,并在调用 window 的析构函数之前将其从其父对象中删除。

但现在考虑如果我们交换构造顺序会发生什么,如下第二个代码段所示

int main()
{
    QPushButton quit("Quit");
    QWidget window;

    quit.setParent(&window);
    ...
}

在这种情况下,析构顺序导致问题。由于它是最后一个创建的,因此首先调用父对象的析构函数。然后它调用其子对象 quit 的析构函数,这是不正确的,因为 quit 是一个局部变量。随后,当 quit 继续超出范围时,它的析构函数再次被调用,这次是正确的,但损害已经造成。

© 2024 The Qt Company Ltd. 本文档中包含的贡献归各自所有者所有。本文档的提供遵守免费软件基金会发布的 GNU 自由文档许可协议第 1.3 版 的条款。Qt及其相关徽标是 The Qt Company Ltd 在芬兰和/或其他国家/地区的 商标。所有其他商标均为其各自所有者的财产。