QMetaType类
QMetaType类管理Qt的元类型系统,它将类型名称与类型关联,从而可以在run-time时创建、销毁。Q_DECLARE_METATYPE() 宏静态,使类型可以用于基于模板的方法,qRegisterMetaType()方法动态 让类型可用于非模板方法。
QT内置类型都已经注册enum QMetaType::Type,enum QMetaType::Type从0开始,其中 0 为UnknowType, 1024为用户自定义类型起始位置。
Q_DECLARE_METATYPE()宏与int qMetaTypeId()
此二者用于编译时,自定义类型只要满足以下条件就可以用宏 Q_DECLARE_METATYPE()类注册为元类型。
public default constructor
public copy constructor
public destructor
使用Q_DECLARE_METATYPE(T)时类型T(包括指针类型)必须是完全定义的,正常情况下就在类型定义之后注册,或者在每次 都要包含的私有头文件中。
对于前置声明类型的指针可以使用宏Q_DECLARE_OPAQUE_POINTER(),之后可以正常注册。
如果T在namespace中, Q_DECLARE_METATYPE(T)必须在namespace之外,T要包含完全限定名:
namespace MyNamespace
{
...
}
Q_DECLARE_METATYPE(MyNamespace::MyStruct)
Q_DECLARE_METATYPE在compile time注册类型,使类型T可以用于所有的模板类,包括QVariant。
如果要在QueuedConnections的信号槽连接中使用类型T,还需要使用qRegisterMetaType()来注册,因为QueuedConnections信号槽参数是动态查找的。
qMetaTypeId<T>()可以在编译时获取注册的类型id,如果T没有使用Q_DECLARE_METATYPE注册,会产生编译错误。
int id = qMetaTypeId<QString>(); // id is now QMetaType::QString
id = qMetaTypeId<MyStruct>(); // compile error if MyStruct not declared
具体实现
#define Q_DECLARE_METATYPE(TYPE) Q_DECLARE_METATYPE_IMPL(TYPE)#define Q_DECLARE_METATYPE_IMPL(TYPE) \
QT_BEGIN_NAMESPACE \
template <> \
struct QMetaTypeId< TYPE > \
{ \
enum { Defined = 1 }; \
static int qt_metatype_id() \
{ \
static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
if (const int id = metatype_id.loadAcquire()) \
return id; \
const int newId = qRegisterMetaType< TYPE >(#TYPE, \
reinterpret_cast< TYPE *>(quintptr(-1))); \
metatype_id.storeRelease(newId); \
return newId; \
} \
}; \
QT_END_NAMESPACE
Q_DECLARE_METATYPE宏首先定义QMetaTypeId的具现,这时还没有注册ID,只有在调用静态方法qt_metatype_id之后才会间接调用qRegisterMetaType注册类型id。
再来看qMetaTypeid()方法,类型必须已经有QMetaTypeId的具现,最终调用qRegisterMetaType注册类型,如果只有宏,类型实际上并没有注册,QMeta::type()也就查不到id,Queued信号槽也不能传递该参数。
template <typename T>
inline Q_DECL_CONSTEXPR int qMetaTypeId()
{
Q_STATIC_ASSERT_X(QMetaTypeId2<T>::Defined, "Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system");
return QMetaTypeId2<T>::qt_metatype_id();
}
template <typename T>
struct QMetaTypeId2
{
enum { Defined = QMetaTypeId<T>::Defined, IsBuiltIn=false };
static inline Q_DECL_CONSTEXPR int qt_metatype_id() { return QMetaTypeId<T>::qt_metatype_id(); }
};
qRegisterMetaType()方法与QMetaType::type()
qRegisterMetaType()就是将类型与类型名关联,之后可以用QMetaType::type()查找类型id
typedef QString CustomString;
qRegisterMetaType<CustomString>("CustomString");
QMetaType::type()类似qMetaTypeId返回类型id,不过是在runtime查找注册的类型,而qMetaTypeId在编译时。
const char * QMetaType::typeName(int typeId)
同一类型的不同alias注册后的id为同一值,查找时获取的是最原始的类型名。
关于QT内置类型以及类型alias
QT内置类型已经有id,可以用Q_DECLARE_METATYPE宏定义,不会报错,但也没什么效果,因为不使用宏调用qMetaTypeid()可以获取类型id,不调用qMetaTypeid()就可以用QMeta::type()查到id,说明已经注册。
QT内置类型的alias,可以用Q_DECLARE_METATYPE宏定义,不会报错,但也没什么效果,调用qMetaTypeid()可以获取原类型id, 但是QMeta::type()不行(调用qMetaTypeid()之后也不行), 所以内置类型别名用于Queued信号槽还需要调用qRegisterMetaType注册类型名。
自定义类型在Q_DECLARE_METATYPE宏定义,调用qMetaTypeid()后,QMeta::type()可以查找到正确id。
自定义类型alias与原类型间只能有一个用Q_DECLARE_METATYPE宏定义,其余的具现化都会编译错误,但只要一个用宏定义后看,其余的都能用qMetaTypeid()查询到正确id,QMeta::type()需要在qMetaTypeid()或qRegisterMetaType之后才能正确查找。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。