警告

本节包含自动从C++转换为Python的片段,可能包含错误。

对象树与所有制#

关于Qt中用于描述对象所有制的父子模式的信息。

概述#

QObjects 组织 themselves in object trees. 当您使用另一个对象作为父对象创建一个 QObject 时,它将添加到父对象的 children() 列表,当父对象被删除时也会被删除。事实证明,这种方法非常适合GUI对象的需求。例如,一个 QShortcut(键盘快捷键)是相关窗口的子对象,因此当用户关闭该窗口时,快捷键也会被删除。

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

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

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

在应用程序看起来或表现异常时,调试函数 dumpObjectTree()dumpObjectInfo() 通常非常有用。

QObjects 的构造/析构顺序#

当在堆上创建 QObject 对象时(即使用 new 创建),可以从它们构建一个树,且可以按任意顺序创建,之后可以按任意顺序销毁树中的对象。当树中的任何 QObject 对象被删除时,如果该对象有父对象,析构函数会自动将该对象从其父对象中删除。如果对象有子对象,析构函数会自动删除每个子对象。不会重复删除任何一个 QObject 对象,无论销毁顺序如何。

当在栈上创建 QObject 对象时,也会有相同的行为。通常,消亡的顺序不会引起问题。考虑下面的片段:

if __name__ == "__main__":

    window = QWidget()
    quit = QPushButton("Quit", window)
    ...

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

但现在考虑如果我们交换构建顺序会发生什么,就像第二个片段中显示的那样

if __name__ == "__main__":

    quit = QPushButton("Quit")
    window = QWidget()
    quit.setParent(window)
    ...

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