QT5初级奥义(一)

QT5初级奥义(一)

这些内容主要转载的是QT学习之路2,转载主要是加深自己的理解,也算是学习的一个过程记录吧。在这里感谢豆子大大的无私奉献!

豆子大大博客链接:QT学习之路2



前言

至于QT5的安装流程这里就不介绍了,网上有挺多安装教程的,这里就只是放出下载链接。
下载链接:QT5.15下载

HelloWorld 世界,你好

首先,第一次都是helloworld嘛!所以在QT Creator先新建一个工程
在这里插入图片描述
点击这个“新建文件或工程”,在左侧选择项目-Applications,中间选择 Qt Gui 应用,然后点击“选择…”:在这里插入图片描述
在弹出的对话框中填写名称、创建路径等信息:
在这里插入图片描述
点击“下一步”,选择该工程的编译器。这里我们只选择 mingw 调试即可(在以后的项目中,根据自己的需要选择。)Shadow Build 的含义是“影子构建”,即将构建生成的文件不放在源代码文件夹下。这样可以最大地保持源代码文件夹的整洁。
在这里插入图片描述
点击“下一步”,可以选择生成的主窗口文件。不过在我们的简单示例中是不需要这么复杂的窗口的,因此我们尽可能简单地选择,将“创建界面”的选择去除:
在这里插入图片描述
终于到了最后一步。这里是在询问我们是否添加版本控制。对于我们的小项目当然是不需要的,所以选择“无”,然后点击“完成”即可:
在这里插入图片描述
可以看到,Qt Creator 帮助我们在 HelloWorld 项目文件夹下生成了四个文件:main.cpp,mainwindow.cpp,mainwindow.h 和 HelloWorld.pro。pro 文件就是 Qt 工程文件(project file),由 qmake 处理,生成 make 程序所需要的 makefile;main.cpp 里面就是一个main函数,作为应用程序的入口函数;其他两个文件就是先前我们曾经指定的文件名的文件。

我们将 main.cpp 修改如下:

#include <QApplication>
#include <QLabel>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QLabel label("Hello, world");
    label.show();

    return app.exec();
}

点击 Qt Creater 左侧下面的绿色三角按钮即可运行(这里一共有三个按钮,从上到下分别是“运行”、“调试”和“构建”)。如果没有错误的话,就会看到运行结果:
在这里插入图片描述
这个程序有这么几行。我们解释一下。

前两行是 C++ 的 include 语句,这里我们引入的是QApplication以及QLabel这两个类。main()函数中第一句是创建一个QApplication类的实例。对于 Qt 程序来说,main()函数一般以创建 application 对象(GUI 程序是QApplication,非 GUI 程序是QCoreApplication。QApplication实际上是QCoreApplication的子类。)开始,后面才是实际业务的代码。这个对象用于管理 Qt 程序的生命周期,开启事件循环,这一切都是必不可少的。在我们创建了QApplication对象之后,直接创建一个QLabel对象,构造函数赋值“Hello, world”,当然就是能够在QLabel上面显示这行文本。最后调用QLabel的show()函数将其显示出来。main()函数最后,调用app.exec(),开启事件循环。我们现在可以简单地将事件循环理解成一段无限循环。正因为如此,我们在栈上构建了QLabel对象,却能够一直显示在那里(试想,如果不是无限循环,main()函数立刻会退出,QLabel对象当然也就直接析构了)。

示例程序我们已经讲解完毕。下面再说一点。我们可以将上面的程序改写成下面的代码吗?

#include <QApplication>
#include <QLabel>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QLabel *label = new QLabel("Hello, world");
    label->show();

    return app.exec();
}

答案是,不可以建议这样做!

首先,按照标准 C++ 来看这段程序。这里存在着内存泄露。当exec()退出时(也就是事件循环结束的时候。窗口关闭,事件循环就会结束),label 是没办法 delete 的。这就造成了内存泄露。当然,由于程序结束,操作系统会负责回收内存,所以这个问题不会很严重。即便你这样修改了代码再运行,也不会有任何错误。

早期版本的 Qt 可能会有问题(详见本文最后带有删除线的部分,不过豆子也没有测试,只是看到有文章这样介绍),不过在新版本的 Qt 基本不存在问题。在新版本的 Qt 中,app.exec()的实现机制确定,当最后一个可视组件关闭之后,主事件循环(也就是app.exec())才会退出,main()函数结束(此时会销毁app)。这意味着,所有可视元素已经都关闭了,也就不存在后文提到的,QPaintDevice没有QApplication实例这种情况。另外,如果你是显式关闭了QApplication实例,例如调用了qApp->quit()之类的函数,QApplication的最后一个动作将会是关闭所有窗口。所以,即便在这种情况下,也不会出现类这种问题。由于是在main()函数中,当main()函数结束时,操作系统会回收进程所占用的资源,相当于没有内存泄露。不过,这里有一个潜在的问题:操作系统只会粗暴地释放掉所占内存,并不会调用对象的析构函数(这与调用delete运算符是不同的),所以,很有可能有些资源占用不会被“正确”释放。事实上,在最新版的 Sailfish OS 上面就有这样的代码:

#include <QApplication>

int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> app(new QApplication(argc, argv));
    QScopedPointer<QQuickView> view(new QQuickView);
    view->setSource("/path/to/main.qml");
    ...
    return app->exec();
}

这段代码不仅在堆上创建组件实例,更是把QApplication本身创建在了堆上。不过,注意,它使用了智能指针,因此我们不需要考虑操作系统直接释放内存导致的资源占用的问题。

当然,允许使用并不一定意味着我们建议这样使用。毕竟,这是种不好的用法(就像我们不推荐利用异常控制业务逻辑一样),因为存在内存泄露。而且对程序维护者也是不好的。所以,我们还是推荐在栈上创建组件。因为要靠人工管理new和delete的出错概率要远大于在栈上的自动控制。除此之外,在堆上和在栈上创建已经没有任何区别。

如果你必须在堆上创建对象,不妨增加一句:

label->setAttribute(Qt::WA_DeleteOnClose);

这点提示足够告诉程序维护者,你已经考虑到内存问题了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值