使用C++代码实现hello world
之前介绍过用图形化界面的方式创建hello world,这里我们使用C++代码的方式再来实现一次hello world。
如上,首先要先包含一个头文件。
在QT这里,每一个类都有一个对应的同名头文件。比如这里我就包含了 <QLabel>
不过,在调用 setText函数的时候
发现这里的参数是一个QString类型,这是什么呢?
这其实是一个历史问题,因为QT是在1991年推出的,那时的C++对字符串类型的定义比较混乱,C++自身的string类型还不是很好用,于是QT就自己造了一个轮子,也就是QString类型,后来C++标准定义的string类型发布了,但是QT也并没有把原来设计的类型删掉,而是保留了下来。
但是我们在这里调用的时候,直接传入了一个 "hello world",这是因为C风格的字符串会隐式转化为QString。
关于内存泄漏的问题
还是这个代码,我们发现,我们new了一个QLabel对象后,并没有调用delete来进行释放,这是否会造成内存泄漏呢?
这里是不会造成内存泄漏的,因为在QT中有一个对象树的概念,QLabel继承自它的父类Widget,在我们构造QLabel对象的时候,就把this指针传入了过去,那么也就是将 QLabel对象挂到这个对象树中了。
使用对象树的目的,就是为了能够在合适的时机(比如窗口的关闭/销毁),把这些对象统一进行释放。统一释放这种事情还是挺不错的,如果某一个窗口内有对象提前释放了,这不就是bug了吗
我们这里是通过new的方式创建 QLabel对象的,也就是在堆上创建的,我们也可以在栈上创建,在栈上创建编译器不会报错,但是不建议,因为这样就会导致对象提前被释放了。
比如我们将刚刚实现hello world改为栈上生成:
发现这一次并没有hello world,说明对象被提前释放掉了。
我们知道内存泄漏是一件很可怕的事情,因为不容易被察觉,并且暴露问题的周期不确定,可能是几分钟,几小时,甚至几天。那么,我们将对象挂到对象树中,我们没有手动调用delete,这个对象真的被释放了吗?
为了进行一个验证,我们可以手动创建一个类,让它继承 QLabel,我们手动实现一下析构函数,来进行一个打印的方式进行验证。
先在头文件那里选择创建C++类
这里我们要选择继承QLabel
点击创建以后,就会帮我们生成对应的.h和.cpp文件
头文件中:
注意,我们要想把对象挂到QT的对象树中,在构造函数中记得传入父类的指针
Qt中通过继承内置自Qt内置的类,就可以达到对控件进行扩展的效果。
在头文件中写完声明后,有一个技巧:
用alt + enter(回车)的方式,自动在对应的.cpp文件中添加函数定义
在cpp文件中
最后在widget.cpp中调用,记得包含头文件
运行程序
发现hello world打印出来了,但是因为我们还没关闭窗口,所以MyLabel还没有销毁,当我们关闭窗口后
发现打印了这个,虽然有一些乱码,但是我们能认出这就是我们析构函数打印出来的内容。
关于乱码问题
有这么一个问题:一个汉字占用多少字节?
如果我们直接回答,那么百分百就是错了,因为现在表示汉字的字符集主要有两种,一种是GBK:也就是Windows下对简体中文用的字符集,另一种是utf-8,在Linux下默认用的就是这种。
然后GBK下一个汉字是2字节,而utf-8下一个汉字一般是3字节。
所有出现乱码问题的原因只有一个:那就是编码方式不匹配。
如果要解决这个乱码问题,我们可以使用qDebug()函数来打印。
比如
Qt中提供了一个qDebug工具,可以帮我们打印日志,并处理字符编码,还有不需要换行。
qDebug是一个宏,封装了QDebug对象。
并且如果我们使用qDebug,可以很方便的通过编译开关,来进行一键式关闭和打开。
因为我们打印日志主要是给程序员看的,当我们将程序发布上线给用户使用后,当然不应该给用户看到日志信息。
关于为什么不用VS 或者 gdb这样的调试工具来调试,这是因为调试器在很多情况下是有局限性的,比如一个bug出现的概率是1%,这时想要通过调试器来调试就会非常困难和麻烦。
用按钮实现一个hello world
第一种方法:我们同样可以使用图形化界面的方式来实现
这是一个按钮,但是没有进行任何处理的话,我们点击这个按钮是没有反应的。
这里就简单说下Qt信号槽机制
这里的本质就是给按钮的点击操作,关联一个处理函数。
当用户点击的时候,就会调用这个处理函数。
这里的设置处理函数的接口就是 connect
这里的connect跟网络套接字那里没有任何关系。
来看一下我这里的调用方式
这里我们发现,我们是用了一个 ui->pushButton。并且显然这个pushButton就是我们的按钮对象
为了做区分,我又用代码的方式创建了一个QLabel
运行结果:
可以看到是没问题的,现在的情况就是 这个按钮是我用图形化界面创建的,这个QLabel是我用代码的方式创建的。
接着在设计图形化界面这里:
我们发现用代码生成的不会显示在这里,并且注意右边,在QPushButton下有一个对象,名字就是pushButton,这个对象其实就是由我们的ui自动帮我们创建的,并且这个对象的名字也是可以由我们进行修改的。我们怎么看到ui的代码呢?
我们先点击在Explorer中显示:
在build中找到
打开后
我们发现,这里就会自动帮我们创建一个QPushButton的对象,对象名正是pushButton,它就是ui_Widget的类成员。
所以到这里我们就搞清楚了,为什么通过图形化界面创建的,要通过ui->这种方式使用了。
这下我们也就能理解,在Widget中的这个ui是干啥的了。
关于connect参数的解释:
这个handleClick当然也是我们自己实现的:
那么步骤就是:先在Widget.h中进行声明,然后再对应的.cpp中实现:
这里我们实现的功能是:如果这个按钮的名字是 hello world hzj,那么就按钮设置为 hello world qt,否则就又设置成 hello world hzj。
这里就是用图形化界面的方式用按钮实现一个hello world,那么用代码的方式如下:
这里记得也要将这个_myButton作为Widget的成员变量声明一下
对两种实现方式的总结:
在实际开发中,这两种方式都会用到,都很重要。
使用图形化界面的场景:当界面内容固定,此时就会以图形化界面的方式来构造界面。
使用代码的场景:当程序界面需要经常动态变化时,此时就会用代码的方式构造界面。
给变量/函数/文件/类命名的规范
在C/C++中,一般使用蛇形命名法:
而在其它的语言中,几乎都使用驼峰命名法,包括Qt这里也是:
项目文件的解析
关于.pro文件
widget.h ⽂件解析
main.cpp ⽂件解析
Qt Creator中的快捷键
使用帮助文档
如果我们要查询某一个函数或者类,可以点击一下这个函数,然后按f1
右边就会给我们将文档展示出来
我们还可以点击左边的帮助按钮:
不过实际开发中,还是用到什么,用f1查比较多。
认识对象模型(对象树)
在 Qt 中创建很多对象的时候会提供⼀个 Parent 对象指针,下⾯来解释这个 parent 到底是⼲什么的。
QObject 是以对象树的形式组织起来的。
QWidget 是能够在屏幕上显⽰的⼀切组件的⽗类。
Qt 引⼊对象树的概念,在⼀定程度上解决了内存问题。
不过也存在一些特殊情况:
但是如果我们调换一下对象的构造顺序:
因此:在 Qt 中,尽量在构造的时候就指定 parent 对象,并且⼤胆在堆上创建。
Qt的坐标体系
坐标体系:以左上⻆为原点(0,0),X向右增加,Y向下增加。
这里跟数学的坐标体系是有点不一样的。
对于嵌套窗⼝,其坐标是相对于⽗窗⼝来说的。