The Meta-Object System
Qt元对象系统(meta-object)为对象间通信提供信号和槽机制, 运行时类型信息及动态属性系统.
元对象系统基于以下三件事:
- QObject 作为利用元对象系统的对象的基类.
- 类声明私有 Q_OBJECT 宏, 启用元对象特性, 如动态属性, 信号和槽.
- 元对象编译器 Meta-Object Compiler (
moc
) 为每个 QObject 子类扩充必要的元对象特性代码.
moc
工具读取C++源文件. 如果它发现一个或多个包含 Q_OBJECT 宏的类声明, 它将生成另一个包含元对象代码的c++源文件. 这个源文件要么通过 #include
方式嵌入到类的源文件, 要么编译并链接到类实现.
除了提供对象间通信的 信号和槽 机制外, 元对象还提供如下功能:
- QObject::metaObject() 返回关联的元对象( meta-object).
- QMetaObject::className() 运行时, 不依赖C++编译器提供的本机运行时类型信息(RTTI)支持, 以字符串形式返回类名.
- QObject::inherits() 判断在 QObject 继承树中, 当前对象是否继承自特定类.
- QObject::tr() 和 QObject::trUtf8() 字符串 国际化.
- QObject::setProperty() 和 QObject::property() 通过名称动态读写属性.
- QMetaObject::newInstance() 构建一个新的类实例.
元对象系统也支持QObject子对象通过 qobject_cast() 动态转换. qobject_cast() 函数行为类似标准 C++ dynamic_cast()
, 其优点是不需要RTTI支持, 且可以跨动态库边界工作. 它尝试将参数转换成尖括号中指定的指针类型, 若类型正确(运行时确定), 返回非零指针. 若类型不兼容, 返回nullptr.
例如, 假设 MyWidget
继承 QWidget , 并声明 Q_OBJECT 宏:
QObject *obj = new MyWidget;
实际上, QObject *
类型变量 obj
指向对象 MyWidget
, 因此我们可以对其转换:
QWidget *widget = qobject_cast<QWidget *>(obj);
从 QObject 到 QWidget 的转换是成功的, 因为obj
对象其实是一个 MyWidget
, 它是 QWidget 的子类. 由于我们知道 obj
是 MyWidget
对象, 我们也可以将其转换为 MyWidget *
:
MyWidget *myWidget = qobject_cast<MyWidget *>(obj);
qobject_cast() 对于Qt内置类型和自定义类型没有区别, 因此obj
可以转换为 MyWidget
.
QLabel *label = qobject_cast<QLabel *>(obj); // label is 0
另一方面, obj
转换成 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!")); }
尽管在没有Q_OBJECT宏和元对象代码的情况下, 可以将 QObject对象作为基类. 但是如果不使用 Q_OBJECT 宏, 信号和槽及本文描述的其他功能无法使用. 从元对象系统看, 没有元对象代码的 QObject 子类近似于有元对象代码的最近祖先类. 例如, QMetaObject::className() 无法返回类名, 而是祖先类名.