【Qt】编写第一个Qt程序 对象树 内存泄漏问题探讨

news2024/11/17 1:36:25

编写第一个Qt程序

  • 1. 使用图形化界面生成
  • 2. 使用代码生成
  • 3. 对象树
    • 3.1什么是对象树
    • 3.2 验证对象树
  • 4. 解决编码问题

1. 使用图形化界面生成

  1. 创建好一个项目后,我们可以点击 widget.ui 进入图形化界面设计,可以直接通过拖拽的方式进行添加。

在这里插入图片描述

  1. 通过拖拽的方式进行设计

在这里插入图片描述
在这里插入图片描述

此时ui界面就会生成对应的 XML 格式代码,这个时候qmake就会根据这个XML代码生成对应的C++代码,我们也可以在同目录下找到这个C++代码。

在这里插入图片描述

2. 使用代码生成

我们点击 widget.cpp 里面,会有一个widget 的构造函数和析构函数,我们一般使用代码进行编辑界面的时候,一般都是在widget的构造函数中实现,因为在main’函数中调用了widget类之后就直接showl,所以卸载构造函数中的时候,一旦执行到了show就一定可以显示出设计的界面。

#include "widget.h"
#include "ui_widget.h"
#include <QLabel> // 包含标签的头文件

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QLabel* label = new QLabel(this); // 给当前的Label标签指定一个父类
    label->setText("helle Qt"); // 个标签设置内容
}

Widget::~Widget()
{
    delete ui;
}
void setText(const QString &);

这里我们会发下使用字符串的时候并不是我们C++使用的标准库中的string,而是Qt自己包装好的字符串QString。这个其实也是历史原因,Qt诞生于1991年,那个时候C++还没有定标准,而Qt为了更好的开发就自己包装了一些容器。但是我们也还是可以使用C++的标准库中的容器来使用。

这个时候我们就可以运行了。

在这里插入图片描述

到这里我们有小伙伴就会问了,我们上面使用new在堆上开辟了一块空间,但是我们没有使用delete进行释放控件,这样不就会导致内存泄漏啊。这个就需要涉及到Qt的对象树了。

3. 对象树

3.1什么是对象树

在 Qt 中创建很多对象的时候会提供⼀个 Parent 对象指针,下⾯来解释这个 parent 到底是干什么的。

  • Object是以对象数组织起来的
    • 当创建一个Object对象时,会看到Object的析构函数接收到了一个Object指针作为参数,这个参数就是parent,也就是父对象指针
    • 这相当于,在创建Object对象时,可以提供一个父类对象,我们创建这个Object对象会自动添加到其父类对象的children()列表中
    • 当父类对象析构的时候,这个列表中的所有对象也会被析构。(注意这个父对象并不是继承意义上的父类)

这种机制在 GUI 程序设计中相当有用。例如,⼀个按钮有⼀个 QShortcut(快捷键)对象作为其子对象。当删除按钮的时候,这个快捷键理应被删除。这是合理的。

  • QWidget是能够在屏幕上显示一切主键的父类。

    • QWidget 继承自 QObject ,因此也继承了这种对象树关系。⼀个孩子自动地成为父组件的⼀个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭⼀个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该⼀起被删除。事实就是如此,因为这些都是对话框的⼦组件。
    • 当然,我们也可以自己删除子对象,它们会自动从其父对象列表中删除。比如,当我们删除了⼀个工具栏时,其所在的主窗口会自动将该工具栏从其子对象列表中删除,并且自动调整屏幕显示。
  • Qt引入了对象树概念,在一定程度上解决了内存泄漏的问题。

    • 当⼀个 QObject 对象在堆上创建的时候,Qt 会同时为其创建⼀个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。
    • 任何对象树中的 QObject 对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的 children() 列表中删除;如果有孩子,则自动 delete 每⼀个孩子。Qt 保证没有 QObject 会被 delete 两次,这是由析构顺序决定的。
  • 如果 QObject 在栈上创建,Qt 保持同样的行为。正常情况下,这也不会发⽣什么问题。来看下⾯的代码片段:

{
	QWidget window;
	QLabel label("hello", &window); // 指定父类是widow
}

作为父组件的 window 和作为子组件的 lable 都是 QObject 的子类(事实上,它们都是QWidget的⼦类,而QWidget 是 QObject 的子类)。这段代码是正确的,lable的析构函数不会被调⽤两次,因为标准 C++ 要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作用域时,会先调用 lable 的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。
在这里插入图片描述

  • 但是一旦我们的代码稍微修改一点就会出错
{
	QLabel label("hello"); // 指定父类是widow
	QWidget window;
	label.setParent(&window);
}

情况又有所不同,析构顺序就有了问题。我们看到,在上面的代码中,作为父对象的 window 会首先被析构,因为它是最后⼀个创建的对象。在析构过程中,它会调用子对象列表中每⼀个对象的析构函数,也就是说, label此时就被析构了。然后,代码继续执行,在 window 析构之后,label也会被析构,因为 label也是⼀个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第⼆次调⽤ label 的析构函数了,C++ 不允许调用两次析构函数,因此,程序崩溃了。
在这里插入图片描述

