Qt 对象树与继承关系

it2025-10-13  1

Qt 对象树与继承关系

QObject对象树QObject的构建/销毁顺序


QObject对象树

QObject将自己组织在对象树中。当您创建一个以另一个对象作为父对象的QObject时,它将被添加到父对象的children()列表中。因为 QObject 类就有一个私有变量 QList<QObject *>,专门存储这个类的子对象们。

typedef QList<QObject*> QObjectList;

比如创建一个 QObject 并指定父对象时,就会把自己加入到父对象的子对象 children() 列表中,也就是 QList<QObject *> 变量中。并在父对象被删除时被删除。事实证明,这种方法非常适合GUI对象的需求。例如,QLabel(标签)是相关窗口的子级,因此,当用户关闭该窗口时,快捷方式也会被删除。

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

//由于跟派生的主窗口类名相同,所以使用Ui命名空间区分开来。 //ui文件有uic.exe自动生成ui_mainwindow.h文件 QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { //moc元对象宏,使得对象继承信号槽等机制。 //moc.exe会把该头文件自动转换为标准的c++ Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: //此处的ui指针不指向任何父对象 Ui::MainWindow *ui; }; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) //此处new 了一个Ui::MainWindow对象。 , ui(new Ui::MainWindow) { //组装界面,this是该窗口指针 //将ui内定义的所有控件指针指向父窗口,而不是UI::MainWinow *ui; //使得父窗口析构之前,先释放子对象(控件) //setupUi(this)是由.ui文件生成的类的构造函数, //这个函数的作用是对界面进行初始化,它按照我们在Qt设计器里设计的样子把窗体画出来, //把我们在Qt设计器里面定义的信号和槽建立起来。 //也可以说,setupUi是我们画界面和写程序之间的桥梁。 ui->setupUi(this); } MainWindow::~MainWindow() { //ui指针未指向任何父类,所以需要单独释放回收。 //控件并非跟随ui释放 delete ui; }

QWidget是Qt Widgets模块的基本类,它扩展了父子关系。通常,子级也将成为子级小部件,即,它在其父级的坐标系中显示,并通过其父级的边界进行图形裁剪。例如,当应用程序在关闭后删除消息框时,消息框的按钮和标签也会被删除,就像我们想要的那样,因为按钮和标签是消息框的子级。Qt 运用对象树模式,当父对象被析构时,子对象自动就 delete 掉了,不用再写一大堆的代码了。

您也可以自己删除子对象,它们将从自己的父对象中删除。例如,当用户delete删除工具栏时,它可能导致应用程序删除其QToolBar对象之一,在这种情况下,工具栏的QMainWindow父级将检测到更改并相应地重新配置其屏幕空间。

当应用程序外观或行为异常时,调试功能QObject :: dumpObjectTree()和QObject :: dumpObjectInfo()通常很有用。

QObject的构建/销毁顺序

当在堆上创建QObject时(即,用new创建),可以以任何顺序从它们构造树,然后,可以以任何顺序销毁树中的对象。删除树中的任何QObject时,如果该对象具有父对象,则析构函数会自动从其父对象中删除该对象。如果对象有子对象,则析构函数会自动删除每个子对象。没有QObject中被删除两次,不管破坏的秩序。 正常情况下,最后被创建出来的会先被析构掉,用代码来演示一下:

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

父窗口和子窗口quit都是QObject,因为QPushButton继承QWidget, QWidget继承QObject。这段代码是正确的:quit的析构函数不会被调用两次,因为c++语言标准(ISO/IEC 14882:2003)规定本地对象的析构函数被调用的顺序与其构造函数相反。因此,子程序quit的析构函数首先被调用,它在调用window的析构函数之前从父程序window中删除自己。 但是现在考虑一下,如果我们交换构造的顺序会发生什么,如下面的第二个片段所示:

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

由于 window 的创建在quit对象之后,程序退出时先调用 window 的析构函数,此时由于 quit 是window的子对象,所以第一次被析构。接着程序再一次调用 quit 的析构函数,这是quit 的第二次被析构,这时由于被两次析构,已经找不到指针,程序崩溃。

这种特殊情况在编程中很隐蔽,不容易发现。因为编译的时候不会报错,只有运行时才会产生问题。所以我们要保持良好的编程习惯以及对事物产生顺序有科学的认知。

最新回复(0)