☰
【Qt 入门】第一个 Hello World 程序
2026/10/8 23:47:09 网站建设 项目流程

🔥 个人主页: Mercury
🍉 学习方向: C/C++方向学习者
⭐ 人生格言:给时光以生命,而不是给生命以时光


​


📑 目录

  • 一、新建项目
  • 二、项目代码介绍
    • 2.1 main.cpp
    • 2.2 widget.h
    • 2.3 widget.cpp
    • 2.4 widget.ui
    • 2.5 .pro 工程文件
    • 2.6 build 构建目录
  • 三、编写一个 Hello World 程序
    • 3.1 图形化方式
    • 3.2 纯代码方式
  • 四、关于程序内存泄漏
  • 五、验证对象自动释放
  • 总结

一、新建项目

先新建一个项目,进行简单命名后一直下一步即可。这里的Base class(基类)先选择 QWidget:

创建好之后,Qt 就会自动生成一些 C++ 头文件和源文件:


二、项目代码介绍

2.1 main.cpp

作为一个 C++ 程序,Qt 也一样有 main 入口函数。这里的 main 函数不用我们编写,Qt 会自动生成,一般我们也不用做什么修改。

让我们简单看看 Qt 在 main 函数内做了哪些事:

  1. 首先创建了一个QApplication 对象,这是编写一个 Qt 图形化界面程序一定要有的对象;
  2. 创建了一个Widget(控件)对象,就是我们刚才创建项目时选择的类名,它的父类是 QWidget;
  3. 调用 Widget 对象的show 方法让控件显示出来,show 继承自父类 QWidget;
  4. 调用a.exec()让程序跑起来。
💡Qt 程序的启动流程
创建 QApplication(管理整个 GUI 程序)→ 创建主窗口控件 → 调用show()显示窗口 → 调用exec()启动事件循环,程序开始运行并等待用户操作。QApplication 简单认识即可,一般很少直接操作它。

2.2 widget.h

我们主要看 Widget 类的定义:

这个头文件里有几个关键点,我们分开来看。

① 继承 QWidget

Widget 类继承了 QWidget,这是创建项目时选择的父类。QWidget 是 Qt SDK 内置的类,想使用它就要包含相应头文件。

💡Qt 类的头文件规则
使用 Qt 内置类时,包含的头文件名与类名一致。比如用 QWidget 就#include <QWidget>,用 QLabel 就#include <QLabel>,非常好记。

② Q_OBJECT 宏

💡Q_OBJECT 宏
宏在 C++ 中本质就是文本替换。Q_OBJECT与 Qt 的核心机制信号槽有关,现在只需认识它,后面了解。

③ 构造函数、析构函数与 parent 参数

再往下是类的构造函数和析构函数。构造函数有个很关键的参数parent。

💡parent 参数与对象树
parent用来指定父对象,把当前 Qt 对象挂到"对象树"上统一管理(对象树后面详细讲)。这是 Qt 管理对象、自动释放内存的关键。

④ 成员变量 ui

最后有个成员变量ui,它与 form file(界面文件)有关,我们一会儿结合 ui 文件再做介绍:

2.3 widget.cpp

widget.cpp 比较简单,如图简单了解即可:

接下来,我们了解 Qt 的一个关键要点——form file。

2.4 widget.ui

这个就是上文一直提到的 form file,是一个.ui后缀的文件。

1. 双击打开图形化编辑器

直接双击 ui 文件,Qt Creator 会调用 Qt Designer 打开它,呈现一个图形化的界面编辑器:

  • 左栏:一系列 Qt 内置控件,把控件拖拽到程序窗口中,就能创建出具体界面;
  • 右下角:控件对应的属性,会影响控件的各种行为,后续再学习。

2. 查看 ui 文件的本体

点击左侧边栏的"编辑"按钮,显示的就是.ui文件的本体:

关于这个文件,要搞清楚三件事:

  • 它是什么:一种叫XML的标签语言,对这些< >、</ >应该不陌生,xml 和 html 很相似;
  • 和 HTML 的区别:xml 有哪些标签、各自什么含义,都由程序员自定义;而 html 的标签是标准委员会固定的。这里的标签是开发 Qt 的大佬们约定好的;
  • 有什么用:用来描述程序界面长什么样。
