QT——第一个项目(HelloWorld)
- 通过控件创建
- 通过代码的方式实现
- 对象树
- 自己观察
我们之前对QT有了一定的了解,今天我们要用QT来写一段经典代码:HelloWorld。如果还没有看过前两次QT初识的小伙伴可以点击这里:
https://blog.csdn.net/qq_67693066/article/details/137194408
https://blog.csdn.net/qq_67693066/article/details/137200742
通过控件创建
QT可以通过拖拉控件的方式来显示HelloWorld,我们首先创建好一个项目:
然后点击下面的ui文件,进入Desginer:
往下滑,找到Lable:
长按并拖拽到上方的灰框中:
就会多出来一个文本框,双击可以编辑它:
然后点击“编辑”:
我们会发现这个ui文件多了很多,这个就是我们刚才的操作。我们可以运行一下:
发现HelloWorld出现了。
通过代码的方式实现
通过代码的方式实现我们要通过构造函数来实现:
点击下方的widget.cpp:
我们要在Widget的构造函数创建一个label标签,到时候通过构造函数,会直接构造一个label,显示在窗口中:
结果QT不认识,我们得首先包一下头文件:
这下没有报错了,我们来设置文本:
运行:
成功。
对象树
如果有同学有留心的,就会发现,我刚刚是new了一个对象,但是我最后没有delete释放它:
这时候会不会产生内存泄漏的问题呢?答案是不会,为啥呢?因为我们这个变量创建在堆上,已经挂到了对象树上了,在堆上的对象会随着对象树的销毁而销毁。
在Qt框架中,对象树(Object Tree)是一种基于QObject类及其子类的内存管理策略和对象组织方式。对象树的概念对于理解和编写Qt应用程序至关重要,特别是在GUI编程和事件处理等方面。
特点概述:
- 对象关联:
- 所有从QObject派生出来的类都可以构成对象树。每个QObject实例都有一个指向其父对象(parent object)的指针,并且维护着一个子对象列表(children list)。
- 内存管理:
- 当创建一个QObject子类实例时,可以传入一个父对象作为构造函数的参数。一旦设置了父对象,该子对象就被添加到父对象的子对象列表中。
- 当父对象被析构时,其所有的子对象也会被自动析构,并释放内存,这样就实现了层级式的内存管理,有效防止内存泄漏。
- 事件处理:
- 事件传播也是沿着对象树进行的,父对象可以拦截和处理其子对象的事件,或者决定是否将事件传递给子对象。
- 信号与槽:
- 通过对象树,Qt的信号与槽机制也能很好地运作,信号可以跨对象树边界连接,方便不同组件之间的通信。
- 布局管理:
- 在图形用户界面(GUI)编程中,窗口部件(widgets)和其他可视组件常常组织成一个对象树,这对于布局管理和整体界面刷新非常有用。
构造和析构顺序:- 对象树的构造是从父对象到子对象自上而下的顺序进行的,即先构造父对象,再构造其子对象。
- 相反,对象树的析构是从子对象到父对象自下而上的顺序进行的,即先析构最底层的子对象,然后逐层向上析构直至父对象。
总之,Qt对象树简化了内存管理,并且为跨对象的交互和事件处理提供了结构化的方式,极大地提高了开发效率和代码的可维护性。
对象树大概就是长成这个样子:
我们可以试试不用new的方式:
就会出现一些奇怪的事:
因为我们这个对象是在栈上创建的,销毁的时间不同步,就会出现这样的问题。
自己观察
我们可以自己写一段程序来观察对象树,我们首先加一个C++ class:
选择新建文件和项目:
创建好之后,会帮我们建立起.h和.cpp文件,之后在头文件里写这些东西:
#ifndef MYLABEL_H
#define MYLABEL_H
#include<QLabel>
#include<iostream>class MyLabel : public QLabel
{
public:MyLabel(QWidget* parent);~MyLabel();
};#endif // MYLABEL_H
在对应的.cpp文件中,写相应的实现:
#include "mylabel.h"MyLabel::MyLabel(QWidget* parent): QLabel(parent)
{}MyLabel::~MyLabel()
{std::cout << "MyLabel has be destoryed" <<std::endl;
}
之后在widget.cpp中换成我们自己写的MyLabel:
之后运行:
之后在终端,我们可以看到析构函数打印的信息:
之所以我们能挂到对象树上,是因为我们的QLabel挂到了QWidget这个父对象: