一、概述
对象以对象树的方式组织自己。当创建一个以另一个对象为父对象的QObject时,它将被添加到父对象的children()列表中,并在父对象被删除时跟随着被删除。事实证明,这种方法非常适合GUI对象的需要。
QQuickItem是Qt Quick模块的基本视觉元素,它继承自QObject,但是却有一个与QObject父元素不同的视觉父元素的概念。项目的视觉父级可能不一定与其对象父级相同。
QWidget是Qt小部件模块的基本类,它扩展了父子关系。子对象通常也会成为子控件,即它显示在其父控件的坐标系中,并由其父控件的边界以图形方式剪裁。
函数QObject::dumpObjectTree()(打印对象树信息)和QObject::dumpObjectInfo()(打印信号信息)在应用程序外观或行为异常时通常很有用。
二、对象的构造\销毁顺序
当在堆上创建QObject时,可以按任何顺序从它们构造树,然后可以按任何顺序销毁树中的对象。当树中的任何QObject被删除时,如果该对象有父对象,析构函数将自动从其父对象中删除该对象。如果对象有子对象,析构函数会自动删除每个子对象。无论销毁顺序如何,都不会两次删除任何QObject。
当在栈上创建QObject时,同样的行为也适用。通常情况下,销毁顺序仍然不存在问题。以下代码段:
int main()
{
QWidget window;
QPushButton quit("Quit", &window);
...
}
父窗口和子窗口quit都是QObject,因为QPushButton继承QWidget,而QWidget继承QObject。此代码是正确的:退出的析构函数不被调用两次,因为C++局部变量调用析构函数的次序正好与调用构造函数的次序相反:最先被调用的构造函数,其对应的(同一对象中的)析构函数最后被调用,而最后被调用的构造函数,其对应的析构函数最先被调用。因此,首先调用quit对象的析构函数,在quit的析构函数中,它将自己从父函数window中移除。
但是现在考虑一下如果交换构造顺序会发生什么,如第二个片段所示:
int main()
{
QPushButton quit("Quit");
QWidget window;
quit.setParent(&window);
...
}
在这种情况下,销毁顺序会引起问题。首先调用父级的析构函数,因为它是最后创建的。然后在父对象中会调用它的子对象quit的析构函数,因为quit是一个局部变量。当quit随后超出范围时,它的析构函数被再次调用。