为什么在头文件中有的是使用前置声明,而有的是包含头文件?
如下代码:
#include <QDialog>
class QCheckBox;
class QLabel;
class FindDialog :public QDialog
{
Q_OBJECT
public:
FindDialog(QWidget *parent=0);
private:
QCheckBox *checkBox;
QLabel *label;
}
- 前置声明(forward declaration)会告诉C++编译程序类的存在,而不需要提供类定义中的所有的细节(通常放在它自己的头文件当中)。因为我们的这些私有变量都是指针,而且没有必要在头文件当中就去访问它们,因而编译程序就无需这些类的完整的定义。我们不使用这些类的相关的头文件(如
<QCheckBox>
,<QLabel>
),而是使用前置声明,这可以使得变异的过程更快一些。 - 在真正的实现文件.cpp当中,我们可以包含
#include <QtGui>
,这个头文件包含了Qt GUI类的定义。Qt由数个模块组成,每一个模块都有自己的类库。最为重要的模块有QtCore、QtGui、QtNetwork、QtOpenGL、QtScript、QtSql、QtSvg、QtXml。其中,在<QtGui>
头文件中为构成QtCore和QtGui组成部分的所有类进行了定义。在程序中包含这个头文件,就能够使我们省去在每一个类中分别包含的麻烦。在头文件中本可以直接包含<QtGui>
即可,然而在一个头文件中再包含一个那么大的头文件实在不是一种好的编程风格,尤其对于比较大的工程项目更是如此。
为什么在Qt中大多数时候都是只见new 不见delete?
- Qt会在删除父对象的时候自动的删除其所属的子对象,包括其所有的子窗口部件和子布局对象.
信号和槽机制
- 信号和槽机制是Qt编程的基础.它可以让应用程序的编程人员把这些互不了解的对象绑定在一起.槽和普通的C++成员函数几乎是一样的—-可以是虚函数;可以被重载,可以是公有,保护,私有类型,并且也可以直接被其他的C++成员函数直接调用.它们的参数可以使任意的类型.唯一不同的是:槽还可以和信号连接在一起.每次发射和槽连接在一起的信号时,就会自动的触发槽函数.
- 一个信号可以连接多个槽:
connect(slider,SIGNAL(valueChanged(int)),this,SLOT(setValue(int)))
,connect(slider,SIGNAL(valueChanged(int)),this,SLOT(updateStatusBarIndicator(int)))
.在发射这个信号的时候会以不确定的顺序一个接一个地调用这些槽. - 多个信号可以连接同一个槽函数
- 一个信号也可以和另外的一个信号相连接:
connect(slider,SIGNAL(valueChanged(int)),this,SIGNAL(updateRecord(int)));
当发射第一个信号的时候,第二个信号也会被触发.除此之外,信号和信号之间的连接和信号和槽之间的连接是难以区分的. - 连接可以被移除:
disconnect(slider,SIGNAL(valueChanged(int)),this,SIGNAL(updateRecord(int)));
这种情况比较少用,因为当删除掉对象时,Qt会自动的移除和这个对象相关的所有的连接. - 对应的信号和槽必须要有相同顺序和类型的参数,但是允许槽的参数比信号的参数少,信号中多余的参数会被简单的忽略掉.
- Qt的信号和槽机制是在QObject中实现的,并不仅仅局限于图形用户界面编程当中,这种机制可以使用于任何的QObject的子类当中.
Qt的元对象系统
- Qt的主要成就之一就是使用了一种机制对C++进行了扩展,并且使用了这种机制创建了独立的软件组件.这些组件可以绑定在一起,但是任何组件对于他所要连接的组件的情况事先都一无所知.这种机制称为元对象系统(meta-object system),它提供了关键的两项技术:信号-槽以及内省(introspection).内省功能对于实现信号和槽是必须的.并且允许应用程序的开发人员在运行的时候获得有关QObject子类的元信息(meta-information),包括一个含有对象的类名以及他所支持的信号和槽的列表.这一机制也支持属性(广泛用于Qt的设计师中)和文本翻译(用于国际化),并且它也为QtScript模块奠定了基础.
- 标准C++中并没有对Qt的元对象系统所需要的动态元信息提供支持.Qt通过一个独立的moc工具解决了这个问题,moc解析Q_OBJECT类的定义并且通过C++函数来提供可供使用的信息.由于moc使用纯C++来实现其所有的功能,所以Qt的元对象系统可以在任意的C++编译器上工作.
这一机制的工作过程是:
- Q_OBJECT宏声明了在每一个QObject子类中必须要实现的一些内省函数:metaObject()、tr(),qt_metacall(),以及其他的一些函数.
- Qt的moc工具生成了用于由Q_OBJECT声明的所有函数和所有信号的实现.
- 像connect()和disconnect()这样的QObject的成员函数使用这些内省函数来完成他们的工作.
由于所有的这些工作都是由qmake,moc和QObject自动处理的,所以很少需要再去考虑这些事情.但是如果你对此充满好奇,那么也可以阅读以下有关QMetaObject类的文档和由moc生成的C++源代码文件,可以从中看出这些实现工作是如何进行的.
元对象工具
- 元对象编译器moc(meta object compiler)对C++文件中的类声明进行分析并产生用于初始化元对象的C++代码,元对象包含全部信号和槽的名字以及指向这些函数的指针. moc读取C++源文件,如果发现有Q_OBJECT宏声明的类,他就会生成另外一个C++源文件,这个新生成的源文件中包含有该类的元对象代码. 例如,假设我们有一个头文件mysignal.h,在这个文件中包含有信号或槽的声明,那么在编译之前 moc 工具就会根据该文件自动生成一个名为mysignal.moc.h的C++源文件并将其提交给编译器;类似地,对应于mysignal.cpp文件moc 工具将自动生成一个名为mysignal.moc.cpp文件提交给编译器。
- 元对象代码是signal/slot机制所必须的。用moc产生的C++源文件必须与类实现一起进行编译和连接,或者用#include语句将其包含到类的源文件中。moc并不扩展#include或者#define宏定义,它只是简单的跳过所遇到的任何预处理指令。
Qt 进程间通信
Qt的通信可分为Qt**内部通信和外部通信**两大类。对于这两类通信机制及应用场合做如以下分析:
1. Qt内部对象间通信
对于这种内部对象间的通信,QT主要采用了信号和槽的机制。
- QT的信号和槽机制是用来在对象间通讯的方法,当一个特定事件发生的时候,signal会被 emit 出来,slot 调用是用来响应相应的 signal 的。简单点说就是如何在一个类的一个函数中触发另一个类的另一个函数调用,而且还要把相关的参数传递过去.好像这和回调函数也有点关系,但是消息机制可比回调函数有用多了,也复杂多了。例如,实现单击按钮终止应用程序运行的代码
connect(button , SIGNAL(clicked()) , qApp , SLOT(quit()) );
实现过程就是一个button被单击后会激发clicked**信号,通过connect()
函数的连接qApp会接收到此信号并执行槽函数quit()
。在此过程中,信号的发出并不关心什么样的对象来接收此信号,也不关心是否有对象来接收此信号**,只要对象状态发生改变此信号就会发出。此时槽也并不知晓有什么的信号与自己相联系和是否有信号与自己联系,这样信号和槽就真正的实现了程序代码的封装,提高了代码的可重用性。 - 关键字signals指出随后开始信号的声明,这里signals用的是复数形式而非单数,siganls没有public、 private、protected等属性,这点不同于slots。另外,signals、slots关键字是QT自己定义的,不是C++中的关键字。信号也可采用C++中虚函数的形式进行声明,也可以实现重载.
- 宏定义不能用在signal和slot的参数中,因为moc工具不扩展#define,因此,在signals和slots中携带参数的宏就不能正确地工作,如果不带参数是可以的。
- 函数指针不能作为信号或槽的参数。
class someClass : public QObject
{
Q_OBJECT
public slots:
void apply(void (*applyFunction)(QList*, void*), char*); // 不合语法
};
typedef void (*ApplyFunctionType)(QList*, void*);
class someClass : public QObject
{
Q_OBJECT
public slots: