qt内存自动释放的两种情况

qt内存管理机制

QObject的parent

我们时常能看到QWidget或者其他的控件的构造函数中有一项参数parent,默认值都为NULL,例如:

QLineEdit(const QString &contents, QWidget *parent = nullptr);
QWidget(QWidget *parent = nullptr, Qt::WindowFlags f = ...);

这个parent的作用就在于使当前的对象实例加入parent指定的QObject及其派生类的children中,当一个QObject被delete或者调用了它的析构函数时,所有加入的children也会全部被析构

如果parent设置为NULL,会有如下的情况:

1、如果是构造时直接指定了NULL,那么当前实例不会有父对象存在,Qt也不能自动析构该实例除非实例超出作用域导致析构函数被调用,或者用户在恰当的实际使用delete操作符或者使用deleteLater方法;
2、如果已经指定了非NULL的parent,这时将它设置成了NULL,那么当前实例会从父对象的children中删除,不再受到QObject & parent机制的影响;
对于QWidget,parent为NULL时代表其为一个顶层窗口,也可以就是独立于其他widget在系统任务栏单独出现的widget,对于永远都是顶层窗口的widget,例如QDialog,当parent不为NULL时他会显示在父widget中心区域的上层;
3、如果QWidget的parent为NULL或是其他值,在其加入布局管理器或者QMainWindow设置widget时,会自动将parent设置为相应的父widget,在父控件销毁时这些子控件以及布局管理器对象会一并销毁
所以我们可以看出,QObject对象实际上拥有一颗类实例关系树,在树中保存了所有通过指定parent注册的子对象,而子对象里又保存有其子对象的关系树,所以当一个父对象被销毁时,所有依赖或间接依赖于它的对象都会被正确的释放,使用者无需手动管理这些资源的释放操作。

内存使用建议

基于此原理,我们可以放心的让Qt管理资源,这里有几个建议:
对于QObject及其派生类,如果彼此之间存在一定联系,则应该尽量指定parent,对于QWidget应该指定parent或者加入布局管理器由管理器自动设置parent。
对象只需要在局部作用域存在时可以选择不进行内存分配,利用局部作用域变量的生命周期自动清理资源。
对于非QWidget的对象来说,如果不指定非NULLparent,则需要自己管理对象资源。QWidget比较特殊,我们在下一节讲解。
对于在局部作用域上创建的父对象及其子对象,要注意对象销毁的顺序,因为父对象销毁时也会销毁子对象,当子对象会在父对象之后被销毁时会引发double free。
QWidget和内存的释放
QWidget也是QObject的子类,所以在parent机制上是没有区别的,然而实际使用时我们更多的是使用“关闭”(close)而不是delete去删除控件,所以差异就出现了。
先提一下widget关闭的流程,首先用户触发close()槽,然后Qt向widget发送QCloseEvent,默认的QCloseEvent会做如下处理:
将widget隐藏,也就是hide();
如果有设置Qt::WA_DeleteOnClose,那么会接着调用widget的析构函数
我们可以看到,widget的关闭实际是将其隐藏,而没有释放内存,虽然我们有时会重写closeEvent但也不会手动释放widget。
看一个因为close机制导致的内存泄漏的例子,我们在button被单击后弹出某个自定义对话框:

button.ConnectClicked(func (_ bool) {dialog := NewMyDialog()dialog.Exec()
})

因为dialog在close时会被隐藏,而且没有设置DeleteOnClose,所以Qt不会去释放dialog,而用户也无法回收dialog的资源,每次点击按钮后内存用量都会增加一点,没错,内存泄露了。
那么给dialog设置一个parent,像这样,会如何呢?

dialog.SetParent(self)

遗憾的是,并没有什么区别,因为这样只是把dialog加入父控件的children,并没有删除dialog,只有父对象被销毁时内存才会真正释放。
解决办法也有三个。

第一种是使用deleteLater,例如:
dialog.DeleteLater()
这会通知Qt的eventloop在下次进入主循环的时候析构dialog,这样一来确实解决了内存泄露,不过缺点是会有不可预测的延迟存在,有时候延迟是难以接受的。

第二种是手动删除widget,适用于parent为NULL的场合:
C++:
delete dialog;
说明一下,调用底层C++对象的析构函数。

