QT 第二讲 --- 基础篇 Qt的第一个程序
前言:
在上一讲中,我们系统完成了Qt开发环境的搭建与配置,包括:
-
安装Qt Creator:集成开发工具的核心安装与基本设置;
-
配置编译器:适配MinGW/MSVC等编译器,确保代码编译环境正常;
-
环境验证:通过创建空白项目并运行,确认开发环境无异常。
在本讲中,我们将正式开启Qt开发的实践之旅!在上一讲中,你已经成功搭建了Qt开发环境,现在我们将以此为基石,动手编写第一个Qt程序,本讲将为你打开Qt图形界面开发的大门。
一、图像化实现
下面对于这个“hello world ” 的实现是非常简单的,我们先创建一个项目。
1.1 介绍边栏
滑倒最下面找的我们将要使用QT的第一个控件“label”,长按拖拽他放到右边的空白地方就好。
然后在往生成的空间里面输入我们想要填写的内容就好,然后在点击左下角的运行符号,观察效果。
1.2 观察效果
程序运行结果
1.3 查看修改
运行到这里,我们关于使用图形化界面实现QT程序的操作就结束了,是不是难以置信,这么简单。
对没错,实际上就这么简单,我们只需要将我们想要使用的控件拖拽就好了。
但是我们也必须明白,QT在背后为我们做出的改变,下面我们看一下响应的改变。
变化一:
变化二、
对应的在我们的ui文件当中也添加了对应的代码,同理我们明白了,其实图形化界面的操作如此简单是因为QT在背后为我们实现了具体的代码。
那是不是意味着我们同样可以使用代码直接实现,没错下面我们看一下在不借助QT本身的图形化界面,如何使用代码去实现相同的效果呢?
二、代码实现
在上一讲当中我们讲到,QT的ui界面的实现主要是借助了,widget这个类的实现,现在我们向对界面做出改变我们就必须对该类做出改变。
所以我们的目标是很明确的,那我们在类里面做什么可以到达我们想要的效果呢?
那这里就不得不提回我们上面说的“label”,我们在图形化界面当中使用了这个类,所以在代码中也是需要使用这个类,但是不再是图像的形式,我们要使用QT为我们提供的控件类---“QLabel”,当然它的使用肯定也是要包含对应的同文件,但是QT的设计还是很人性化的,我们可以直接根据类名就好了。
当然在实际写的时候,我们发现编译器识别的有两个头文件,这其实是QT由于出现的时间太早了,比C++98的标准还早,所以导致了存在两种头文件方式,现在大家直接使用C++风格的“QL abel”就好。
这是想要实现效果需要的代码,当然看到下图的你(如果是一位初学者的话,肯定是很疑惑的,但是没关系,下面对于两种写法会有专门的讲解)
写法一:
下面我要讲解一下具体的写法一的含义,下面是结果图。
这种代码大家在学习过C++,应该是明白它表示的含义的,它是构建一个在堆上的对象,然后调用类内的函数接口。
大家在这里肯定至少有两个疑惑,
一、是为啥要在构建指针的时候,传入一个this指针?
二、是setText的作用是什么?
下面一一回答:
问题一:这是将我们的控件放到对象树上,当然关于对象树又是啥,下面一样有讲解。
问题二:这个函数的功能,就是类似于printf()可以将括号的内容输出到图形化界面当中。
当然这里还有一个值得提醒的点。
我们看到根据语法提醒,我们发现这个函数接口的接受参数的类型是QString,可QString又是啥呢?
其实结合我们上面的QT的出现时间比C++98标准还早,其实就有猜测了。
没错就是在QT出现的时候,C++的String库还是不怎么好用的时候,QT为了自己的使用体验,就直接以自己的方式封装了一批库,如:QString,QVector等,大家后面看到了知道就好。
当然了QT也为它自己库重载了C风格的构造接口,所以我们也可以使用“xxx”的形式调用。
当然如果是细心一点的同学肯定有疑问,那就是我们这种写法没有delete我们创建出来的变量,没有问题吗,不会存在内存泄漏的情况吗?
回答是,会,但是我们将这个对象挂到了对象树上,那么就没有这个问题了,至于为什么,那也是我们要到下面才能解释的了。
写法二:
大家首先想一想我们这种写法能得到我们想要的结果吗?这是在栈上开辟的变量,这种写法与第一种堆上开辟空间的做法有啥不同呢?
当然面对问题,想要得出结果最简单的办法就是实操一下,就明白了。
结果很明显了,这种写法是不能达到我们的目的的。所以为什么呢?
我们想一想写法二是不是在栈上开辟空间,当构造函数结束的时候这个变量的生命周期是不是也结束了。结束了,当然也没有结果了。
所以这种写法是不行的。
三、内存泄漏问题
当然到了这个小段,我们在回头看一下我们写法一当中的问题没有对变量进行delete,是否会导致内存泄漏问题?
首先我们要回顾一下什么是内存泄漏。
3.1 内存泄漏的本质
当通过 new
在堆上动态分配内存后,未通过 delete
释放内存时,会导致内存泄漏。该内存将无法被程序回收,持续占用系统资源。
当然根据定义我们发现,这个变量的创建不就是一个典型的内存泄漏,当然这是建立我们没有主动对内存进行释放,但是如果我们的QLabel自己调用了析构函数怎么办?
为了检验这种情况是否存在,下面我们直接使用继承新增一个子类
我们运行结果发现,我们虽然存在乱码问题,但是的确是有符号打印出来,说明 在我们程序结束后的确是调用了对应的析构函数,证明了不存在内存泄漏问题,这种写法。
当然这个结果肯定是归功于对象树,下面我们就第一次正式的认识一下我们QT的对象树。
四、初识对象树
4.1 对象树是什么?
-
树形结构:Qt 中的对象树是一个 N 叉树,每个
QObject
派生类(如窗口、按钮等)可以有一个父对象和多个子对象。 -
父子关系:通过构造函数指定父对象(如
new QLabel(this)
),子对象会被自动添加到父对象的子对象列表中。 -
类比 DOM:类似于网页开发中的 DOM 树,通过树形结构组织界面元素。
类似的结构图如下,就是一个多叉树,管理图形化界面的每一个控件变量
这也是为什么,我们的写法二不行的原因,因为他在出构造函数的时候,就已经析构了。
2. 对象树如何工作?
-
自动释放:当父对象被销毁时,Qt 会 递归删除所有子对象。这是通过
QObject
的析构函数实现的。 -
手动干预:若手动调用
delete
删除子对象,Qt 会将其从父对象的子列表中移除,避免重复释放。
3. 对象树的核心规则
-
父对象管理子对象:父对象析构时,子对象被自动删除。
-
禁止悬空指针:若子对象被手动删除,父对象会更新子列表,避免访问无效内存。
-
跨线程限制:Qt 对象树是线程相关的,父子对象必须在同一线程。
4.核心原则:
- Qt 的对象树机制通过 父子关系 和 自动递归释放,极大简化了内存管理。
- 需要长期存在的对象 → 使用堆对象并指定父对象。
- 短生命周期对象 → 可谨慎使用栈对象,但需确保作用域安全。
- 掌握对象树,是写出高效、无泄漏 Qt 程序的关键!
当然了这只是对于对象树的一个简单的认识,关于对象树后续更深层次的内容,我们会在后面的学习当中了解到的。
五、乱码问题与日志打印
在我们解释了控件对象自动析构后面,那我们又要考虑一个问题了。
那就是我们在打印到终端的时候,我们的字符变成了乱码,不过这个问题对于有过学习数据库经验的同学来说肯定是是很简单的吗。一看就是打印和显示的字符集对不上。
那要怎么办呢?
经过调查发现还行QT的终端是无法改变字符集的,那要我们去改变我们的cout的字符集设定,这显然是不可能的。
所以在QT当中为了解决,我们的乱码问题,特别提供了一个类<QDebug>,使用这个类的方法就会自动为用户解决字符集不一样的问题。
当然了,使用qDubug的好处还有一个。
总结:
通过本讲的学习,你已经能够独立完成简单的Qt界面程序,并初步理解了对象树的优点。接下来,在下一讲中,我们进一步探索以下内容:
-
控件进阶实践:通过按钮的分步实现(如多状态交互)完善“HelloWorld”功能;
-
编码规范:学习Qt推荐的变量命名风格(如小驼峰命名法),提升代码可维护性;
-
高效开发技巧:掌握Qt官方文档的查询方法,快速定位类与函数的用法;
-
界面布局基础:理解Qt坐标系系统,精准控制控件的位置与尺寸。
下一讲将为你揭开Qt开发的更多细节,助你从“能用”迈向“精通”。准备好迎接更复杂的界面设计与功能实现吧!
下面给出本讲的思维导图: