Qt6教程之三(11) 内存分配和管理

在Qt中,可以把对象分为两类,一类是QObject类及其派生类;另一类则是普通的C++类。
若是普通C++类则需要开发者自行管理,若是Qt的QObject类或其子类,那么Qt则拥有自己的内存回收管理机制,下面将进行简单介绍。

一 QObject的parent设置为NULL
如果对象在构造时直接指定了NULL,这说明当前实例无父对象存在,Qt也不能自动析构该实例,除非实例超出作用域导致析构函数被调用,然后使用deleteLater方法删除对象;
对于窗口基类QWidget,parent为NULL时代表其为一个顶层窗口,当parent不为NULL时他会显示在父widget中心区域的上层;如果QWidget的parent为NULL或是其他值,在其加入布局管理器或者QMainWindow设置widget时,会自动将parent设置为相应的父widget,在父控件销毁时这些子控件以及布局管理器对象会一并销毁。所有QWidget加入布局管理器或者QMainWindow设置widget时,就不需要手动释放其内存了,否则二次释放会崩溃。
对于在局部作用域上创建的父对象及其子对象,要注意对象销毁的顺序,因为父对象销毁时也会销毁子对象,当子对象会在父对象之后被销毁时会引发double free。

二 QObject的parent设置不为NULL
根据Qt的内存内存管理规则,QObject及其派生类的对象,如果其parent非0,那么其parent析构时会析构该对象。
Qt在管理对象时,会在底层依据对象的层级关系建议对象树,当该树上的某个分支节点或根节点对象使用完成后,Qt便会按照内存管理规则依次回收叶子节点、父节点对象所占用的内存,最后之直至回收到根节点对象。

设置窗口的Qt::WA_DeleteOnClose熟悉,当窗口销毁时,会全部回收与窗口相关的所有资源,包括启动的后台线程,这个熟悉在日常开发中,非常容易被忽略,这里提出来跟大家进行交流。

setAttribute(Qt::WA_DeleteOnClose); //当你想让关闭窗口的时候,让窗口销毁,在构造函数中设置Qt::WA_DeleteOnClose标志,对于单纯显示而不需要和父控件做交互的widget,直接设置DeleteOnClose即可,close时widget会被自动析构。

三 专用于容器内存回收的qDeleteAll和clear
qDeleteAll,专门用于指针容器,对容器或者迭代器中的每个对象进行delete操作,而不是从容器中移除对象。

四 Qt的智能指针
也可以使用QPointer、QScopedPointer、QSharedPointer和QWeakPointer进行内存管理。

五 对象引用计数管理内存
在对象被引用时,采用计数的方式进行管理,当对象被引用一次则计数加一,若解除引用则计数减一,直到最后计数为0则说明无任何引用,此时便可以放心回收内存了。

一般控件使用代码示例:

#include "mainwindow.h"
#include <QDoubleValidator>
#include <QLabel>
#include <QLineEdit>
#include<QPushButton>
#include<QVBoxLayout>


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(800,800);
    setWindowTitle("window point");

    QDoubleValidator* fValidator = new QDoubleValidator();
        QWidget * widget = new QWidget(this);
        QVBoxLayout * mainLayout = new QVBoxLayout(); //不指定父节点,当被其他控件包含时,Qt自动指定父节点。
        QHBoxLayout * horLayout = new QHBoxLayout();
        QVBoxLayout * vctLayout = new QVBoxLayout();
        //
        QLabel * _label = new QLabel("Please Enter Coordinate:");
        horLayout->addSpacing(20);
        horLayout->addWidget(_label);
        //
        QLabel * x_label = new QLabel("x:");
        QLabel * y_label = new QLabel("y:");
        QLabel * z_label = new QLabel("z:");
        QLineEdit * x_edit = new QLineEdit();
        x_edit->setValidator(fValidator);
        QLineEdit * y_edit = new QLineEdit();
        y_edit->setValidator(fValidator);
        QLineEdit * z_edit = new QLineEdit();
        z_edit->setValidator(fValidator);

        QHBoxLayout * xLayout = new QHBoxLayout();
        xLayout->addSpacing(20);
        xLayout->addWidget(x_label);
        xLayout->addWidget(x_edit);

        QHBoxLayout * yLayout = new QHBoxLayout();
        yLayout->addSpacing(20);
        yLayout->addWidget(y_label);
        yLayout->addWidget(y_edit);

        QHBoxLayout * zLayout = new QHBoxLayout();
        zLayout->addSpacing(20);
        zLayout->addWidget(z_label);
        zLayout->addWidget(z_edit);

        vctLayout->addLayout(xLayout);
        vctLayout->addLayout(yLayout);
        vctLayout->addLayout(zLayout);

        mainLayout->addLayout(horLayout);
        mainLayout->addLayout(vctLayout);
        mainLayout->addStretch();//在下方添加一个弹簧填空
        widget->setLayout(mainLayout);
        //当widget回收时,其下面挂载的所有布局、控件均会被依次回收!
        this->setCentralWidget(widget);

}