第三种比较简单,对于单纯显示而不需要和父控件做交互的widget,直接设置DeleteOnClose即可,close时widget会被自动析构。
总结
Qt提供了一套方便的机制帮助我们进行内存和资源管理,使我们从繁重的劳动中得到了部分的解放,但同时也要注意到那些很容易坑,这样才能写出健壮的正确执行的程序。
具体哪种情况自动释放呢,先程序验证一下:

代码情况:

myLabel.h:

#ifndef MYLABEL_H
#define MYLABEL_H
#include <QWidget>
#include <QLabel>
class MyLabel : public QLabel
{Q_OBJECT
public:explicit MyLabel(QWidget *parent = nullptr);~MyLabel();
signals:
public slots:
};
#endif // MYLABEL_H
myLabel.cpp:
#include "myLabel.h"
MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
{qDebug("enter function MyLabel::MyLabel");qDebug("exit function MyLabel::MyLabel");
}
MyLabel::~MyLabel()
{qDebug("enter function MyLabel::~MyLabel");qDebug("exit function MyLabel::~MyLabel");
}

main.cpp:

#include "widget.h"
#include <QApplication>
#include <QDebug>
#pragma execution_character_set("utf-8")
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{qDebug("program begin!");QApplication a(argc, argv);Widget *w = new Widget();w->resize(300,400);w->show();delete w;qDebug("program end!");return a.exec();
}

myVBoxLayout.h:

#ifndef MYVBOXLAYOUT_H
#define MYVBOXLAYOUT_H
#include <QWidget>
#include <QVBoxLayout>
class MyVBoxLayout : public QVBoxLayout
{Q_OBJECT
public:explicit MyVBoxLayout(QWidget *parent = nullptr);~MyVBoxLayout();
signals:
public slots:
};
#endif // MYVBOXLAYOUT_H

myVBoxLayout.cpp:

#include "myVBoxLayout.h"
MyVBoxLayout::MyVBoxLayout(QWidget *parent) : QVBoxLayout(parent)
{qDebug("enter function MyVBoxLayout::MyVBoxLayout");qDebug("exit function MyVBoxLayout::MyVBoxLayout");
}
MyVBoxLayout::~MyVBoxLayout()
{qDebug("enter function MyVBoxLayout::~MyVBoxLayout");qDebug("exit function MyVBoxLayout::~MyVBoxLayout");
}

是父子关系时的情况:

widget.cpp:

#include "widget.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <myLabel.h>
Widget::Widget(QWidget *parent): QWidget(parent)
{MyLabel *label1 = new MyLabel(this);label1->setText("this is test label1");MyLabel *label2 = new MyLabel(this);label2->setText("this is test label2");
//    QVBoxLayout *v_layout_main = new QVBoxLayout(this);
//    //this->setLayout(v_layout_main);
//    v_layout_main->addWidget(label1);
//    //v_layout_main->addWidget(label2);
//    QHBoxLayout *h_layout_buttons = new QHBoxLayout(this);
//    h_layout_buttons->addWidget(label2);
}
Widget::~Widget()
{
}

运行情况:

program begin!
enter function MyLabel::MyLabel
exit function MyLabel::MyLabel
enter function MyLabel::MyLabel
exit function MyLabel::MyLabel
1127, FromFilemap
enter function MyLabel::~MyLabel
exit function MyLabel::~MyLabel
enter function MyLabel::~MyLabel
exit function MyLabel::~MyLabel
program end!

是通过布局添加时的情况:

#include "widget.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include "myLabel.h"
#include "myVBoxLayout.h"
Widget::Widget(QWidget *parent): QWidget(parent)
{
//    MyLabel *label1 = new MyLabel(this);
//        label1->setText("this is test label1");
//        MyLabel *label2 = new MyLabel(this);
//        label2->setText("this is test label2");
//    MyLabel *label1 = new MyLabel(this);
//    label1->setText("this is test label1");
//    MyLabel *label2 = new MyLabel(this);
//    label2->setText("this is test label2");
//    QVBoxLayout *v_layout_main = new QVBoxLayout(this);
//    this->setLayout(v_layout_main);
//    v_layout_main->addWidget(label1);
//    v_layout_main->addWidget(label2);
//    QHBoxLayout *h_layout_buttons = new QHBoxLayout(this);
//    h_layout_buttons->addWidget(label2);MyLabel *label1 = new MyLabel();label1->setText("this is test label1");MyLabel *label2 = new MyLabel();label2->setText("this is test label2");MyVBoxLayout *v_layout_main = new MyVBoxLayout();this->setLayout(v_layout_main);v_layout_main->addWidget(label1);v_layout_main->addWidget(label2);
}
Widget::~Widget()
{qDebug("enter function Widget::~Widget");qDebug("exit function Widget::~Widget");
}

