元对象系统

Qt的元对象系统提供信号与槽机制,用于对象间的通信,运行时类型信息,以及动态属性系统。

元对象系统基于以下三点:

1.QObject类为需要利用元对象系统的对象提供了一个基类。

2.位于类声明的私有部分中的Q_OBJECT宏被用于开启元对象特性,例如动态属性,信号和槽。

3.元对象编译器(moc)为每个QObject子类提供了必需的代码用于实现元对象特性。

moc工具读取C++源文件。如果它找到包含Q_OBJECT宏的一个或多个类的声明,那么它会产生另外的C++源文件,其中包含了这些类的元对象代码。这些产生的源文件或者

#include到类的源文件,或者通常编译和链接到类的实现。

除了提供信号与槽机制用于对象间的通信(引入系统的主要原因),元对象代码提供了以下额外的特性:

1.QObject::metaObject()返回与类相关的元对象.

2.QMetaObject::className()在运行时返回类名的字符串,不需要通过C++编译器的RTTI支持。

3.QObject::inherits()函数返回某个对象是否是QObject继承树中继承指定类的类的实例。

4.QObject::tr()和QObject::trUtf8()翻译用于国际化的字符串。

5.QObject::setProperty()和QObject::property()动态的设置和按照名称获取属性。

6.QMetaObject::newInstance()构造一个新的类实例。

也可以使用qobject_cast()对QObject类进行动态转换。qobject_cast()函数的行为类似于标准C++的dynamic_cast(),但是它有优势如下:1.不需要RTTI支持。2.可以跨动态库工作。这个函数尝试将其参数转换为在尖括号中指定的指针类型,如果这个对象的类型正确(在运行时决定),则返回一个非零的指针,如果对象类型不兼容,返回0。

例如,让我们假设 MyWidget 继承自 QWidget 并且用宏 Q_OBJECT 声明:

QObject *obj = new MyWidget;

obj 是类型为 QObject * 的变量,实际指向一个 MyWidget 对象,因此,我们可以适当的转换它:

QWidget *widget = qobject_cast(obj);

这个从QObject到QWidget的转换是成功的,因为这个对象实际上是一个MyWidget,是一个QWidget的子类。由于我们知道 obj 是i一个 MyWidget,我们可以将其转化为MyWidget *:

MyWidget *myWidget = qobject_cast(obj);

这个到MyWidget的转换是成功的,因为qobject_cast()不区分内置的Qt类型和自定义类型。

QLabel *label = qobject_cast(obj);// label is 0

另一方面,转换为QLabel是失败的。这个指针被设置为0。这样就可以根据类型在运行时处理不同类型的对象:

if(QLabel *label = qobject_cast(obj)){

label->setText(tr("Ping"));

}else if(QPushButton *button = qobjcet_cast(obj)){

button->setText(tr("Pong!"));

}

可以没有Q_OBJECT宏和元对象代码,而使用QObject作为基类。然而不使用Q_OBJECT宏,将无法使用信号与槽以及这里描述的其他特性。从元对象系统的角度来看,没有元代码的QObject子类等价于其与元对象代码最接近的祖先。这意味着对于示例,QMetaObject :: className()不会返回你的类的实际名称,而是这个祖先的类名。

因此,我们强烈建议QObject的所有子类使用Q_OBJECT宏,无论它们实际上是否使用信号,插槽和属性。



你可能感兴趣的:(Qt)