Qt 的元对象系统为对象间通信、运行时类型信息及动态特性系统,提供信号和槽机制。
元对象系统基于 3 件事:
moc
) 供给各
QObject
子类 (采用必要代码) 以实现元对象特征。
The
moc
工具读取 C++ 源文件。若找到一个或多个类声明包含
Q_OBJECT
宏,它产生包含每个这些类的元对象代码的另一 C++ 源文件。这生成的源文件将
#include
放入类源文件,或更通常,编译并链接类实现。
除了提供 信号和槽 机制用于对象之间的通信 (引入系统的主要原因),元对象代码提供以下额外特征:
也可以履行动态铸造使用
qobject_cast
() 在
QObject
类。
qobject_cast
() 函数行为类似于标准 C++
dynamic_cast()
,它的优点是不要求 RTTI (运行时类型信息) 支持,且工作跨动态库边界。它试图将其自变量铸造向在尖括号中指定的指针类型,返回非零指针若对象类型正确 (在运行时确定),或
nullptr
若对象类型不兼容。
例如,假定
MyWidget
继承自
QWidget
和声明采用
Q_OBJECT
宏:
QObject *obj = new MyWidget;
The
obj
变量,类型
QObject *
,实际引用
MyWidget
对象,因此可以适当铸造:
QWidget *widget = qobject_cast<QWidget *>(obj);
铸造从
QObject
to
QWidget
成功,因为对象实际是
MyWidget
,这是子类对于
QWidget
。由于知道
obj
是
MyWidget
,也可以将它铸造成
MyWidget *
:
MyWidget *myWidget = qobject_cast<MyWidget *>(obj);
铸造成
MyWidget
成功因为
qobject_cast
() 使在内置 Qt 类型和自定义类型之间没有区别。
QLabel *label = qobject_cast<QLabel *>(obj); // label is 0
铸造成 QLabel ,在其它方面,失败。然后,将指针设为 0。这使基于不同类型在运行时处理不同类型对象,成为可能:
if (QLabel *label = qobject_cast<QLabel *>(obj)) { label->setText(tr("Ping")); } else if (QPushButton *button = qobject_cast<QPushButton *>(obj)) { button->setText(tr("Pong!")); }
虽然可以使用 QObject 作为基类不用 Q_OBJECT 宏及不用元对象代码,信号和槽及在此处描述的其它特征将不可用若 Q_OBJECT 宏不被使用。从元对象系统角度来看, QObject 子类 (没有元代码) 相当于具有元对象代码的其最接近祖先。例如,这意味着 QMetaObject::className () 不会返回实际类名,但会返回此祖先的类名。
因此,强烈推荐所有子类化的 QObject 使用 Q_OBJECT 宏,不管它们是否有实际使用信号,槽及特性。
另请参阅 QMetaObject , Qt 的特性系统 ,和 信号和槽 .