运行情况:

program begin!
enter function MyLabel::MyLabel
exit function MyLabel::MyLabel
enter function MyLabel::MyLabel
exit function MyLabel::MyLabel
enter function MyVBoxLayout::MyVBoxLayout
exit function MyVBoxLayout::MyVBoxLayout
1127, FromFilemap
enter function Widget::~Widget
exit function Widget::~Widget
enter function MyVBoxLayout::~MyVBoxLayout
exit function MyVBoxLayout::~MyVBoxLayout
enter function MyLabel::~MyLabel
exit function MyLabel::~MyLabel
enter function MyLabel::~MyLabel
exit function MyLabel::~MyLabel
program end!

总结

通过上面实验可知,成为子控件,或者成为布局中的控件,都会跟着释放。
如图所示:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/660837.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3学习——初始化项目及配置

初始化项目 环境 node 16pnpm 8.0.0 命令 pnpm create vite进行以下选择 &#x1f447; – 项目名 – VUe – Ts – cd/目录 – pnpm run dev 浏览器自动打开 package.json 配置eslint 安装依赖包 pnpm i eslint -D npx eslint --init // 生成配置文件进行以下选择 &a…

【2024】大三寒假再回首:缺乏自我意识是毒药,反思和回顾是解药

2024年初&#xff0c;学习状态回顾 开稿时间&#xff1a;2024-1-23 归家百里去&#xff0c;飘雪送客迟。 搁笔日又久&#xff0c;一顾迷惘时。 我们饱含着过去的习惯&#xff0c;缺乏自我意识是毒药&#xff0c;反思和回顾是解药。 文章目录 2024年初&#xff0c;学习状态回顾一…

vue——实现多行粘贴到table事件——技能提升

最近在写后台管理系统时&#xff0c;遇到一个需求&#xff0c;就是要从excel表格中复制多行内容&#xff0c;然后粘贴到后台系统中的table表格中。 如下图所示&#xff1a;一次性复制三行内容&#xff0c;光标放在红框中的第一个框中&#xff0c;然后按ctrlv粘贴事件&#xff0…

掌上医院预约挂号缴费系统源码,与医院信息系统共享数据,实现在线预约挂号、移动支付、医保支付、检验检查报告查看、门诊病历查询等功能。

随着信息技术的发展和互联网的普及&#xff0c;越来越多的患者开始习惯于通过互联网获取医疗服务。网上预约挂号是近年来开展的一项便民就医服务&#xff0c;旨在缓解看病难、挂号难的就医难题&#xff0c;许多患者为看一次病要跑很多次医院&#xff0c;最终还不一定能保证看得…

MySQL数据控制语言DCL

MySQL数据控制语言DCL 目录 MySQL数据控制语言DCLDCL关键字1.事务事务的四大特性START TRANSACTION&#xff1a;开始事务ROLLBACK&#xff1a;回滚COMMIT&#xff1a;提交事务 2.用户权限CREATE USER&#xff1a;创建新的用户并指定权限DROP USER&#xff1a;删除用户ALTER USE…

深度强化学习(王树森)笔记10

深度强化学习&#xff08;DRL&#xff09; 本文是学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。本文在ChatGPT辅助下完成。 参考链接 Deep Reinforcement Learning官方链接&#xff1a;https://github.com/wangshusen/DRL 源代码链接&#xff1a;https://github.c…

LeetCode —— 17. 电话号码的字母组合

&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️Take your time ! &#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️…

基于人体姿态的目标追踪实现

文章目录 概要人体姿态识别人体关键点转检测框实时跟踪器概要 这篇博文简单介绍了如何实现基于人体姿态的多目标跟踪算法。主要分为以下几个步骤: 基于FastDeploy实现人体姿态识别;将人体姿态转化为检测框;基于SORT算法实现目标跟踪。人体姿态识别 参考Fastdeploy实现。 模…

Ubuntu系统硬盘分区攻略(磁盘分区、RAID磁盘阵列、RAID阵列、固态硬盘分区、机械硬盘分区、swap分区、swap交换分区)

