首页 > 系统相关 >【Qt】编写第一个Qt程序 && 对象树 && 内存泄漏问题探讨

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

时间:2024-09-28 10:22:50浏览次数:3  
标签:&& QWidget Qt parent 对象 label 内存 析构

编写第一个Qt程序

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

在这里插入图片描述

标签:&&,QWidget,Qt,parent,对象,label,内存,析构
From: https://blog.csdn.net/qq_74276498/article/details/142595927

相关文章

  • 【项目综合】高并发内存池
    目录一、项目背景1)mini版的tcmalloc2)内存池是什么3)C/C++的malloc和new4)所用技术栈和项目环境二、实现定长内存池1)基本框架2)申请内存块3)释放内存块4)细节优化和性能测试 三、高并发内存池的整体框架四、实现threadcache申请内存1)基本框架2)哈希桶中的映射......
  • Qt源码编译-Ubuntu平台
    Qt源码编译-Ubuntu平台Qt官网已取消了Qt5.15版本二进制安装包。如果要安装Qt5.15需要下载源码自行编译安装或使用商业授权版本。Qt是一个功能强大的跨平台开发框架,支持从嵌入式系统到桌面应用程序的开发。如果你希望在Ubuntu平台上从源码编译Qt,这篇教程将带你一步步......
  • Qt面试题
    什么是Qt的元对象系统?点击查看代码Qt的元对象系统是Qt框架中一个核心的组成部分,提供了一种机制来拓展C++的功能,Qt的元对象系统(Meta-ObiectSystem)提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。元对象系统由以下三个基础组成。1.QObiect类是所有......
  • PyQt5 使用 QFrame 实现页面类抽屉式的进入与退出的动画
    PyQt5使用QFrame实现页面类抽屉式的进入与退出的动画当多个页面切换,但是又不想每个页面里的内容只是简单的出现与消失,则可以使用这个QPropertyAnimation动画代码结构本文中全部代码全在test_QFrame_Animation.py这一个文件中编码,步骤中有变动的地方会注释标注,无改动的不会重......
  • 二级指针内存模型
    二级指针主要分成三种内存模型:1》指针数组:指针指向栈区的一段内存的首地址,并且栈区分配内存空间,每个元素又装有一个指针指向常量区的某一个地址类似于char*myArray[]={"aaaaa","cccccc","bbbbbb","11111"};应用场景名称:指针数组涉及到2个内存区:栈区和栈区 ......
  • 大页内存统计
    #!/bin/bash#大页内存大小(单位kB)HUGE_PAGE_SIZE=1048576#1048576KB=1024MB=1GB#系统所有进程总HUGE_PAGETOTAL_HUGE_PAGE_SIZE=0#KEYWORDS=KernelKEYWORDS=HugetlbforPIDin/proc/[0-9]*;doPID=${PID##*/}#单个进程总HUGE_PAGEPROCESS_HUGE_PAGE_......
  • Qt - QObject设置对象名称和属性
    作用用来作为id选择器和属性选择器,来设置样式setObjectName方法用来设置对象的名字,不同对象可以重复label=QLabel()label.setObjectName("label")objectName方法获取对象的名字QStringstrobj=this->objectName();qDebug()<<"strobj="<<strobj;setProperty方法......
  • 深度剖析数据在内存中的存储~
    今天给小伙伴们分享的是数据在内存中存储的相关知识,全程干货满满哦~一.基本类型的介绍在C语言中,基本内置类型及其通常的内存大小如下。但请注意,实际的大小取决于编译器和正在运行程序的系统的架构(32位或64位)。整数类型:int:通常为4字节,但有时在某些系统上可能是2字节......
  • QT 获取视频帧Opencv获取清晰度
    先展示结果:1.获取摄像头的分辨率mResSize.clear();mResSize=camera_->supportedViewfinderResolutions();ui->comboBox_resulation->clear();inti=0;foreach(QSizemsize,mResSize){qDebug()<<msize;ui->comboBox_re......
  • oracle数据库内存分配方案
    查询当前参数设置SQL>showparametersgaSQL>showparameterpgaSQL>showparametermemory参数说明sga_target期望的sga大小sga_max_size最大sga大小pga_aggregate_target期望的pga大小pga_aggregate_limit最大pga大小设置原则sga_target不能大于sga_max_si......