元对象系统

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

元对象系统基于以下三点

  1. QObject 类提供了一个基类,供可以利用元对象系统的对象使用。
  2. 使用 Q_OBJECT 宏来启用元对象功能,例如动态属性、信号和槽。
  3. 元对象编译器 (moc) 为每个 QObject 子类提供实现元对象功能所需的所有代码。

moc 工具读取 C++ 源文件。如果它发现一个或多个包含 Q_OBJECT 宏的类声明,它将生成另一个 C++ 源文件,该文件包含每个这些类的元对象代码。这个生成的源文件将要么被 #include'd 到类的源文件中,要么更通常地与其实现编译并链接。

除了提供对象间通信(引入该系统的首要原因)的信号和槽机制外,元对象代码还提供了以下附加功能

还可以使用 qobject_cast() 在 QObject 类上执行动态转换。该 qobject_cast() 函数的行为类似于标准 C++ 的 dynamic_cast(),优点是它不需要 RTTI 支持,并且可以在动态库边界之间工作。它尝试将其参数转换为目标尖括号中指定的指针类型,如果对象是正确的类型(在运行时确定),则返回非零指针,或者如果对象类型不兼容,则返回 nullptr

例如,假设 MyWidget 继承自 QWidget 并使用 Q_OBJECT 宏声明

    QObject *obj = new MyWidget;

变量 obj,类型为 QObject *,实际上指向一个 MyWidget 对象,因此我们可以适当地进行类型转换

    QWidget *widget = qobject_cast<QWidget *>(obj);

QObject 转换到 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!"));
    }

虽然可以在不使用 Q_OBJECT 宏和元对象代码的情况下将 QObject 作为基类使用,但如果没有使用 Q_OBJECT 宏,则既没有信号和槽,也没有这里描述的其他功能。从元对象系统的角度来看,没有元代码的 QObject 子类与其最近的祖先(拥有元对象代码的)等价。这意味着比如,QMetaObject::className() 将返回你类的名称,而不是这个祖先的类名。

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

另请参阅 QMetaObjectQt 的属性系统,和 信号和槽

© 2024 Qt 公司。包含在本处的文档贡献的版权属于各自的拥有者。此处提供的文档是根据自由软件基金会发布的 GNU 自由文档许可证版本 1.3 的条款授权的。Qt 和相应的标志是芬兰及/或其他国家 Qt 公司的商标。所有其他商标均属于各自所有者。