Qt 学起来挺有意思的,但初学者往往会被一些细节绕晕。
在上一篇博客里,我们讲了 Qt 的环境搭建和基础概念,这篇我们直接来动手做一个最简单的HelloWorld,顺便把控件的生命周期、堆栈分配、QString 这些关键点顺便梳理一下。
一、HelloWorld 的两种实现方式
在 Qt 中,实现 HelloWorld,其实就是在窗口上显示一段文本。
有两种方式:
- 图形化方式:使用 Qt Designer(拖控件、设置属性)
- 纯代码方式:直接写 C++ 代码创建控件
两种方式最终效果一样,底层都是创建控件对象,只不过 Designer 帮你自动生成了代码而已。
二、方式一:Qt Designer(可视化方式)
1. 打开 UI 设计界面
双击widget.ui,Qt Creator 会进入Qt Designer 模式,看到的是可视化窗口,而不是代码。
2. 拖拽 Label 控件显示 HelloWorld
- 在左侧控件栏找到
Label - 拖动到设计窗口
- 拖拽控件边缘或右下角的小方块,可以改变大小
- 在右侧属性编辑器中,把
text属性改为"Hello World"
这样,界面就完成了。
3. Qt Designer 的对象树
右上角有一个对象树(Object Inspector),显示当前界面所有控件的层级关系:
- 顶层是窗口(Widget)
- 子节点是 Label 或按钮
- 层级关系 = 运行时的父子关系
也就是说 Designer 里看到的结构,运行时会一模一样。
4. 拖拽控件背后的机制
拖控件后,.ui文件会新增一段 XML 代码,每个控件都有<widget>标签,记录控件类型、大小、位置等。
编译时,qmake 会调用 uic 工具,把.ui文件生成对应的ui_widget.hC++ 代码。
运行时,这些代码帮我们构建界面,完全自动化。
5. ui_widget.h 中的真实代码
打开ui_widget.h可以看到类似:
label=newQLabel(Widget);label->setObjectName(QString::fromUtf8("label"));label->setGeometry(QRect(220,120,171,91));new QLabel(Widget)→ 创建控件并指定父对象setObjectName→ 给控件命名,用于查找或样式匹配setGeometry→ 控件位置和大小
这说明:
Qt Designer 并不是魔法,它只是帮你写了这些 C++ 代码。
三、方式二:纯代码方式
1. 构造函数中创建控件
纯代码方式通常把控件创建写在Widget 的构造函数里:
QLabel*label=newQLabel(this);label->setText("Hello World");原因很简单:
- 构造函数执行时,窗口对象已经存在
- 可以安全使用
this作为父对象 - 控件会被挂到对象树上,生命周期由 Qt 管理
不放在构造函数里,会很难保证控件能正常显示。
2. setText 的 QString
label->setText("Hello World");setText接收QString类型- QString 是 Qt 自己的字符串类,支持 Unicode
- 可以直接传 C 风格字符串(隐式转换)
当初 Qt 诞生时,C++ 还没标准库,使用起来并不方便,为了更好的开发,Qt 自己造了这一套轮子:QString、QList、QVector、QMap……
即便现在标准库成熟,在开发中可以使用标准库类型,但这些类依然存在,因为他们与 Qt 的框架有着深度绑定,不可能删去。而且这些类型也是蛮好用的,例如QString内部对字符串编码进行了处理。
在 Qt 原生 API 中,优先使用 Qt 类型,比如 QString,比 std::string 更好用。
3. 控件默认显示在左上角
如果没有手动设置位置或使用布局管理器:
QLabel*label=newQLabel(this);- 控件会默认出现在
(0,0)左上角 - 可以用
move()或setGeometry()指定位置 - 或者使用布局管理器(推荐):
QVBoxLayout*layout=newQVBoxLayout(this);layout->addWidget(label);布局管理器会自动处理位置和大小,响应窗口缩放。
4. 为什么控件要放在堆上
QLabel*label=newQLabel(this);- 如果写成栈对象:
QLabellabel(this);- 构造函数结束后,控件就被销毁了
- 窗口显示时控件已经不存在
堆对象 + parent 指定,可以保证控件在父窗口生命周期内一直存在。
栈对象仅适合临时控件或数据对象,GUI 控件不适合。
5. 为什么没有 delete 也不会内存泄漏
很多人会疑惑:
QLabel*label=newQLabel(this);没有 delete,会不会泄漏?
- 不会
- 因为我们把 label 挂到了对象树
- 父窗口析构时,Qt 会自动释放所有子对象
- 对象树管理生命周期,是 Qt 内存管理的重要机制
四、小结
- Qt Designer:拖控件、属性编辑,背后生成 XML → C++ 代码
- 纯代码方式:手动 new 控件,设置 text、几何、布局
- 控件内存管理:父对象 + 对象树 → 自动释放
- QString:Qt 自己的字符串类,跨平台、Unicode 支持
- 堆 vs 栈:控件必须堆分配,否则生命周期过短
这就是一个最小可运行的 Qt HelloWorld背后的完整逻辑。
两种方式最终效果一样,只是你选择用 Designer 还是纯代码,看你习惯和场景。
↖(ω)↗↖(ω)↗↖(ω)↗