💡ui 文件的本质
ui 文件本质是一个描述界面的XML 文件(类似网络编程里的自定义应用层协议)。构建时,qmake 会调用相关工具,依据这个 xml 生成一批 C++ 代码,最终把完整界面构建出来。这些标签的具体含义不用记。

2.5 .pro 工程文件

.pro是 Qt 项目的工程文件,也是 qmake 工具构建时的重要依据:

💡.pro 工程文件
它是 qmake 构建的依据,作用类似 CMake 的CMakeLists.txt。

2.6 build 构建目录

上面看到的.h、.cpp、.pro、.ui都是源代码。编译运行 Qt 项目时,构建过程会生成一些临时文件。

💡build 构建目录
构建产生的临时文件,存放在与项目同级的一个build-xxxxxx目录下,源码和构建产物是分开的。

让我们看看其中的ui_widget.h,它就是根据 ui 文件生成的 C++ 代码:

阅读完以上代码,相信大家对 Widget 类也有了更全面一点的了解。文件的其他细节涉及后续学习内容,不过多关注。

build 目录下还有debug 和 release 两个目录,里面存放着我们熟悉的编译产物:

  • .exe:最终生成的可执行程序;
  • .o:编译产生的目标文件。

看到这里,恭喜你已经了解了一个 Qt 项目的完整全貌。接下来我们着手编写第一个 Qt 项目。


三、编写一个 Hello World 程序

Hello World 有两种实现方式:

  1. 图形化方式:在界面上创建一个控件,显示 Hello World;
  2. 纯代码方式:编写代码在界面上创建控件,显示 Hello World。

3.1 图形化方式

第 1 步:新建一个项目,在左侧文件栏双击打开 ui 文件,进入图形化界面

第 2 步:在控件栏拉到最下方,找到Display Widgets,选择Label拖拽到窗口中:

第 3 步:选中这个 Label,直接修改它的文本,Hello World 程序就大功告成啦。

第 4 步:点击左下角运行按钮,可以看到显示 Hello World 的窗口:

3.2 纯代码方式

首先打开widget.cpp:

💡代码写在哪里
用代码构造界面时,通常把构造界面的代码放进Widget / MainWindow 的构造函数中。

第 1 步:创建 label 对象

label 在 Qt 中的类型为QLabel,这里用 new 在堆上创建,并传入this:

💡为什么用 new(堆)而不是栈
•栈上创建:对象一出 Widget 构造函数的作用域就销毁了,运行时窗口上什么都看不到;
•堆上创建(new):对象不会随作用域销毁,才能一直显示在窗口上;
• 构造时传入this:指定当前 Widget 为父对象,把控件挂到对象树上管理。

第 2 步:包含头文件

此时编译器还不认识 QLabel,包含同名头文件#include <QLabel>即可(头文件名 = 类名,前面讲过)。

第 3 步:设置文本

调用 QLabel 对象的setText方法,参数给"Hello World"。眼尖的小伙伴会发现,参数提示的是一个QString类型:

QString又是什么呢?这就要从 Qt 的历史讲起了:

  • 背景:Qt 诞生于 1991 年,那时 C++ 还没有形成标准、更没有"标准库",表示字符串只能用 C 风格字符串或 C++ 的 string,都不太好用;
  • Qt 的方案:Qt 自己发明了一套轮子,搞了一系列基础类来支持开发;
  • 现状:多年后标准库已成熟,但 Qt 原生接口用的都是 Qt 自己这套容器,这些类也删不掉,只能和标准库共存。
💡Qt 的基础容器类
Qt 自己封装了一套基础类:字符串QString、动态数组QVector、链表QList、字典QMap等。Qt 原生接口都用它们,开发时更推荐使用 Qt 这套,当然也提供了与标准库互相转换的方法。
其中QString 比 std::string 好用亿点:内部实现了编码转换,不会像标准库 string 一样出现中文乱码。

完整代码如下:

#include"widget.h"#include"ui_widget.h"#include<QLabel>Widget::Widget(QWidget*parent):QWidget(parent),ui(newUi::Widget){ui->setupUi(this);QLabel*lable=newQLabel(this);//QLabel lable;lable->setText("Hello World");// 隐式构造 QString}Widget::~Widget(){deleteui;}