由此我们看到,Qt 的对象树机制虽然在⼀定程度上解决了内存问题,但是也引⼊了⼀些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰⼀下,所以,我们最好从开始就养成良好习惯,就是我们尽量将其开辟在堆上,并指定好parent父类对象。

  • 如果我们把最初的代码改成在栈上开辟的话我们运行程序会发现什么都没有。

在这里插入图片描述

3.2 验证对象树

首先问们自定义一个label类,并在析构部分打上日志。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

此时我们可以按F4来进行.h文件和.cpp文件来回切换。

在这里插入图片描述
此时我们的mylabel.cpp中就是

#include "mylabel.h"
#include <iostream>

Mylabel::Mylabel(QWidget* parent) : QLabel(parent)
{
}
Mylabel::~Mylabel()
{
    std::cout << "Mylagel 进行析构了" << std::endl;
}

widget.cpp中的代码就是

#include "widget.h"
#include "ui_widget.h"
#include "mylabel.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    Mylabel* label = new Mylabel(this);
    label->setText("hello Qt");
}

Widget::~Widget()
{
    delete ui;
}

此时我们运行代码,就可以看到窗口上有helle Qt的字样,只要我们关闭窗口,就会输出我们的日志。

在这里插入图片描述

这里也是验证了对象树自动释放对象的能力。但是这里似乎出现了乱码的情况,所以接下来我们要解决的就是编码方式匹配。

4. 解决编码问题

我们用文本文件打开mylabel.cpp文件,可以看到这个文件的编码方式。

在这里插入图片描述

可看到这个文件的编码方式是utf8,但是Qt的这个终端的编码方式肯定不是utf8,但是Qt不支持修改编码方式,所以这里我们就需要借助Qt自己提供的打印日志的功能qDebug,或者使用QString来处理编码方式。

#include "mylabel.h"
#include <iostream>
#include <QDebug>

Mylabel::Mylabel(QWidget* parent) : QLabel(parent)
{
}

Mylabel::~Mylabel()
{
//    std::cout << "Mylagel 进行析构了" << std::endl;
    qDebug() << "Mylagel 进行析构了"; // 注意这里我们并不是直接使用QDebug,而是使用了qDebug是一个宏
}
#define qDebug QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug

在这里插入图片描述

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

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

相关文章

群体神经科学中的社会公正:避免数据分析中的社会构建误用

TLDR&#xff1a;有意思的观点。文章作者强调&#xff0c;使用群体神经科学大数据时&#xff0c;研究人员必须承担起伦理和科学责任&#xff0c;避免对边缘化群体造成进一步的污名化。通过像ABCD这样的开放数据集&#xff0c;研究人员应避免将种族、性别等简单化处理为独立变量…

「JavaScript深入」聊一聊 new操作符具体干了什么?

JavaScript深入 — new操作符 概念流程手写new操作符 概念 在JavaScript中&#xff0c;new 操作符用于创建一个给定构造函数的实例对象 function Person(name, age){this.name name;this.age age; } Person.prototype.sayName function () {console.log(this.name) } cons…

记一次从登录框到内网横向

前言 接到公司通知要对某某行业做渗透测试&#xff0c;挖一些尽可能高危的漏洞&#xff0c;于是便有了接下来的故事。 0x01 信息收集 首先收集了十几个该行业相关的网站&#xff0c;然后一个个做信息收集&#xff0c;先列出来可利用的点。 1.某某公司为YouDianCMS&#xff08…

车载诊断技术:汽车健康的守护者

一、车载诊断技术的发展历程 从最初简单的硬件设备到如今智能化、网络化的系统,车载诊断技术不断演进,为汽车安全和性能提供保障。 早期的汽车诊断检测技术处于比较原始的状态,主要依靠操作经验和主观评价。随着汽车工业的发展,车载诊断技术也经历了不同的阶段。20 世纪初…

Java生成Markdown格式内容

前一篇写的是markdown格式的文本内容转换保存为word文档&#xff0c;是假定已经有一个现成的markdown格式的文本&#xff0c;然后直接转换保存为word文档&#xff0c;不过在开发中&#xff0c;通常情况下&#xff0c;数据是从数据库中获取&#xff0c;拿到的数据映射到java对象…

趋势外推法

趋势外推法主要利用图形识别法和差分法计算&#xff0c;进行模型的基本选择。 一、图形识别法。 这种方法是通过绘制散点图来进行的&#xff0c;即将时间序列的数据绘制成以时间 t 为横轴、时序观察值为纵轴的图形&#xff0c;观察并将其变化曲线与各类函数曲线模型的图形进行…

物联网系统中高精度温度检测方案_温度变送器

01 物联网系统中为什么要使用温度变送器 在物联网系统中使用温度变送器的原因主要可以归纳为以下几点&#xff1a; 1、温度监测与控制 实时数据获取&#xff1a;温度变送器能够将温度这一物理量转换为可传输的标准化电信号&#xff08;如4-20mA电流信号、0-10V电压信号或RS48…

