qt类的构造与析构

一 说明

在qt代码中,我们经常给类的构造函数传入父指针,这个有什么用?类实例化后又是在什么时候释放的?
同样很多人担心,在构造函数中new对象,如果析构函数中不释放会不会造成内存泄漏?为什么我看很对按钮等控件new后都没有delete?所以我自己new的到底需不需要手动释放?

为了避免代码运行中出现内存泄漏,规范编程习惯,下面做一个验证。
其次也是对qt中对象树做一些了解,qt对象树学习文章:https://zhuanlan.zhihu.com/p/43523879

对象树在 GUI 编程中是非常非常有用的!当父对象被析构时子对象也会被析构,这样一定程度上简化了内存回收机制。

二 测试类设计

使用定时器打印信息,这样表示类实例化后是否被释放。
h文件

#ifndef TESTCLASS_H
#define TESTCLASS_H

#include <QObject>
#include <QTimer>

class TestClass : public QObject
{
    Q_OBJECT
public:
    explicit TestClass(QObject *parent = nullptr,QString info = "");

signals:

public slots:
private:
    QString myInfo;
};

#endif // TESTCLASS_H

cpp文件

#include "testclass.h"
#include <qDebug>

TestClass::TestClass(QObject *parent,QString info) :
    QObject(parent),
    myInfo(info)
{
    QTimer *time = new QTimer(this);
    connect(time,&QTimer::timeout,this,[=](){
        qDebug()<<Q_FUNC_INFO<<myInfo;
    });
    time->start(1000);
}

三 测试代码设计

使用mainwindows中加入一个按钮,使用按钮控制From类的创建与释放,然后再From类中使用各种方式创建TestClass类。

1 mainwindow类设计

h文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "form.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    Form *win;
};

#endif // MAINWINDOW_H

cpp文件

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    win = NULL;
}
MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::on_pushButton_clicked()
{
    if(win != NULL)
    {
        delete win;
        win = NULL;
    }
    else
    {
        win = new Form();
        win->show();
    }
}
2 From类测试的实现

h文件

#ifndef FORM_H
#define FORM_H

#include <QWidget>
#include "testclass.h"

namespace Ui {
class Form;
}

class Form : public QWidget
{
    Q_OBJECT

public:
    explicit Form(QWidget *parent = 0);
    ~Form();

private:
    Ui::Form *ui;
    TestClass c;
};

#endif // FORM_H

cpp文件

#include "form.h"
#include "ui_form.h"
Form::Form(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Form),
    c(NULL,"C")     //设置父对象无效,this对象释放的时候,c依据被释放
{
    ui->setupUi(this);

    TestClass *a = new TestClass(this,"A");         //等this对象(From)释放时释放
    TestClass *b = new TestClass(nullptr,"B");      //需要等进程关闭后,系统自动回收
    TestClass *e = new TestClass(parent,"E");       //需要等parent对象(Mainwindow)释放时释放
}
Form::~Form()
{
    delete ui;
}
四 测试结论
  1. new实例化,传入父类指针,随着父类指针释放的时候释放,子类前于父类执行析构函数。
  2. new实例化,传入父指针为空,即无父对象时,需要手动释放,即使用delete(一般放在析构函数中),否则需要等待进程关闭后系统回收(此情况为内存泄漏)。
  3. 类成员子类,实例化在构造的时候,释放的方式无法通过修改父对象修改,即使后期将父对象改为空。如:在From释放的时候,From的成员子类c,虽然后面改了c的父对象,在From对象释放的时候,c依据被释放。 原因分析: c作为成员,c的内存段在From内存范围内,在From释放后,其所有内存都要被释放,所以c也一定会被释放。

在From类代码的注释中也有体现,如果有兴趣的可以使用代码跑一下体验体验。

五 注意点

标准 C++ (ISO/IEC 14882:2003)要求,局部对象的析构顺序应该按照其创建顺序的相反过程。

如果函数内部执行如下代码会导致内存错误:

 QPushButton push("Quit");
 QWidget widget;
 
 push.setParent(&widget);

在上面的代码中,作为父对象的 widget会首先被析构,因为它是最后一个创建的对象。

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

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值