1. 本有项目基础结构
1.1 项目文件(.pro)
在创建的Qt项目中自动生成了一个后缀为 .pro 的项目文件,该文件中记录着项目的一些属性信息,具体信息如下:
# 在项目文件中, 注释需要使用 井号(#) # 项目编译的时候需要加载哪些底层模块 QT += core gui # 如果当前Qt版本大于4, 会添加一个额外的模块: widgets # Qt 5中对gui模块进行了拆分, 将 widgets 独立出来了 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets # 使用c++11新特性 CONFIG += c++11 #如果在项目中调用了废弃的函数, 项目编译的时候会有警告的提示 DEFINES += QT_DEPRECATED_WARNINGS # 项目中的源文件 SOURCES += \ main.cpp \ mainwindow.cpp # 项目中的头文件 HEADERS += \ mainwindow.h # 项目中的窗口界面文件 FORMS += \ mainwindow.ui
2. main.cpp
在这个源文件中有程序的入口函数
main()
,介绍下这个文件中自动生成的几行代码:
#include "mainwindow.h" // 生成的窗口类头文件 #include <QApplication> // 应用程序类头文件 int main(int argc, char *argv[]) { // 创建应用程序对象, 在一个Qt项目中实例对象有且仅有一个 // 类的作用: 检测触发的事件, 进行事件循环并处理 QApplication a(argc, argv); // 创建窗口类对象 MainWindow w; // 显示窗口 w.show(); // 应用程序对象开始事件循环, 保证应用程序不退出 return a.exec(); }
1.3 mainwindow.ui
在Qt中每一个窗口都对应一个可编辑的可视化界面(*.ui), 这个界面对应的是一个xml格式的文件, 一般情况下不需要在xml格式下对这个文件进行编辑, 关于这个文件结构了解即可。
<!-- 双击这个文件看到的是一个窗口界面, 如果使用文本编辑器打开看到的是一个XML格式的文件--> <!-- 看不懂这种格式没关系, 我们不需要在这种模式下操作这个文件。 --> <!-- 这里只是给大家介绍这个文件的本质 --> <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"/> <widget class="QMenuBar" name="menubar"/> <widget class="QStatusBar" name="statusbar"/> </widget> <resources/> <connections/> </ui>
1.4 mainwindow.h
这个文件是窗口界面对应的类的头文件。
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> // Qt标准窗口类头文件 QT_BEGIN_NAMESPACE // mainwindow.ui 文件中也有一个类叫 MainWindow, 将这个类放到命名空间 Ui 中 namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT // 这个宏是为了能够使用Qt中的信号槽机制 public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; // 定义指针指向窗口的 UI 对象 }; #endif // MAINWINDOW_H
1.5 mainwindow.cpp
这个文件是窗口界面对应的类的源文件。
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) // 基于mainwindow.ui创建一个实例对象 { // 将 mainwindow.ui 的实例对象和 当前类的对象进行关联 // 这样同名的连个类对象就产生了关联, 合二为一了 ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; }
2. Qt中的窗口类
在通过Qt向导窗口基于窗口的应用程序的项目过程中倒数第二步选择跟随项目创建的第一个窗口的基类, 下拉菜单有三个选项, 分别为:
QMainWindow
、QDialog
、QWidget
如下图:
2.1基础窗口类
- 常用的窗口类有3个
- 在创建Qt窗口的时候, 需要让自己的窗口类继承上述三个窗口类的其中一个
- QWidget
- 所有窗口类的基类
- Qt中的控件(按钮, 输入框, 单选框…)也属于窗口, 基类都是QWidget
- 可以内嵌到其他窗口中: 没有边框
- 可以不内嵌单独显示: 独立的窗口, 有边框
- QDialog
- 对话框类, 后边的章节会具体介绍这个窗口
- 不能内嵌到其他窗口中
- QMainWindow
- 有工具栏, 状态栏, 菜单栏, 后边的章节会具体介绍这个窗口
- 不能内嵌到其他窗口中
2.2 窗口的显示
- 内嵌窗口
- 依附于某一个大的窗口, 作为了大窗口的一部分
- 大窗口就是这个内嵌窗口的父窗口
- 父窗口显示的时候, 内嵌的窗口也就被显示出来了
- 不内嵌窗口
- 这类窗口有边框, 有标题栏
- 需要调用函数才可以显示
// QWidget是所有窗口类的基类, 调用这个提供的 show() 方法就可以显示将任何窗口显示出来 // 非模态显示 void QWidget::show(); // 显示当前窗口和它的子窗口 // 对话框窗口的非模态显示: 还是调用show() 方法 // 对话框窗口的模态显示 [virtual slot] int QDialog::exec();
3. 内存回收
在Qt中创建对象的时候会提供一个
Parent对象指针
(可以查看类的构造函数),parent是干什么的? . QObject是以对象树的形式组织起来的。当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是parent,也就是父对象指针。
这相当于,在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表。当父对象析构的时候,这个列表中的所有对象也会被析构。(这里的父对象并不是继承意义上的父类
)QWidget是能够在屏幕上显示的一切组件的父类。QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。 Qt 引入对象树的概念,在一定程度上解决了内存问题。
- 当一个QObject对象在堆上创建的时候,Qt会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。
- 任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete两次,这是由析构顺序决定的。
综上所述, 我们可以得到一个结论: Qt中有内存回收机制, 但是不是所有被new出的对象被自动回收, 满足条件才可以回收
, 如果想要在Qt中实现内存的自动回收, 需要满足以下两个条件:
- 创建的对象必须是QObject类的子类(间接子类也可以)
- QObject类是没有父类的, Qt中有很大一部分类都是从这个类派生出去的
- Qt中使用频率很高的窗口类和控件都是 QObject 的直接或间接的子类
- 其他的类可以自己查阅Qt帮助文档
- QObject类是没有父类的, Qt中有很大一部分类都是从这个类派生出去的
- 创建出的类对象, 必须要指定其父对象是谁, 一般情况下有两种操作方式:
// 方式1: 通过构造函数 // parent: 当前窗口的父对象, 找构造函数中的 parent 参数即可 QWidget::QWidget(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()); QTimer::QTimer(QObject *parent = nullptr); // 方式2: 通过setParent()方法 // 假设这个控件没有在构造的时候指定符对象, 可以调用QWidget的api指定父窗口对象 void QWidget::setParent(QWidget *parent); void QObject::setParent(QObject *parent);