当前位置: 首页 > news >正文

QT项目初步认识(对象树)

        本博客主要为大家讲解当我们创建一个QT项目,Qt Creator给我们自动生成的文件的作用与联系,以及QT是怎么通过图形化界面生成QT项目的,最后再为大家讲解一下QT中的对象树。

一.创建QT项目

        比较简单,如果各位熟悉创建QT项目的创建,可以跳过这一小节。

        打开Qt Creator,界面如下:

点击new创建新项目,open可以打开以前的项目,这里我们点击new,出现下面的界面。

         这里点击右边第一个创建图形化项目,出现界面如下:

        选择合适的名称和路径,要注意的是路径一定要是全英文,不然qt会报错。点击下一步,界面如下:

        这里我们要选择一个构建系统,构建系统的主要作用就是帮助应用程序或者项目完成代码的构建,在QT中构建系统的主要作用我们可以看成帮助我们自动生成makefile文件。这里我们选择qmake点击下一步,界面如下:

        QMainWindow是一个完整的界面,具有标题栏和菜单栏等,QWidget是一个普通界面,没有标题栏和菜单栏等,QDialog就是一个普通的对话框。这里我们先选择QWidget进行讲解,点击下一步,界面如下:

        这个是为了给国际化项目提供多个国家语言的功能,我们不做了解,点击下一步。

        选择一个qt编译器的sdk,这里我们直接使用之前安装的就行,点击下一步:

        这里是把项目代码提交到GitHub等代码管理平台的,我们直接选择none,点击下一步,项目就创建成功了。界面如下:

         这里我们直接点击左下角的第一个三角形,就可以运行项目,结果如下:

                        

        是一个空的窗口,第一个项目就创建运行成功了。

二.项目代码讲解

        Qt creator 会自动我们之前的选择创建下面的几个文件,我们来一次看看。

1.pro文件

        点击pro文件,我们可以看到以上内容,在Qt creator中pro文件主要是为了项目的构建,qmake会自动根据pro文件的内容生成对应makefile,而makefile可以完成编译、链接的功能。通过.pro文件+qmake,我们就可以自动生成makefile。为了验证,我们鼠标右击项目文件夹,点击构建或运行选项:

        之后我们打开qt项目所在的文件目录:

        点击build文件夹,里面就有生成的makefile文件。

2.widget.ui

        这里我们点击widget.ui,可以进入图形化编辑页面:

        选择左边的控件拖拽进中间的界面就可以便捷的生成控件,这里我们选择底部的label控件,进行拖拽。

        点击控件可以生成文字,右下角可以调整label的属性。创建一个label后我们在点击一下左边的编辑页面:

        出现了一个XML文件,这个文件就是qt根据我们刚才的拖拽操作生成的。这个文件有什么作用吗?我们先不解释,直接运行代码。

                                

        结果成功,我们在点击之前打开过的build文件夹。

         有一个ui_widget.h,我们打开这个文件。

        这里有俩个类:Ui_Widget和Widget(后面会用到),widget继承于Ui_Widget。在Ui_Widget中就有我们之前设计的label。

        因此我们可以得出ui文件的作用如下:

        qt系统通过我们点击ui文件后设计的界面生成一个xml文件,再根据这个xml文件的内容生成ui_widget.h文件,其中ui_widget.h的代码就实现了我们设计的界面。

3.widget.h和widget.cpp

先看widget.h:

        再看widget.cpp文件:

        这里构造函数为什么要有parent参数和对象树有关,我们最后再讲。 

4.main.cpp

QApplication应用程序类

        一个qt程序自带的类,用于实现qt图形化程序。

 a.exec()

        程序进入消息循环,等待对用户输入进行响应。这里a.exec会一直循环运行,直到用户退出,在exec()中,Qt接受并处理用户和系统的事件并且把它们传递给适当的窗口部件。

w.show

        显示widget窗口

Widget

        widget是在栈上定义的,循环结束,栈要销毁时会自动调用构造函数。

三.对象树

        在 Qt 中创建很多对象的时候,构造函数会提供一个 Parent 对象指针,这个指针就是用来构建对象树的。

1.什么是对象树

        简单的来说就是通过N叉树的结构讲各个qt中的对象组织起来,如:

        通过对象树我们就让qt中的类被组织了起来。对象树的作用就是当父对象析构时子对象也应该析构,如上图的QWidget析构释放时,他的子控件也应该全部释放,不允许存在。

2.挂载到对象树的条件

        继承自QObject的类才可以参与到对象树中。qt中的控件都是继承自QObject类,都可以产于到对象树中,但是我们自定义实现没有继承QObject的类是无法参与到对象树中的。

3.对象树实现原理

 1. 父对象指针与子对象列表:
   每个QObject实例包含一个指向父对象的指针(parent)和一个子对象列表(children)。
   当创建子对象时,通过构造函数或`setParent()`方法指定父对象,该子对象会被添加到父对象的children列表中。

