Qt 概述

1. Qlabel HelloWorld 程序

使用纯代码实现

cpp 复制代码
// widget.cpp
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 给当前这个lable对象,指定一个父对象
    QLabel* label = new QLabel(this);
    // C语言风格的字符串可以直接进行隐式类型转换成QString
    label->setText("Hello World");
    // same as label->setText(QString("Hello World"));
}
Widget::~Widget()
{
    delete ui;
}

2. 对象树

2.1 概念和注意事项

看到上面的第8行,在堆上new了一个对象,但是在析构函数中并没有delete,这样不会出现问题,因为Qt中有对象树的概念

  • QObject是以对象树的形式组织起来的。
    • 当创建⼀个QObject对象时,会看到QObject的构造函数接收⼀个QObject指针作为参数,这个参数就是parent,也就是⽗对象指针。
    • 这相当于,在创建QObject对象时,可以提供⼀个其⽗对象,我们创建的这个QObject对象会自动添加到其⽗对象的children()列表。
    • 当⽗对象析构的时候,这个列表中的所有对象也会被析构。(注意,这⾥的⽗对象并不是继承意义上的⽗类!而是一个指针)
  • QWidget是能够在屏幕上显⽰的⼀切组件的⽗类。
    • QWidget继承⾃QObject,因此也继承了这种对象树关系。⼀个孩⼦⾃动地成为⽗组件的⼀个⼦组件。因此,它会显⽰在⽗组件的坐标系统中,被⽗组件的边界剪裁。例如,当⽤⼾关闭⼀个对话框的时候,应⽤程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该⼀起被删除。事实就是如此,因为这些都是对话框的⼦组件
    • 当然,我们也可以⾃⼰删除⼦对象,它们会⾃动从其⽗对象列表中删除。⽐如,当我们删除了⼀个⼯具栏时,其所在的主窗⼝会⾃动将该⼯具栏从其⼦对象列表中删除,并且⾃动调整屏幕显⽰
  • Qt引⼊对象树的概念,在⼀定程度上解决了内存问题
    • 当⼀个QObject对象在堆上创建的时候,Qt会同时为其创建⼀个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的
    • 任何对象树中的QObject对象delete的时候,如果这个对象有parent,则⾃动将其从parent的children()列表中删除;如果有孩⼦,则⾃动delete每⼀个孩⼦。Qt保证没有QObject会被delete两次,这是由析构顺序决定的

如果QObject在栈上创建,Qt保持同样的⾏为。正常情况下,这也不会发⽣什么问题。但是要注意顺序

cpp 复制代码
QWidget window;
QPushButton quit("quit", &window);

作为⽗组件的window和作为⼦组件的quit都是QObject的⼦类。这段代码是正确的,quit的析构函数不会被调⽤两次,因为标准C++要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作⽤域时,会先调⽤quit的析构函数,将其从⽗对象window的⼦对象列表中删除,然后才会再调⽤ window的析构函数。 如果改成下面这样

cpp 复制代码
QPushButton quit("quit");
QWidget window;
quit.setParent(&window);

在上⾯的代码中,作为⽗对象的window会⾸先被析构,因为它是最后⼀个创建的对象。在析构过程中,它会调⽤⼦对象列表中每⼀个对象的析构函数,也就是说,quit此时就被析构了。然后,代码继续执⾏,在window析构之后,quit也会被析构,因为quit也是⼀个局部变量,在超出作⽤域的时候当然也需要析构。但是,这时候已经是第⼆次调⽤quit的析构函数了,C++不允许调⽤两次析构函数,因此,程序崩溃了


**所以,在Qt中,尽量在构造的时候就指定parent对象,并且大胆在堆上创建。 **

2.2 看到析构函数执行

自己定义一个类,继承QLabel

cpp 复制代码
// myqlabel.h
class MyQLabel : public QLabel
{
public:
    // 构造函数需要使用QWidget *版本
    // 这样才能确保咱自己写的构造函数能够加到对象树上
    MyQLabel(QWidget* parent);
    ~MyQLabel();
};

// myqlabel.cpp
// 调用QLabel类的构造函数,将MyQLabel挂到对象树上
MyQLabel::MyQLabel(QWidget *parent) : QLabel(parent)
{
    std::cout << "MyQLabel(QWidget *parent)" << std::endl;
}

MyQLabel::~MyQLabel()
{
    std::cout<<"~MyQLabel()"<<std::endl;
}

// widget.cpp
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 这里使用自己定义的Mylabel代替原来的 Qlabel, 虽然写的是"继承",本质上是拓展
    // 保持原有功能不变的基础下,打印日志,方便观察结果
    MyQLabel* label = new MyQLabel(this);
    label->setText("Hello World");
}
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 这里使用自己定义的Mylabel代替原来的 Qlabel, 虽然写的是"继承",本质上是拓展
    // 保持原有功能不变的基础下,打印日志,方便观察结果
    MyQLabel* label = new MyQLabel(this);
    label->setText("Hello World");
}
Widget::~Widget()
{
    delete ui;
}

可以看到,自动调用了析构函数,所以挂到了对象树上,就无需担心内存泄露问题

3. QLineEdit Hello World

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QLineEdit* edit = new QLineEdit(this);
    edit->setText("Hello World");
}

可以看到,与Qlabel 差别不大

4. Qt中的坐标系

坐标体系:以左上⻆为原点(0,0),X向右增加,Y向下增加。

对于嵌套窗⼝,其坐标是**相对于⽗窗⼝**原点来说的


cpp 复制代码
 QPushButton* b1 = new QPushButton(this);
QPushButton* b2 = new QPushButton(this);
b1->setText("按钮1");
b2->setText("按钮2");
b2->move(200,300);          // b2按钮move到(200,300)位置,相对于Widget来说
相关推荐
Humbunklung4 分钟前
Rust枚举:让数据类型告别单调乏味
开发语言·后端·rust
Y1nhl7 分钟前
力扣_链表_python版本
开发语言·python·算法·leetcode·链表·职场和发展
OEC小胖胖20 分钟前
深入理解 Vue.js 响应式原理及其在 Web 前端开发中的应用
开发语言·前端·javascript·vue.js·web
qq_4017004123 分钟前
C语言中位运算以及获取低8位和高8位、高低位合并
c语言·开发语言·算法
yanjiaweiya27 分钟前
云原生-集群管理
java·开发语言·云原生
qianbo_insist1 小时前
c++ python 共享内存
开发语言·c++·python
今天背单词了吗9801 小时前
算法学习笔记:8.Bellman-Ford 算法——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·开发语言·后端·算法·最短路径问题
CoderPractice1 小时前
C#控制台小项目-飞行棋
开发语言·c#·小游戏·飞行棋
Coding小公仔2 小时前
LeetCode 151. 反转字符串中的单词
开发语言·c++·算法