Qt小知识1.Q_DECLARE_METATYPE和qRegisterMetaType
1 了解Q_DECLARE_METATYPE
Q_DECLARE_METATYPE 是一个Qt宏,用以通知Qt的反射系统关于自定义类型的存在。当使用此宏声明一个类型后,该类型可以在QVariant中使用。QVariant是Qt中用于存储可以包含任意类型的一个“通用”值容器。
Qt 元对象系统不知道非Qt类的存在,因此如果要在QVariant中存储自定义类型,就需要用这个宏声明它。此宏必须在全局作用域中使用,并且用于类型定义之后。它实际上就是为类型定义一个特殊的模板特化,这样QVariant才能了解如何使用它。
这个模板特化提供了一个静态函数qt_metatype_id,它为该类型分配并返回一个独一无二的ID。这个ID用于QVariant创建、复制、比较和析构该类型的实例。
综上,Q_DECLARE_METATYPE(Type)宏用于告诉 Qt 框架某个自定义类型Type是存在的,并且可以被元对象系统所使用。使用这个宏之后,Type就可以用于 QVariant 类型和信号与槽的参数传递中。该宏通常在类的定义外部使用,不需要修改类的定义。注意,仅仅使用Q_DECLARE_METATYPE并不能够在使用信号和槽时动态地创建类型的对象,为此需要使用qRegisterMetaType。
struct MyStruct {
int a;
QString b;
};
Q_DECLARE_METATYPE(MyStruct)
上述代码使得MyStruct可以被用在 QVariant 内部。
2 了解qRegisterMetaType
qRegisterMetaType 是一个在运行时调用的函数,它将自定义类型注册到Qt的元对象系统中。此函数确保类型不仅已知于QVariant,还已知于Qt的整个类型系统,尤其是用于多线程环境中信号和槽的QObject通信。
注册类型之后,Qt可以动态地在运行时构造和销毁对象,即使是对于非Qt类型。这是因为qRegisterMetaType在内部为类型存储了创建和析构该类型对象所需的方法指针。这允许在运行时创建和复制传递给信号和槽作为参数的类型,这些参数必须能够在事件循环中跨线程边界安全移动。
综上,qRegisterMetaType
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 注册到元类型系统
qRegisterMetaType<MyStruct>("MyStruct");
// 现在 MyStruct 可以安全地用做信号和槽参数
// ...
return app.exec();
}
3 实际应用示例
如果有一个自定义的类型 MyStruct 并想在不同线程间的信号和槽通信中使用它,需要这么做:
- 使用 Q_DECLARE_METATYPE() 声明这个结构体。
struct MyStruct {
int a;
QString b;
};
Q_DECLARE_METATYPE(MyStruct)
- 在应用程序启动时(例如在 main 函数中),使用 qRegisterMetaType() 来注册自定义类型。
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
qRegisterMetaType<MyStruct>("MyStruct");
// ...
return app.exec();
}
这使得 MyStruct 可以在跨线程的信号和槽调用中安全使用。如果只是想在同一线程内的信号和槽或者使用 QVariant 存储自定义类型的话,通常只需要 Q_DECLARE_METATYPE 宏。
此外,qRegisterMetaType 还使得自定义类型可用于QMetaObject中的类型信息,比如QObject::property(),QObject::setProperty(),以及QMetaProperty::read()和QMetaProperty::write()这些反射相关的函数。
4 总结
Q_DECLARE_METATYPE 通知Qt元对象系统关于自定义类型的存在,这样该类型就可以在QVariant中使用。qRegisterMetaType在此基础上更进一步,它将自定义类型完全集成到Qt的元对象系统中,使得类型可以跨线程在信号和槽中使用,以及在Qt的属性系统中使用。了解这两者是如何工作的有助于在Qt应用程序中更有效地使用自定义类型,特别是在需要类型信息的高级特性时,如跨线程信号与槽的通信或属性系统。
每一步踏出,都是一次探索,一次成长。