运行就可以看到左上角显示了 Hello World,关于位置的设定后续也会学习到:


四、关于程序内存泄漏

作为一个 C/C++ 程序员,防止内存泄漏是要融入 DNA 的。内存泄漏、文件描述符泄漏都非常严重,而且不容易第一时间发现。

这里自然会冒出一个疑问:

  • 疑问:代码 new 了对象之后,咋没有 delete 呢?不 delete 不就内存泄漏了吗?
  • 结论:上述代码在 Qt 中不会产生内存泄漏,label 对象会在合适的时候被析构释放。

为什么能自动释放?关键就在于对象被挂到了对象树上:

  • 类比 DOM:网页开发里也有类似的对象树(DOM),本质是一棵 N 叉树,把界面上的各种元素组织起来;
  • Qt 的对象树:同样是一棵 N 叉树,把界面上的各种控件组织起来;
  • 核心目的:在合适的时机(窗口关闭时)把这些对象统一释放。
💡对象树机制(重点)
Qt 用一棵N 叉树(对象树)组织界面上的控件。创建对象时通过parent指定父对象、挂到树上;当窗口关闭时,树上的对象会被统一析构释放。所以 new 之后即使不手动 delete,也不会内存泄漏。


五、验证对象自动释放

怎么验证对象真的被释放了呢?最直接的办法是:在对象析构时打印日志。但 Qt 内置类我们改不了,于是有了下面的思路:

💡验证思路
Qt 内置类无法修改,就继承一个子类、重写它的析构函数,在保留原有功能的情况下加入日志打印。

第 1 步:新建 C++ class

创建一个自定义类继承 QLabel,在新建项目这里选择C++ class:

第 2 步:编写 mylabel.h

Qt 不会自动包含头文件、不认识 QLabel,手动把头文件包含上,并完善构造、析构函数的声明:

#include<QLabel>classMyLabel:publicQLabel{public:MyLabel(QWidget*parent);~MyLabel();};

第 3 步:编写 mylabel.cpp

重写析构函数,构造函数也要初始化基类 QLabel:

#include"mylabel.h"//#include<iostream>#include<QDebug>MyLabel::MyLabel(QWidget*parent):QLabel(parent){}MyLabel::~MyLabel(){//std::cout << "label被销毁!" <<std::endl; 编码错误出现乱码qDebug()<<"label被销毁!";// 用 Qt 封装的 qDebug 打印,避免输出乱码,头文件 <QDebug>}
💡为什么用 qDebug 而不是 std::cout
用std::cout输出中文容易出现乱码;Qt 封装的qDebug能正确处理编码,需包含#include <QDebug>。

第 4 步:替换并运行

把原来的 QLabel 换成自定义的 MyLabel:

MyLabel*lable=newMyLabel(this);

关闭程序时,就会打印我们的日志,印证了对象树会自动释放对象:


总结

本文完成了第一个 Qt 程序,并理清了 Qt 项目的整体结构和对象树机制:

  • 项目结构:main.cpp是入口(QApplication → 主窗口 → show → exec);widget.h/.cpp定义主窗口类,Q_OBJECT与信号槽有关,parent用于挂对象树;widget.ui本质是描述界面的 XML,qmake 据此生成 C++ 代码;.pro是 qmake 工程文件(类似 CMakeLists.txt);build目录存放构建临时文件、ui_widget.h以及 debug/release 下的.exe、.o;
  • 两种 Hello World 实现:图形化方式在 ui 里拖 Label 改文本;纯代码方式在构造函数里new QLabel(this)、包含同名头文件、调用setText;
  • Qt 容器:Qt 封装了 QString、QVector、QList、QMap 等一套基础类,Qt 接口都用它们、更推荐使用,QString 内置编码转换、能避免中文乱码;
  • 对象树:new 出的控件通过 parent 挂树,窗口关闭时被统一释放,因此不会内存泄漏;我们通过继承 QLabel、重写析构、qDebug 打印日志,亲手验证了这一点。

下一篇我们会在此基础上继续学习 Qt 的更多控件和机制。

如果对你有帮助,欢迎点赞收藏关注~ 有问题欢迎在评论区交流👏


需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询