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

news2024/11/28 20:56:14

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.coloradmin.cn/o/1425325.html

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

相关文章

AI绘画探索人工智能的未来

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-8fL64RHWVzwpzR6m {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

MBR分区转换为GPT分区

这里有一个ecs-test用于测试MBR转换为GPT 新增一块数据盘 将数据盘以MBR分区格式分区 将整块磁盘以mbr形式分区 格式化&#xff0c;挂载等 上传文件&#xff0c;方便测试(以便后续转换格式类型&#xff0c;防止文件丢失) 取消挂载 将MBR转换为GPT 需先下载gdisk yum instal…

【每日一题】6.LeetCode——轮转数组

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》|《数据结构与算法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢…

使用 axios 请求库,设置请求拦截

什么是 axios&#xff1f; 基于promise网络请求库&#xff0c;可以同构&#xff08;同一套代码可以运行在浏览器&#xff09;&#xff0c;在服务端&#xff0c;使用原生node.js的http模块&#xff0c;在客户端&#xff08;浏览器&#xff09;中&#xff0c;使用XMLHttpRequests…

鸿蒙开发教学-图片的引用

Image通过调用接口来创建&#xff0c;接口调用形式如下&#xff1a; Image(src: string | Resource | media.PixelMap)该接口通过图片数据源获取图片&#xff0c;支持本地图片和网络图片的渲染展示。其中&#xff0c;src是图片的数据源。 加载图片资源 Image支持加载存档图&…

开年3D第一爆,“恋与深空”网游周流水破1.5亿!底层技术其实是它!

“恋与深空”3D游戏最近在互联网频繁刷屏&#xff0c;首周流水更是破1.5亿&#xff0c;称得上2024新年第一爆。 图源网络&#xff0c;侵删 近些年2D游戏模式已经给网友带来倦怠&#xff0c;一部分网友开始寄希望于3D版本&#xff0c;但3D版本研发之初&#xff0c;大部分人其实…

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

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

nodejs+vue+ElementUi电商购物个性化商城推荐系统gqfe

电本电商个性化推荐系统是为了提高用户查阅信息的效率和管理人员管理信息的工作效率&#xff0c;可以快速存储大量数据&#xff0c;还有信息检索功能&#xff0c;这大大的满足了用户和管理员这二者的需求。操作简单易懂&#xff0c;合理分析各个模块的功能&#xff0c;尽可能优…

3 分布式文件系统

3.1 什么是分布式文件系统 要理解分布式文件系统首先了解什么是文件系统。 查阅百度百科&#xff1a; 文件系统是负责管理和存储文件的系统软件&#xff0c;操作系统通过文件系统提供的接口去存取文件&#xff0c;用户通过操作系统访问磁盘上的文件。 下图指示了文件系统所处…

负载均衡下webshell连接

负载均衡下webshell连接、nginx解析漏洞、sql注入第一关&#xff08;三选一&#xff09; 负载均衡下webshell连接&#xff1a; 实验环境&#xff1a; 先下载一下蚁剑&#xff1a;https://github.com/AntSwordProject/AntSword-Labs 安装docker环境&#xff1a; 将蚁剑放在虚…

数据结构—循环队列

数据结构—循环队列 循环队列设计循环队列结构体的创建循环队列的初始化判空判满插入&#xff08;尾插&#xff09;出队列&#xff08;头删&#xff09;取队头元素取队尾元素空间的销毁 循环队列 一般在操作系统课程讲解生产者消费者模型时可以就会使用循环队列。环形队列可以…

Vulnhub-RIPPER: 1渗透

文章目录 一、前言1、靶机ip配置2、渗透目标3、渗透概括 开始实战一、信息获取二、rips的使用三、获取密码文件四、日志审查五、提权 一、前言 由于在做靶机的时候&#xff0c;涉及到的渗透思路是非常的广泛&#xff0c;所以在写文章的时候都是挑重点来写&#xff0c;尽量的不饶…

nodejs+vue+ElementUi宠物领养救助网站0w6wc

宠物领养救助平台采用B/S架构&#xff0c;数据库是MySQL。网站的搭建与开发采用了先进的nodejs进行编写&#xff0c;使用了vue框架。该系统从两个对象&#xff1a;由管理员和用户来对系统进行设计构建。主要功能包括&#xff1a;个人信息修改&#xff0c;对用户、宠物类别、宠物…

appsmith安装手记:5.Sql server数据库容器再安装(yml)

上次安装好了sql server容器&#xff0c;但是appsmith创建数据源出现连接错误&#xff1a; [2024-01-30 00:25:52,186] userEmailttapp.com, sessionIdb99a2476-5c35-4a71-9e8e-25c9c3292cfa, threadboundedElastic-38, requestIdea262c1d-722b-4176-aac7-4b062d7066b6 - Opera…

小白初探|神经网络与深度学习

一、学习背景 由于工作的原因&#xff0c;需要开展人工智能相关的研究&#xff0c;虽然不用参与实际研发&#xff0c;但在项目实施过程中发现&#xff0c;人工智能的项目和普通程序开发项目不一样&#xff0c;门槛比较高&#xff0c;没有相关基础没法搞清楚人力、财力如何投入&…

敏捷软件研发管理流程- scrum

Leangoo领歌是一款永久免费的专业的敏捷开发管理工具&#xff0c;提供端到端敏捷研发管理解决方案&#xff0c;涵盖敏捷需求管理、任务协同、进展跟踪、统计度量等。 Leangoo领歌上手快、实施成本低&#xff0c;可帮助企业快速落地敏捷&#xff0c;提质增效、缩短周期、加速创新…

嵌入式学习第十六天

制作俄罗斯方块小游戏&#xff08;一&#xff09; 分析&#xff1a; printf函数高级用法 \033[&#xff1a;表示转义序列的开始 m&#xff1a;表示转义序列的结束 0&#xff1a;重置所有属性 1&#xff1a;设置粗体或高亮 30-37&#xff1a;设置字体色 30: 黑 31: 红 32:…

服务器未启动而端口进程仍在运行如何查看并杀死

首先登录服务器然后查看当前监听的端口&#xff1a; sudo netstat -tuln比如这里的8080&#xff0c;我们此时并未启动服务器&#xff0c;但是它却正在运行&#xff0c;这会导致服务器刚启动就秒挂。如果没有日志的话会让人有点疑惑&#xff0c;这种情况可能是之前运行了该进程…

系统架构设计师-21年-下午答案

系统架构设计师-21年-下午答案 更多软考知识请访问 https://ruankao.blog.csdn.net/ 试题一必答&#xff0c;二、三、四、五题中任选两题作答 试题一 (25分) 说明 某公司拟开发一套机器学习应用开发平台&#xff0c;支持用户使用浏览器在线进行基于机器学习的智能应用开发…

rancher证书过期问题处理

问题 起初&#xff0c;打开rancher ui页面打不开&#xff0c;telnet rancher的服务端口也不通。查看rancher 控制节点&#xff0c;日志显示&#xff0c;X509&#xff1a;certificate has expired or is not ye valid。证书已过期 解决 现在网上大部分的解决方案都是针对的2…