Qt的元对象系统(Meta-Object System)提供了对象间通信的信号和槽机制、运行时类型信息和动态属性系统。
元对象系统基于以下三个方面:
(1).QObject类:为可以利用元对象系统的对象提供了基类。
(2).Q_OBJECT宏:用于启用元对象功能,例如动态属性、信号和槽。Q_OBJECT宏内容如下:
#define Q_OBJECT \
public: \
QT_WARNING_PUSH \
Q_OBJECT_NO_OVERRIDE_WARNING \
static const QMetaObject staticMetaObject; \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
QT_TR_FUNCTIONS \
private: \
Q_OBJECT_NO_ATTRIBUTES_WARNING \
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
QT_WARNING_POP \
struct QPrivateSignal { explicit QPrivateSignal() = default; }; \
QT_ANNOTATE_CLASS(qt_qobject, "")
(3).元对象编译器(Meta-Object Compiler, MOC):为每个QObject子类提供实现元对象功能所需的代码。
MOC工具读取(reads) C++源文件。如果它发现一个或多个包含Q_OBJECT宏的类声明,它会生成另一个C++源文件,其中包含每个类的元对象代码(meta-object code)。生成的源文件要么被 #include到类的源文件中,要么更常见的是,被编译并与类的实现链接。
通过MOC工具由mainwindow.h生成moc_mainwindow.cpp,内容类似如下图所示:信号函数的实现也在此文件中;生成此.cpp文件对应的CMake命令为qt6_wrap_cpp
元对象代码除了提供对象间通信的信号和槽机制(引入该系统的主要原因)外,还提供以下附加功能:
(1).QObject::metaObject():返回与该类相关的元对象。
(2).QMetaObject::className():在运行时以字符串形式返回类名,无需通过C++编译器支持本机运行时类型信息(run-time type information, RTTI)。
(3).QObject::inherits():函数返回某个对象是否是继承QObject继承树中指定类的类的实例。
(4).QObject::tr():翻译字符串以实现国际化(internationalization)。
(5).QObject::setProperty():通过名称动态设置属性。
(6).QObject::property():通过名称动态获取属性。
(7).QMetaObject::newInstance():构造该类新实例。
qobject_cast():可以使用qobject_cast()对QObject类执行动态转换。qobject_cast()函数的行为类似于标准C++ dynamic_cast(),其优点是它不需要RTTI支持,并且可以跨动态库边界(dynamic library boundaries)工作。它尝试将其参数转换为尖括号中指定的指针类型,如果对象是正确的类型(在运行时确定),则返回非零指针,如果对象的类型不兼容,则返回nullptr。
虽然可以使用QObject作为基类,而无需Q_OBJECT宏和元对象代码,但如果不使用Q_OBJECT宏,则信号和槽以及上面描述的其他功能都将不可用。从元对象系统的角度来看,没有元代码的QObject子类相当于其具有元对象代码的最近祖先(closest ancestor)。这意味着,例如,QMetaObject::className()将不会返回你的类的实际名称,而是返回此祖先的类名。
强烈建议QObject的所有子类都使用Q_OBJECT宏,无论它们是否实际使用信号、槽和属性。
QMetaObject类:包含有关Qt对象的元信息(meta-information)。
(1).Qt中的元对象系统负责信号和槽对象间通信机制、运行时类型信息和Qt属性系统。为应用程序中使用的每个QObject子类创建一个QMetaObject实例,此实例存储 QObject子类的所有元信息。此对象可用作QObject::metaObject()。
(2).此类通常不是应用程序编程所必需的,但如果你编写元应用程序(例如脚本引擎或GUI构建器),它会很有用。
(3).使用元对象系统的操作通常是线程安全的,因为QMetaObjects通常是编译时生成的静态只读实例(static read-only instances)。但是,如果应用程序动态修改元对象(例如,使用QQmlPropertyMap时),则应用程序必须明确同步对相应元对象的访问。
注:以上整理的内容主要来自于Qt官方文档
GitHub:https://github.com/fengbingchun/Qt_Test