MainWindow::~MainWindow()
{
}


Qt 的智能指针包括:
QSharedPointer
QScopedPointer
QScopedArrayPointer
QPointer
QSharedDataPointer
QWeakPointer

Qt智能指针使用示例:
QSharedPointer
关键代码:

#include "mainwindow.h"
#include "myobject.h"
#include "qdebug.h"

#include <QSharedPointer>


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /*
QSharedPointer 是一个共享指针,它与 QScopedPointer 一样包装了new操作符在堆上分配的动态对象,
但它实现的是引用计数型的智能指针 ,也就是说,与QScopedPointer不同的是,QSharedPointer可以被自由地拷贝和赋值,
在任意的地方共享它,所以QSharedPointer也可以用作容器元素。

所谓的计数型指针,就是说在内部QSharedPointer对拥有的内存资源进行引用计数,
比如有3个QSharedPointer同时指向一个内存资源,那么就计数3,知道引用计数下降到0,那么就自动去释放内存啦。

*/

    //使用智能指针,
  QSharedPointer<MyOBject> obj =QSharedPointer<MyOBject>(new MyOBject, &QObject::deleteLater);
  obj->print(); //使用智能指针对象调用类方法



}

MainWindow::~MainWindow()
{
    qDebug()<<__FUNCTION__;
}


程序运行后的结果:可以看到,程序运行后,自动释放了MyOBject对象所占用的空间。
在这里插入图片描述

QScopedPointer
关键代码:

#include "mainwindow.h"
#include "myobject.h"
#include "qdebug.h"

#include <QSharedPointer>


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    
    //使用智能指针QScopedPointer
    /*
    QScopedPointer的使用原理比较简单,实际上就是通过QScopedPointer类型,记录申请的某一片内存空间的地址,
在QScopedPointer类型变量生命周期结束时,会自动调用QScopedPointer的析构函数,从而达到自动释放堆上申请的内存空间的目的。
    */
   QScopedPointer<MyOBject> sp=QScopedPointer<MyOBject>(new MyOBject);
   sp->print();


}

MainWindow::~MainWindow()
{
    qDebug()<<__FUNCTION__;
}


运行结果:智能指针自动回收了内存
在这里插入图片描述

QScopedArrayPointer

 //使用智能指针QScopedArrayPointer: 主要用于数组对象,当数组对象使用完成后,智能指针自动回收内存!
   QScopedArrayPointer<MyOBject> arrayPointer(new MyOBject[10]);
   arrayPointer[0].print();
   arrayPointer[5].print();

运行结果:我们新建了10对象,所有析构了十次,逐一回收了内存。
在这里插入图片描述

QPointer

 //使用QPointer智能指针:当其指向的对象(必须是QObject及其派生类)被销毁时,它会被自动置NULL,避免野指针的出现
   QPointer<MyOBject> p=new MyOBject;
   p->print();
   delete p;  //当删除指针或自动回收后,QPoint会自动把p指针设为NULL, 避免野指针的出现

   if(p==NULL) qDebug()<<"p is NULL";

   //如果没有使用QPoint进行包裹,那么指针将不会自动指向NULL
   MyOBject *p2=new MyOBject;
   delete p2;
   if(p2==NULL) qDebug()<<"p2 is NULL";
   else qDebug()<<"p2 not is NULL";

运行效果:
在这里插入图片描述

QSharedDataPointer
参考博客:
Qt实现一个隐式共享类(使用QSharedDataPointer)

QWeakPointer
参考博客:
Qt智能指针–QWeakPointer

总结

  1. 当我们所使用的对象属于QObject或QObject的子类时,Qt框架会自动回收内存,不用我们操心;
  2. 当我们所使用的对象不属于QObject或QObject的子类,而是普通的C++类,那么我们就需要根据实际情况在合适的时机进行内存回收,通常可以采用的方式有: 析构函数回收内存、合适的时机直接delete、使用C++提供的智能指针等方式。
  3. 软件开发中,常说的内存通常是指栈内存和堆内存,栈内存有编译器根据对象的作用域不同进行自动回收,不需要我们操行。而上面所说的内存管理均指堆内存。
    所谓堆内存,就是可以动态申请的内存,使用起来非常方便,但是随之而来的问题就是需要开发人员自己回收,上面的Qt框架已经帮我们做了很多工作内存管理方面的工作。
  4. 对象引用计数是一种通用的内存管理措施,凡是涉及内存管理的地方基本上都是使用这种方式进行管理。

下一篇博客:
Qt6教程之三(12) 文件管理

上一篇博客
Qt6教程之三(10) 异步与并发编程

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值