Qt—对象树与拥有权

x33g5p2x  于2022-03-28 转载在 其他  
字(2.6k)|赞(0)|评价(0)|浏览(238)

Qt中使用对象树(objecttree)来组织和管理所有的QObject类及其子类的对象。当创建一个QObject时,如果使用了其他的对象作为其父对象(parent),那么这个QObject就会被添加到父对象的children()列表中;当父对象被销毁时,这个QObject也会被销毁。实践表明,这个机制非常适合于管理GUI对象。
例如,一个QShortcut(键盘快捷键)对象是相应窗口的-一个子对象,当用户关闭这个窗口时,快捷键对象也会被销毁。

QWidget作为Qt Widgets模块的基础类,扩展了对象间的父子关系。

一个子对象一般也就是一个子部件,因为它们要显示在父部件的坐标系统之中。
例如,当关闭一个消息对话框(message box)后要销毁它时,消息对话框中的按钮和标签也会被销毁,这也正是我们所希望的,因为按钮和标签是消息对话框的子部件。

当然,也可以自己手动来销毁一个子对象,这时会将它们从其父对象中移除。这一部分的内容可以在帮助索引中通过Object Trees & Ownership关键字查看。

以前讲解第一个例子时曾经提到了使用new来创建一个部件,但是却没有使用delete来进行释放的问题。这里再来研究一下这个问题。

新建Qt Widgets应用,项目名称为myownership,基类选择QWidget,类名保持 Widget不变。完成后向项目中添加新文件,模板选择C++类,类名为MyButton,基类设置为QPushButton,添加完文件后将mybutton.h文件修改如下:

#ifndef MYBUTTON_H
#define MYBUTTON_H

#include <QPushButton>

class MyButton : public QPushButton
{
    Q_OBJECT
public:
    explicit MyButton(QWidget *parent = 0);
    ~MyButton();
};

#endif // MYBUTTON_H

这里主要添加了析构函数的声明

QT中的explicit关键字的作用

然后到mubutton.cpp文件中,修改如下:

#include "mybutton.h"
#include <QDebug>
MyButton::MyButton(QWidget *parent) :
    QPushButton(parent)
{
}

MyButton::~MyButton()
{
    qDebug() << "delete button";
}

这里添加了析构函数的定义,这样当MyButton的对象被销毁时,就会输出相应的信息。这里定义析构函数只是为了更清楚地看到部件的销毁过程,其实一般在构建新类时不需要实现析构函数。下面到widget.cpp文件中进行更改,添加头文件:

#include "mybutton.h"
#include <QDebug>
#include <QHBoxLayout>

在构造函数中添加代码:
创建按钮部件

MyButton *button = new MyButton(this);    // 创建按钮部件,指定widget为父部件
    button->setText(tr("button"));

更改析构函数:

Widget::~Widget()
{
    delete ui;
    qDebug() << "delete widget";
}

就多个输出了信息 “delete widget”

下面运行程序,下面会输出 “delete widget”
会销毁窗口

可以看到,当关闭窗口后,因为该窗口是顶层窗口,关闭后没有可以显示的窗口,所以应用程序要销毁该窗口部件(如果不是顶层窗口,那么关闭时只是隐藏,不会被销毁),**而当窗口部件销毁时会自动销毁其子部件。**这也就是在Qt中经常只看到new操作而看不到delete操作的原因。

再来看一下main.cpp 文件,其中 Widget对象是建立在栈上的:

Widget w;
    w.show();

这样对于对象w,在关闭程序时会被自动销毁。而对于Widget中的部件,如果是在堆上创建(使用new操作符),那么只要指定Widget为其父窗口(创建时指定parent,参数为this)就可以了,也不需要进行delete 操作。整个应用程序关闭时,会去销毁w对象,而此时又会自动销毁它的所有子部件,这些都是Qt的对象树所完成的。

所以,对于规范的Qt程序,我们要在main()函数中将主窗口部件创建在栈上,比如“Widget w;”而不要在堆上进行创建(使用new操作符)。对于其他窗口部件,可以使用new操作符在堆上进行创建,不过一.定要指定其父部件,这样就不需要再使用delete操作符来销毁该对象了。还有一种重定义父部件(reparented)的情况,例如,将一个包含其他部件的布局管理器应用到窗口上,那么该布局管理器和其中所有部件都会自动将它们的父部件转换为该窗口部件。

在widget.cpp文件中添加头文件# include < QHBoxLayout> ,然后
在构造函数中继续添加代码:

MyButton *button2 = new MyButton;
    MyButton *button3 = new MyButton;
    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(button2);
    layout->addWidget(button3);
    setLayout(layout);      // 在该窗口中使用布局管理器

这里创建了两个MyButton和一个水平布局管理器,但是并没有指定它们的父部件,现在各个部件的拥有权(ownership)不是很清楚。但是当使用布局管理器来管理这两个按钮,并且在窗口中使用这个布局管理器后,这两个按钮和水平布局管理器都将重定义自己的父部件为窗口Widget。可以使用children()函数来获取一个部件的所有子部件的列表,例如,在构造函数中再添加如下代码:

qDebug() << children();    // 输出所有子部件的列表

这时可以运行程序并查看应用程序输出栏中的信息,然后根据自己的想法更改一下程序,进一步体会Qt中对象树的概念。

相关文章