元对象系统

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

元对象系统基于 3 件事:

  1. The QObject 类为可以利用元对象系统的对象提供基类。
  2. The Q_OBJECT 宏 (在类声明私有区间内) 用于启用元对象特征,譬如:动态特性、信号及槽。
  3. The Meta-Object Compiler ( 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 的特性系统 ,和 信号和槽 .