Qt对象树的销毁

一、问题

在C++中中,我们都知道:delete 和 new 必须配对使用(一 一对应):delete少了,则内存泄露。

为什么Qt使用new来创建一个控件,但是却没有使用delete来进行释放?

二、对象树

Qt中使用对象树来组织和管理所有的QObject类及其子类的对象。

当创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是 parent,也就是父对象指针。

每个 QObject 内部都有一个list,用来保存所有的 children,还有一个指针,保存自己的parent。当它自己被析构时,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。

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

三、对象树的实例

我们来分析一个Button对象的创建到销毁的过程。

创建一个MyPushButton对象,继承QPushButton

#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H

#include <QPushButton>

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

#endif // MYPUSHBUTTON_H
#include "mypushbutton.h"
#include<QDebug>

MyPushButton::MyPushButton(QWidget *parent) : QPushButton(parent)

{
    qDebug()<<"MyPushButton construct";
}

MyPushButton::~MyPushButton()

{
    qDebug()<<"MyPushButton destory";
}

main函数

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

创建mainWindow

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>


QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mypushbutton.h"

#include<QtDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    qDebug()<<"MainWindow construct";

    ui->setupUi(this);

    MyPushButton *mybtn=new MyPushButton(this);

    mybtn->resize(120,50);

    mybtn->setText("MyPushButton");

    mybtn->move(200,20);
}


MainWindow::~MainWindow()
{
    delete ui;
    qDebug()<<"MainWindow destory";
}

MainWindow类的析构函数中默认已经有了销毁ui的语句,这里又添加了输出语句。
当MainWindow窗口被销毁时,将输出信息。下面运行程序,然后关闭窗口,在Qt Creator
的应用程序输出栏中的输出信息为:

 可能有读者问这里为什么是MyPushButton后销毁,难道不应该先销毁MyPushButton,再销毁MainWindows吗?

因为这里与Qt的实现有关,堆栈信息如下,deleteChildren 在MainWindows的父类QWidget的析构函数中,C++是子类对象先析构,再进行父类对象的析构,而MyPushButton在QWidget的析构函数中调用,所以顺序与我们预想的不一致。

四、总结

Qt 引入对象树的概念,在一定程度上解决了内存问题,但是也存在风险。

比如下面这段代码,程序崩溃了!!!

{
    QPushButton quit("Quit");

    QWidget window;

    quit.setParent(&window);
}

 分析:作为父对象的 window 会首先被析构,因为它是最后一个创建的对象。在析构过程中,它会调用子对象列表中每一个对象的析构函数,也就是说, quit 此时就被析构了。然后,代码继续执行,在 window 析构之后,quit 也会被析构,因为 quit 也是一个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第二次调用 quit 的析构函数了,C++ 不允许调用两次析构函数,因此,程序崩溃了。

所以,对于规范的Qt程序,我们在main()函数中将主窗口部件创建在栈上,比如“Widget w;”,而不要在堆上进行创建(使用new操作符),避免内存泄漏。对于子窗口部件,可以使用new操作符在堆上进行创建,同时一定要指定其父部件(parent),这样就不需要再使用delete操作符来销毁该对象了。

参考:

QT对象树 - 安静点-- - 博客园

Qt对象树_不想在北京做开发的博客-CSDN博客_qt 对象树

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值