文章目录 分区需求分区方案分区顺序相关疑问swap分区不是应该放在最后吗&#xff1f;我安装系统分区的时候&#xff0c;上面有available devices&#xff0c;下面有create software raid(md)&#xff0c;我该用哪个&#xff1f;我available devices下面有个893G的固态&#xff…

Selenium自动化测试 —— 模拟鼠标键盘的操作事件

鼠标操作事件 在实际的web产品测试中&#xff0c;对于鼠标的操作&#xff0c;不单单只有click()&#xff0c;有时候还要用到右击、双击、拖动等操作&#xff0c;这些操作包含在ActionChains类中。 ActionChains类中鼠标操作常用方法&#xff1a; 首先导入ActionChains类&#…

Python OpenCV实现图片像素区域缩放

Python OpenCV实现图片像素区域缩放 前言项目安装OpenCV和Pillow思路代码编写 前言 遇到一个要将大量图片缩放成统一规格的难题&#xff0c;并且这些图片周围还有很多空白像素&#xff0c;所以用Python实现一下。 项目 安装OpenCV和Pillow pip install opencv-python pip …

C++:异常体系

异常体系 异常1.C语言传统的处理错误的方式2.C异常概念3.异常的使用3.1异常的抛出和捕获3.2 异常的重新抛出3.3异常安全3.4 异常规范 4.C标准库的异常体系5.异常的优缺点 异常 1.C语言传统的处理错误的方式 终止程序&#xff0c;如assert&#xff0c;缺陷&#xff1a;用户难以…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之TextClock组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之TextClock组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、TextClock组件 TextClock组件通过文本将当前系统时间显示在设备上。支持不同…

Pytorch从零开始实战18

Pytorch从零开始实战——人脸图像生成 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——人脸图像生成环境准备模型定义开始训练可视化总结 环境准备 本文基于Jupyter notebook&#xff0c;使用Python3.8&#xff0c;Pytorch2.0.1cu118&#…

【2024年美赛即将开赛】最后一天如何提高获奖率

美赛思路预定 01 美赛赛中时间分配美赛时间安排比赛前2~3天第一天&#xff08;2号&#xff09;第二天&#xff08;3号&#xff09;第三天&#xff08;4号&#xff09;第四天&#xff08;5号&#xff09;第五天&#xff08;6号&#xff09;8&#xff1a;00~10&#xff1a;00 02 …

Hadoop-生产调优(更新中)

第1章 HDFS-核心参数 1.1 NameNode内存生产配置 1&#xff09;NameNode 内存计算 每个文件块大概占用 150 byte&#xff0c;一台服务器 128G 内存为例&#xff0c;能存储多少文件块呢&#xff1f; 128 * 1024 * 1024 * 1024 / 150byte ≈ 9.1 亿G MB KB Byte 2&#xff09…

前端构建变更:从 webpack 换 vite

现状 这里以一个 op &#xff08;内部运营管理用&#xff09;项目为例&#xff0c;从 webpack 构建改为 vite 构建&#xff0c;提高本地开发效率&#xff0c;顺便也加深对 webpack 、 vite 的了解。 vite 是前端构建工具&#xff0c;使用 一系列预配置进行rollup 打包&#x…

gdb 调试 - 在vscode图形化展示在远程的gdb debug过程

前言 本地机器的操作系统是windows&#xff0c;远程机器的操作系统是linux&#xff0c;开发在远程机器完成&#xff0c;本地只能通过ssh登录到远程。现在目的是要在本地进行图形化展示在远程的gdb debug过程。&#xff08;注意这并不是gdb remote &#xff01;&#xff01;&am…

实现vue3响应式系统核心-shallowReactive

简介 今天来实现一下 shallowReactive 这个 API。 reactive函数是一个深响应&#xff0c;当你取出的值为对象类型&#xff0c;需要再次调用 reactive进行响应式处理。很明显我们目前的代码是一个浅响应&#xff0c;即 只代理了对象的第一层&#xff0c;也就是 shallowReactiv…

36万的售价,蔚来理想卖得,小米卖不得?

文 | AUTO芯球 作者 | 雷歌 Are you OK&#xff1f;雷军被网友们叫“小雷”&#xff01; 被网友一猜再猜的小米SU7的价格&#xff0c;因为一份保险上牌价格单的曝光被网友吵得热热闹闹&#xff0c;曝出的小米汽车顶配上牌保险价格为36.14万。 20万以下&#xff0c;人们愿称…