对象树 & 所有权
概述
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 在芬兰和/或其他国家/地区的 商标。所有其他商标均为其各自所有者的财产。