本页内容

对象树与所有权

概述

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

QQuickItem作为Qt Quick 模块的基本视觉元素,它继承自QObject ,但其“视觉父对象”的概念与QObject 父对象有所不同。项目的视觉父对象未必与其对象父对象相同。更多详情请参阅 Qt Quick 中的“概念 - 视觉父对象”。

QWidget,作为Qt Widgets 模块的基础类,扩展了父子关系。子项通常也会成为子控件,即它显示在父项的坐标系中,并在图形上受到父项边界的裁剪。 例如,当应用程序在消息框关闭后将其删除时,消息框中的按钮和标签也会被删除,这正是我们所期望的,因为这些按钮和标签是消息框的子控件。

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

当应用程序的外观或行为异常时,调试函数QObject::dumpObjectTree()和QObject::dumpObjectInfo()通常非常有用。

QObject 的构造/销毁顺序

当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 随后超出作用域时,其析构函数会被再次调用——这次调用是正确的——但损害已经造成。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.