一、项目文件解析
1.1 .pro 工程文件(qmake 配置)
以 QtFirst.pro 为例:
QT += core gui # 1. 引入 core 和 gui 模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets # 2. Qt5+ 需要 widgets 模块
TARGET = QtFirst # 3. 生成的可执行文件名(.exe)
TEMPLATE = app # 4. 应用程序模板(非库)
SOURCES += main.cpp widget.cpp # 5. 所有源文件
HEADERS += widget.h # 6. 所有头文件
FORMS += widget.ui # 7. 界面设计文件
CONFIG += c++11 # 8. 使用C++11标准
-
QT += core gui:告诉qmake链接Qt的Core和GUI模块。Core提供非图形功能(如事件循环、容器),GUI提供窗口基础类。 -
greaterThan(...):如果Qt主版本号大于4(即5或6),则增加widgets模块。因为Qt5将QWidget移到了独立的widgets模块中。 -
TARGET:最终生成的程序名(Windows下为QtFirst.exe)。 -
TEMPLATE = app:生成应用程序的makefile。其他选项如lib(库)、subdirs(多子项目)。 -
SOURCES/HEADERS/FORMS:列出所有源代码、头文件、UI文件。\是换行符。 -
CONFIG += c++11:启用C++11特性(如nullptr、auto等)。
1.2 widget.h 头文件(类声明)
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget> // 父类头文件
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; } // 前置声明UI命名空间中的Widget类
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT // ① 宏,启用信号与槽机制(必须)
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui; // ② 指向界面设计生成的类的指针
};
#endif
-
Q_OBJECT:这是一个必须放在类私有区域开头的宏。它让Qt的元对象系统(moc)工作,提供信号/槽、动态属性等功能。没有它,你无法使用信号槽 。 -
namespace Ui { class Widget; }:widget.ui会被编译成一个Ui::Widget类,包含所有控件和setupUi函数。这里只是前置声明,避免包含整个生成的ui_widget.h。 -
Ui::Widget *ui:指针,用来访问界面上的所有控件。例如在widget.cpp中写ui->pushButton->setText("OK")。
1.3 main.cpp 入口文件(程序起点)
#include "widget.h"
#include <QApplication> // 应用程序类
int main(int argc, char *argv[])
{
QApplication a(argc, argv); // ① 应用程序对象,每个Qt GUI程序只有一个
Widget w; // ② 创建窗口对象(栈上)
w.show(); // ③ 显示窗口(默认隐藏)
return a.exec(); // ④ 进入事件循环,程序阻塞在此,直到窗口关闭
}
-
QApplication a(argc, argv):管理全局事件循环、资源、命令行参数。必须在创建任何窗口对象之前定义。 -
Widget w:在栈 上创建窗口对象。为什么这里用栈?因为main函数结束前程序不会退出,窗口一直存在。但如果窗口内部有子控件,子控件必须在堆上创建并指定父对象 -
w.show():默认窗口是隐藏的,必须调用show()才能显示。 -
a.exec():启动Qt的事件循环。程序会停在这里,等待用户点击、键盘输入等。当最后一个窗口关闭时,exec()返回,程序结束。
1.4 widget.cpp 实现文件(核心逻辑)
#include "widget.h"
#include "ui_widget.h" // 自动生成的UI类头文件
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget) // 初始化UI指针
{
ui->setupUi(this); // 构建所有界面控件,建立它们与this的父子关系
}
Widget::~Widget()
{
delete ui; // 释放UI对象(它会自动删除所有界面控件)
}
-
ui(new Ui::Widget):在成员初始化列表中创建UI对象。 -
ui->setupUi(this):这个函数会根据widget.ui的XML描述,动态生成所有控件(按钮、标签等),并设置this为它们的父对象。如果你手动编写控件代码,可以不用UI设计器,但两者通常不同时混用。 -
delete ui:析构时释放UI对象。由于UI对象内部持有的控件都是this的孩子**,孩子会在this(窗口)析构时自动被删除,但ui本身是堆分配的需要手动delete。**
1.5 widget.ui 界面文件(XML结构)
这是一个XML文件,由Qt Designer自动维护。你永远不需要手动编辑。示例片段:
<ui version="4.0">
<class>Widget</class>
<widget class="QWidget" name="Widget">
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Hello World</string>
</property>
</widget>
</widget>
</ui>
Qt的uic工具会把它编译成ui_widget.h,其中包含一个Ui_Widget类,里面定义所有控件和setupUi方法。这就是为什么widget.cpp中要包含ui_widget.h。
二、QT Hello World 程序
两种方式,实现hello world
- 通过图形化的方式,在界面上创建除一个控件,显示hello world
- 通过纯代码的方式,通过编写代码,在界面上创建空间,显示hello world
2.1 通过图形化方式创建

拖拽控件到 ui页面窗口并修改内容;


转到编辑区 -> widget.ui文件


为我们弹出这个窗口

2.2 通过代码方式创建



label : 标签 --> 界面上一个用来显示内容的字符串控件
创建对象的时候,可以在堆上创建【上面的就是】 , 也可以在栈上创建


2.2.1 QString

已经学了 C++ 标准库的
std::string,为什么 Qt 还要搞一个QString?直接传std::string不行吗?
当你写下第一行 Qt 代码时,IDE 的自动补全可能会加困惑:
QLabel *label = new QLabel(this);
label->setText("Hello Qt"); // 等等,参数类型不是 std::string?
把鼠标悬停在 setText 上,你会看到它的签名是:
void QLabel::setText(const QString &text);
QString ------ 这就是 Qt 世界里的字符串通行证。
Qt 诞生于 1991 年。那个时期:
-
C++ 还没有 ISO 标准(第一个标准 C++98 是 1998 年发布的)
-
没有标准库(STL) 的概念
-
当时的 C++ 编译器五花八门,对语言特性的支持参差不齐

