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();
}
實際應用示例:
如果有一個自定義的類型 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()這些反射相關的函數。
3 總結
Q_DECLARE_METATYPE 通知Qt元對象系統關於自定義類型的存在,這樣該類型就可以在QVariant中使用。qRegisterMetaType在此基礎上更進一步,它將自定義類型完全集成到Qt的元對象系統中,使得類型可以跨線程在信號和槽中使用,以及在Qt的屬性系統中使用。
瞭解這兩者是如何工作的有助於在Qt應用程序中更有效地使用自定義類型,特別是在需要類型信息的高級特性時,如跨線程信號與槽的通信或屬性系統。