元对象系统
Qt 的元对象系统为对象间通信、运行时类型信息和动态属性系统提供了信号和槽机制。
元对象系统基于三个方面:
- QObject 类为可以利用元对象系统的对象提供基类。
- 类声明的私有部分中的 Q_OBJECT 宏用于启用元对象功能,例如动态属性、信号和插槽。
- Meta-Object Compiler () 为每个 QObject 子类提供必要的代码来实现元对象 features.moc。
该工具读取C++源文件。如果它找到一个或多个包含Q_OBJECT宏的类声明,它将生成另一个C++源文件,其中包含每个类的元对象代码。生成的源文件要么被嵌入到类的源文件中,要么更通常地被编译并与类的实现链接。
元对象操作与信息获取
除了为对象之间的通信提供信号和插槽机制(引入系统的主要原因)之外,元对象代码还提供以下附加功能:
- QObject::metaObject() 返回类的关联元对象。
- QMetaObject::className() 在运行时将类名作为字符串返回,而不需要通过 C++ 编译器支持本机运行时类型信息 (RTTI)。
- QObject::inherits() 函数返回对象是否是继承 QObject 继承树中指定类的类的实例。
- QObject::tr() 为国际化翻译字符串。
- QObject::setProperty() 和 QObject::property() 按名称动态设置和获取属性。
- QMetaObject::newInstance() 构造类的一个新实例。
动态类型转换
还可以对QObject类使用qobject_cast()执行动态类型转换。qobject_cast()函数的行为类似于标准的c++ dynamic_cast(),其优点是它不需要RTTI支持,并且可以跨动态库边界工作。它尝试将其参数强制转换为尖括号中指定的指针类型,如果对象的类型正确(在运行时确定),则返回一个非零指针,如果对象的类型不兼容,则返回0。
例如,假设继承自 QWidget 并使用 Q_OBJECT 宏声明:MyWidget
QObject *obj = new MyWidget;
类型的变量实际上是指一个对象,所以我们可以适当地转换它:objQObject *MyWidget
QWidget *widget = qobject_cast<QWidget *>(obj);
从 QObject 到 QWidget 的转换是成功的,因为对象实际上是 a ,它是 QWidget 的子类。由于我们知道这是 a ,我们也可以将其转换为 :MyWidgetobjMyWidgetMyWidget *
MyWidget *myWidget = qobject_cast<MyWidget *>(obj);
转换成功是因为 qobject_cast() 不区分内置 Qt 类型和自定义类型。MyWidget
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!"));
}
虽然可以在没有 Q_OBJECT 宏和元对象代码的情况下使用 QObject 作为基类,但如果不使用 Q_OBJECT 宏,则信号和槽以及此处描述的其他功能都将不可用。从元对象系统的角度来看,没有元代码的 QObject 子类等价于具有元对象代码的最接近的祖先。例如,这意味着 QMetaObject::className() 不会返回您的类的实际名称,而是该祖先的类名。
因此,我们强烈建议 QObject 的所有子类都使用 Q_OBJECT 宏,无论它们是否实际使用信号、槽和属性