2. 自动析构机制
   父对象在析构时,会遍历其children列表,递归删除所有子对象
   子对象析构时,会从父对象的children列表中移除自身,防止父对象重复删除。


3. 动态父对象变更
   调用setParent()时,子对象会从原父对象的列表中移除,并添加到新父对象的列表中。
   若新父对象为nullptr,对象成为顶层对象,需手动管理其生命周期。

4. 禁止拷贝构造:
   QObject禁用拷贝构造函数和赋值运算符,确保每个对象在树中唯一,维护父子关系的一致性。
 

        上面的这些原理都是在QObject中实现的,所以继承自QObject是先决条件。

4.对象树的作用

1. 自动内存管理,避免内存泄漏
        当一个父对象被销毁时(如调用 delete或超出作用域),Qt 会自动递归删除其所有子对象,开发者无需手动释放子对象内存。子对象被删除时,会从其父对象的子对象列表中移除,避免父对象后续误操作已释放的子对象。主要就是对于图形化程序来说父对象没了,位于其上的子对象应该也应自动销毁。
2. 事件传递与信号槽机制的基础
       子对象的事件(如鼠标点击)可沿对象树向上传递到父对象,实现事件过滤或统一处理。
        如:父对象 window 监听子对象button的事件,button->installEventFilter(window); 
3.信号槽自动清理  
       若对象被删除,Qt 会自动断开与其相关的信号槽连接,避免因对象销毁导致的无效回调。

5.使用例子

        这里我们编写一个继承自QLabel的自定义类,学习对象树的使用和验证对象树会自动析构父对象销毁时的子对象。

mylabel.h:

#ifndef MYLABEL_H
#define MYLABEL_H
#include<QLabel>class mylabel:public QLabel
{
public:mylabel(QWidget*parent);~mylabel();
};#endif

  mylabel.cpp:

#include "mylabel.h"
#include<iostream>mylabel::mylabel(QWidget*parent):QLabel(parent)
{}
mylabel::~mylabel()
{std::cout<<"~mylabel"<<std::endl;
}

        这里我们的mylabel是要建立在窗口widget上,因此构造函数定义如下:

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

        mylabel继承自Qlabel,Qlabel继承自QObject,在QObeject的构造函数中才实现了对象树的功能,因此我们直接调用QLabel的构造函数,QLablel的构造函数又会调用QObject的构造函数,从而将mylabel的parent指针指向我们传入的Qwidget对象,并让mylabel加入Qwidget对象的child列表。实现对象树的挂载。

        此外我们也自定义实现了构造函数,来验证对象树会自动析构。 

        首先要显示控件一般是在Widget.cpp的构造函数中,这样在main函数中调用widget类构造的时候才会将我们的控件也构造出来。widget.cpp代码如下:

#include "widget.h"
#include "ui_widget.h"
#include<mylabel.h>
#include"QLabel"
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QLabel* label1=new mylabel(this);label1->setText("hahahahaha");}Widget::~Widget()
{delete ui;
}

        运行结果:

                        

        控制台输出:

 这里注意几点:

1.在代码中我们new出了一个对象,挂载到对象树后,没有调用delete,验证了在对象树中父对象结束后会自动调用析构函数。

2.对应要挂载到对象树的对象,一定要new出来,new是在堆上申请内存,生命周期在delete才会结束,如果我们在栈上直接定义,栈结束会自动析构,父对象结束也会调用析构,重复析构会报错。

3.QLabel中的析构函数是虚函数,因此QLabel和mylabel的析构函数形成了覆盖,最后析构函数调用的是mylabel的析构函数。

        博客就讲到这了,有帮助的话点个赞吧。 


http://www.mrgr.cn/news/93792.html

相关文章:

  • vs code 设置字体颜色
  • deepseek在pycharm中的配置和简单应用
  • K8S学习之基础十九:k8s的四层代理Service
  • 分布式ETCD面试题及参考答案
  • 数据结构第六节:二叉搜索树(BST)的基本操作与实现
  • 数据结构第八节:红黑树(初阶)
  • 74LS148实现优先权管理
  • Python asyncIO 面试题及参考答案 草
  • 《C陷阱与缺陷》读书笔记(一)
  • 如何利用DeepSeek+OCR技术打造一款AI投标工具:自动解析招标文件并生成投标标书的工具?
  • 初识大模型——大语言模型 LLMBook 学习(一)
  • 利用pdf.js+百度翻译实现PDF翻译,创建中文PDF
  • 【UCB CS 61B SP24】Lecture 28 - Tries 学习笔记
  • Java多线程与高并发专题——什么是阻塞队列?
  • Linux中的TCP编程接口基本使用
  • 【开源界的Manus替代战:模块化设计 vs 跨平台实战 vs 全能开发,谁主沉浮?】
  • HTML5(Web前端开发笔记第一期)
  • Vue使用jsts,将wkt转为geojson
  • 微服务与消息队列RabbitMQ
  • Windows控制台函数:控制台窗口设置函数system(“mode con ...“)