Spring Boot 进阶-Spring Boot 开发第一个Web接口

在前面的文章中我们对Spring Boot的配置以及日志配置有了大概的了解,在我们搭建完成第一个Spring Boot项目之后也提到了一个概念就是RestFul风格的接口开发。下面我们就来详细介绍一下使用Spring Boot如何去开发一个RestFul的Web接口。 准备 在开发接口之前,需要引入的就是W…

一文上手Kafka【中】

一、发送消息细节 在发送消息的特别注意: 在版本 3.0 中&#xff0c;以前返回 ListenableFuture 的方法已更改为返回 CompletableFuture。为了便于迁移&#xff0c;2.9 版本添加了一个方法 usingCompletableFuture&#xff08;&#xff09;&#xff0c;该方法为 CompletableFu…

【工具类】证书自动续签免费版 正式发布

证书自动续签免费版 正式发布 1.控制台首页2.申请证书2.1.支持自动解析和手动解析两种模式2.2.LB&#xff08;负载均衡&#xff09;支持主流的云厂商2.3.CDN&#xff08;内容分发&#xff09; 支持主流的云厂商2.4.对象存储&#xff08;OSS&#xff09;支持主流云厂商2.5DCDN&a…

TDengine 在业务落地与架构改造中的应用实践!

前言 在物联网和大数据时代&#xff0c;时序数据的管理和分析变得至关重要。TDengine&#xff0c;作为一款专为时序数据设计的开源数据库&#xff0c;以其卓越的存储和查询效率&#xff0c;成为众多企业优化数据架构的优选。本文将分享我将TDengine成功应用于实际业务的经验&am…

elementui/plus灯下面的指示怎么改成圆圈或者隐藏

改成圆圈的步骤 改成没有indicator-position指示的位置/或者隐藏

用友U8 CRM 多个接口存在SQL注入漏洞

本文所涉及的任何技术、信息或工具&#xff0c;仅供学习和参考之用&#xff0c;请勿将文章内的相关技术用于非法目的&#xff0c;如有相关非法行为与文章作者无关。请遵守《中华人民共和国网络安全法》。 中华人民共和国网络安全法 第二十七条 规定 任何个人和组织不得从事非…

保姆级复现yolov7(论文复现)

保姆级复现yolov7&#xff08;论文复现&#xff09; 本文所涉及所有资源均在传知代码平台可获取 所需环境 当然深度学习环境的搭建是基础&#xff0c;详情可见(win10)&#xff1a; 【深度学习】windows10环境配置详细教程anaconda3环境变量配置win10-CSDN博客 Unbutun搭建深度学…

正则表达式的使用示例--Everything文件检索批量重命名工具

一、引言 Everything是一款非常实用的文件搜索工具&#xff0c;它可以帮助您快速定位并查找计算机中的文件和文件夹。Everything搜索文件资料之神速&#xff0c;有使用过的朋友们都深有体会&#xff0c;相对于Windows自带的搜索功能&#xff0c;使用Everything&#xff0c;可以…

C语言 | Leetcode C语言题解之第442题数组中重复的数据

题目&#xff1a; 题解&#xff1a; int* findDuplicates(int* nums, int numsSize, int* returnSize) { int *ans (int *)malloc(sizeof(int) * numsSize);int pos 0;for (int i 0; i < numsSize; i) {int x abs(nums[i]);if (nums[x - 1] > 0) {nums[x - 1] -…

$attrs 和 $listeners

通常情况下&#xff0c;父子组件之间的数据是通过 props 由父向子传递的&#xff0c;当子组件想要修改数据时&#xff0c;则需要通过 $emit 以事件形式交由父组件完成&#xff0c;而这种交互方式只存在于父子组件之间&#xff0c;多层嵌套的时候&#xff0c;处于内层的组件想要…

OpenAI o1与GPT-4o究竟强在哪里

OpenAI 的 O1 模型与 GPT-4o 相比&#xff0c;具有显著的技术进步和性能提升。以下是两者的主要区别和 O1 的进步之处&#xff1a; 推理能力&#xff1a;O1 模型在处理复杂问题&#xff08;如编程和数学&#xff09;方面表现出更强的推理能力。例如&#xff0c;在国际数学奥林匹…

使用ffmpeg合并视频和音频

使用ffmpeg合并视频和音频 - 哔哩哔哩 简介 FFmpeg是一套可以用来记录、转换数字音频、视频&#xff0c;并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec&#xff0…

【项目文档】软件系统培训方案(Doc原件2024)

1. 培训概述 2. 培训目的 3. 培训对象及要求 3.1. 培训对象 3.2. 培训人员基本要求 4. 培训方式 5. 培训内容 6. 培训讲师 7. 培训教材 8. 培训质量保证 8.1. 用户培训确认报告 8.2. 培训疑问解答 软件全套资料部分文档清单&#xff1a; 工作安排任务书&#xff0c;可行性分析报…