Object Trees & Ownership

概述

Qt将QObjects 组织成对象树. 当你以一个 QObject 对象为父创建QObject时, Qt将新创建的对象加入父对象的 children() 链表, 并在父对象销毁时销毁所有子对象. 事实证明, 这种方式很适合GUI对象需求. 例如, QShortcut (键盘快捷键) 是窗口的子对象, 因此, 当用户关闭窗口时, 快捷方式也会被删除.

QQuickItem是Qt Quick模块的基本视觉元素, 它继承自 QObject, 但是其视觉父对象的概念与 QObject 父对象的概念不同. 项的视觉父对象与QObject父对象不一定相同. 详见 Concepts - Visual Parent in Qt Quick.

QWidget是Qt Widgets模块的基础类, 它扩展父子关系. 通常, 子级对象也是子widget, 即.子widget会显示在父级的坐标系, 并通过父widget的边界裁剪. 例如, 应用程序在消息框关闭后, 删除消息框, 消息框的按钮和标签也会删除. 正如所想, 按钮和标签是消息框的子对象.

你也可以删除子对象, 它们将从父对象的子链表中删除. 例如, 用户删除工具栏时, 应用程序会删除 QToolBar , 这种情况下, 工具栏的父对象 QMainWindow 将检测到这种变更, 并重新计算屏幕空间.

应用程序行为或功能异常时, 调试函数 QObject::dumpObjectTree() 和 QObject::dumpObjectInfo() 是非常有用.

构建/销毁QOject顺序

在堆上创建 QObjects (i.e., new)时, 你可以以任意顺序构造它们. 之后, 你可以以任意顺序销毁树中对象. 当树中 QObject 对象删除时, 如果对象有父对象, 析构函数自动从父对象删除自身. 如果对象有子对象, 析构函数自动删除所有子对象. 不管销毁顺序如何, QObject 对象都不会删除2次.

在栈上创建 QObjects 时, 行为类似. 一般而言, 销毁顺序都不会出现问题. 思考下列代码:


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

父对象(window)和子对象(quit)都继承自QWidget, QWidget继承自QObjects. 这段代码是正确的: quit对象不会调两次, 因为C++语言标准 (ISO/IEC 14882:2003) 规定析构函数的调用顺序与构造函数的调用顺序相反. 因此, 子对象(quit)的析构函数先调用, 它会在父对象(window)析构之前删除自身.

但是我们思考一下, 如果交换这两个对象的构造顺序会发生什么, 如下所示:


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

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

这种情况下, 析构函数的顺序造成一个问题. 由于父对象(window)最后创建, 它的析构函数先调用. 它会调用子对象(quit)的析构函数. 这是不对的, 因为quit是一个局部变量, 之后quit超出范围, 析构函数再次调用.