- 很多年以后,上述的容器等内容,已经打磨得很好了,形成了C++标准~
- 因此,咋们开发QT代码的时候,如果需要用到上述容器,可以使用QT自己搞的这一套
- 但是QT原生的api中,涉及到的接口,用的都是QT自己的这一套容器


QString 对应的头文件,**已经被很多QT内置的其他类给间接包含了。**因此一般不需要显示包含QString文件
2.2.2 代码
#include "widget.h"
#include <QPushButton> // 包含按钮头文件
Widget::Widget(QWidget *parent)
: QWidget(parent) // 调用父类构造函数,parent参数用于对象树
{
// 创建一个按钮对象,在堆上分配内存
QPushButton *btn = new QPushButton; // ① 创建按钮
btn->setText("Hello World"); // ② 设置按钮上显示的文字
btn->setParent(this); // ③ 将按钮挂载到当前窗口(指定父对象)
// 注:也可以一步完成:new QPushButton("Hello World", this);
}
逐行解释 :
-
#include <QPushButton>:Qt中每个类都需要单独包含头文件,类名就是文件名(无.h后缀)。 -
QPushButton *btn = new QPushButton;:在堆 上创建按钮对象。为什么不用栈对象(QPushButton btn;)?因为栈对象会在函数结束时自动销毁,导致按钮一闪而过。而堆对象配合对象树机制,会随父窗口一起销毁。 -
setText("Hello World"):设置按钮显示的文本。 -
setParent(this):这一步最关键 。this指向当前Widget窗口。将按钮的父对象设为该窗口后,Qt会把这个按钮加入窗口的"孩子列表"中。当窗口关闭时,按钮会被自动释放,我们不需要手动delete。
最后,由于按钮已经有了父对象,它会在父窗口上显示。运行后就能看到一个带"Hello World"的按钮窗口。

三、内存泄漏问题

关注 内存泄露 是要融入到DNA中的事情。内存泄露是一个非常害怕,非常严重的事情!!(不仅仅是内存泄露,包括文件描述符泄露 等同类问题,都是非常严重的)
而且这种问题不容易第一时间发现!
- 上述代码,QT中不会产生内存泄漏
- label 对象会合适的时候被析构释放 ~(虽然没有手动写delete,但是的的确确可以释放)
- 之所以能够把对象释放掉,主要是因为把这个对象挂到了对象树上

- 使用对象树,把这些内容组织起来,最主要的目的,就是为了能够在合适的时机【窗口关闭/销毁】 , 把这些对象统一进行释放
- 树上的这些对象,统一销毁是最好不过的,如果某个对象提前被销毁,此时会导致对应的控件在页面上不存在了
- 此处通过new的方式创建对象,也是为了把这个对象的生命周期,交给QT对象树来统一管理
- 如果在栈上创建变量 -- 存在提前释放的问题


四、对象树
4.1 概念
在Qt中创建很多对象的时候会提供一个Parent对象指针,下面来解释这个parent到底是干什么的。
- Q0bject是以对象树的形式组织起来的。




4.2 QT对象树

4.3 代码实例
- 创建一个新工程并编译运行,生成如下窗口:

2、选中工程名,鼠标右键 -------> "add new..."(或 "添加新文件" )


3、选择 "choose...",弹出如下界面;

4、点击 "下一步",弹出如下对话框;

5、点击 "完成" 之后,手动创建类的头文件以及源文件会自动添加到目标工程中;

6、修改头文件;

7、编写源文件;

8、编译并运行;

9、当关闭弹出的对话框时,就会自动调用按钮的析构函数;

10、观察析构函数的执行顺序;

五、Qt 编程必备规范与技巧
5.1 命名规范(推荐驼峰法)
| 元素 | 风格 | 示例 |
|---|---|---|
| 类名 | 首字母大写,每个单词首字母大写 | QPushButton, MyMainWindow |
| 函数/变量 | 首字母小写,后续单词首字母大写 | setText(), userAge |
| 常量/宏 | 全大写,下划线分隔 | QT_VERSION, SOCKET_ERROR |
Qt 官方偏爱驼峰,与许多C++标准库的蛇形命名不同,适应即可。
5.2 必记快捷键
| 功能 | 快捷键 |
|---|---|
| 注释/取消注释 | Ctrl + / |
| 运行 | Ctrl + R |
| 编译 | Ctrl + B |
| 字体缩放 | Ctrl + 鼠标滚轮 |
| 查找 | Ctrl + F |
| 整行上下移动 | Ctrl + Shift + ↑/↓ |
| 自动对齐 | Ctrl + I |
| 帮助文档(光标在类/函数上) | F1 |
在.h和.cpp间切换 |
F4 |
| 根据声明生成定义(光标在函数上) | Alt + Enter |
六、乱码问题
6.1 乱码原因
编码不一致





6.2 解决乱码
查看是啥编码:


QDebug:

后续在Qt中,如果想通过打印日志的方式,输出一些调试信息,**都优先使用qDebug。**虽然使用cout 也行,但是cout 对于编码的处理不太好,在windows上容易出现乱码(如果是Linux使用 Qt Creator,一般就没事了,Linux默认的编码一般都是utf8)
使用qDebug , 还有一个好处 --> 打印的调试日志,是可以统一关闭的!!!!
输出的日志,是开发阶段,调试程序的时候,使用的。如果你的程序上线发布,日志的调试信息其实是不希望用户看到的
qDebug可以通过编译开关,来实现一键式关闭~

