目前接触QT编程,打算将自己学习过程中的一些重要内容做一下梳理,并以QT系列命名
Qt的一个最主要的成功是对C++扩展,即
把彼此独立的软件模块连接起来,而模块间彼此不需要知道相连模块的任何细节。这个机制就是Meta-Object系统,它提供了两个关键的用途:信号和槽和introspection(内省)。introspection功能允许程序员在程序运行时得到QObject它子类的“元信息(meta-information)”,这对实现信号和槽是很必要的,包括全部信号和槽的列表,和类的名字。这个机制还提供了属性(在Qt Designer中使用)和文本翻译(国际化)支持。它们构成了QSA(Qt Script for Application)的基础。
标准C++不提供Qt meta-object系统所需的动态meta-information。为解决该问题,Qt提供了一个独立的工具moc,它通过C++函数来解析Q_OBJECT宏类定义而获得信息。moc是用纯c++实现的,因此元对象系统可以使用在任何C++编译器中。
这个机制工作过程如下:
Q_OBJECT宏声明了一些QObject子类中必须实现的内省函数:metaObject(),TR(),qt_metacall()等。
Qt的moc工具会实现Q_OBJECT宏声明的函数和所有的信号。
QObject成员函数例如connect()和disconnect()使用这些内省函数实现信号和槽的连接。
以上这些是通过qmake,moc和QObject自动处理的,程序员通常不用考虑它们。如果你感到对此好奇,可以查看QMetaObject类文档和moc生成的c++源文件。
QT Meta-Object System 的功能以及实现。
在使用 QT 开发的过程中,大量的使用了 signal 和 slot. 比如,响应一个 button 的 click 事件,我们一般都写如下的代码:
class MyWindow : public QWidget
{ Q_OBJECT
public:
MyWindow(QWidget* parent) : QWidget(parent)
{
QPushButton* btnStart = new QPushButton(“start”, this);
connect(btnStart, SIGNAL(clicked()), SLOT(slotStartClicked()));
}
private slots:
void slotStartClicked();
};
void MyWindow:: slotStartClicked()
{ // 省略
}
在这段代码中,我们把 btnStart 这个 button 的clicked() 信号和 MyWindow 的 slotStartClicked() 这个槽相连接,当 btnStart 这个 button 被用户按下(click)的时候,就会发出一个 clicked() 的信号,然后,MyWindow:: slotStartClicked() 这个 slot 函数就会被调用用来响应 button 的 click 事件。
这段代码是最为典型的 signal/slot 的应用实例,在实际的工作过程中,signal/slot 还有更为广泛的应用。准确的说,signal/slot 是QT提供的一种在对象间进行通讯的技术,那么,这个技术在QT 中是如何实现的呢?
这就是 QT 中的元对象系统(Meta Object System)的作用,为了更好的理解它,让我先来对它的功能做一个回顾,让我们一起来揭开它神秘的面纱。
Meta-Object System 的基本功能
Meta Object System 的设计基于以下几个基础设施:
-
QObject 类
作为每一个需要利用元对象系统的类的基类
-
Q_OBJECT 宏,
定义在每一个类的私有数据段,用来启用元对象功能,比如,动态属性,信号和槽
-
元对象编译器moc (the Meta Object Complier),
moc 分析C++源文件,如果它发现在一个头文件(header file)中包含Q_OBJECT 宏定义,然后动态的生成另外一个C++源文件,这个新的源文件包含 Q_OBJECT 的实现代码,这个新的 C++ 源文件也会被编译、链接到这个类的二进制代码中去,因为它也是这个类的完整的一部分。通常,这个新的C++ 源文件会在以前的C++ 源文件名前面加上 moc_ 作为新文件的文件名。其具体过程如下图所示:
除了提供在对象间进行通讯的机制外,
元对象系统还包含以下几种功能:
-
QObject::metaObject() 方法
它获得与一个类相关联的 meta-object
-
QMetaObject::className() 方法
在运行期间返回一个对象的类名,它不需要本地C++编译器的RTTI(run-time type information)支持
-
QObject::inherits() 方法
它用来判断生成一个对象类是不是从一个特定的类继承出来,当然,这必须是在QObject类的直接或者间接派生类当中
-
QObject::tr() and QObject::trUtf8()
这两个方法为软件的国际化翻译字符串
-
QObject::setProperty() and QObject::property()
这两个方法根据属性名动态的设置和获取属性值
除了以上这些功能外,它还使用qobject_cast()方法在QObject类之间提供动态转换,qobject_cast()方法的功能类似于标准C++的dynamic_cast(),但是qobject_cast()不需要RTTI的支持,
在一个QObject类或者它的派生类中,我们可以不定义Q_OBJECT宏。如果我们在一个类中没有定义Q_OBJECT宏,那么在这里所提到的相应的功能在这个类中也不能使用,从meta-object的观点来说,一个没有定义Q_OBJECT宏的类与它最接近的那个祖先类是相同的,那就是所,QMetaObject::className() 方法所返回的名字并不是这个类的名字,而是与它最接近的那个祖先类的名字。所以,我们强烈建议,任何从QObject继承出来的类都定义Q_OBJECT宏。