解读qRegisterMetaType

解读qRegisterMetaType


qRegisterMetaType(const char *typeName)
代码来源:src/corelib/kernel/qmetatype.h

template <typename T>
int qRegisterMetaType(const char *typeName)
{
    typedef void*(*ConstructPtr)(const T*);
    ConstructPtr cptr = qMetaTypeConstructHelper<T>;
    typedef void(*DeletePtr)(T*);
    DeletePtr dptr = qMetaTypeDeleteHelper<T>;

    return QMetaType::registerType(typeName, reinterpret_cast<QMetaType::Destructor>(dptr),
                                   reinterpret_cast<QMetaType::Constructor>(cptr));
}
该函数的核心就是调用了 registerType 函数
两个Helper模板函数分别对构造和析构函数进行封装
registerType
代码来源:src/corelib/kernel/qmetatype.cpp

int QMetaType::registerType(const char *typeName, Destructor destructor, Constructor constructor)
函数功能:
    根据类型名查找其MetaType类型,如果已存在,则直接返回;否则创建后返回。
    创建一个 !QCustomTypeInfo 对象
    该对象包含要类型的构造、析构信息,已经规范化后的类型名
    该对象存入一个全局的!QVector中
    
qRegisterMetaType()
根据手册,我们可以知道,qRegisterMetaType 还有一个无参的重载函数。
template <typename T>
inline int qRegisterMetaType()
{
    return qMetaTypeId(static_cast<T *>(0));
}
函数看起来和带参数的那个似乎区别很大(确实区别很大)。
手册中告诉我们,执行这个的时候,模板参数T必须用 Q_DECLARE_METATYPE() 声明过

这个宏用来注册一个类(含默认构造、默认析构、拷贝构造函数)为QMetaType类型 ,注册后的类型可以作为QVariant的自定义类型。
这个宏应该放在类或者结构体外面的下面,也可以放在一个非公用引用的头文件里,在每次使用QVariant的时候引用该头文件。
想在信号队列、槽、 QObject’s property system中使用该类型,还得用qRegisterMetaType()注册该类型

Q_DECLARE_METATYPE
代码来源:src/corelib/kernel/qmetatype.h

#define Q_DECLARE_METATYPE(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 (!metatype_id)                                       
                    metatype_id = qRegisterMetaType< TYPE >(#TYPE);     
                return metatype_id;                                     
            }                                                           
    };                                                                  
    QT_END_NAMESPACE
    
Q_DECLARE_METATYPE
如果要使自定义类型或其他非QMetaType内置类型在QVaiant中使用,必须使用该宏。
该类型必须有公有的 构造、析构、复制构造 函数
qRegisterMetaType 必须使用该函数的两种情况
如果非QMetaType内置类型要在 Qt 的属性系统中使用
如果非QMetaType内置类型要在 queued 信号与槽 中使用
二者关系
    二者的代码区别:
    Q_DECLARE_METATYPE 展开后是一个特化后的类 QMetaTypeId<TYPE>
    qRegisterMetaType 将某类型注册中 MetaType 系统中

    二者的联系:
    QMetaTypeId<TYPE>的类中的成员包含对qRegisterMetaType的调用
    我们知道类中的成员函数并不一定会被调用(即,该宏并不确保类型被注册到MetaType)。
    通过qRegisterMetaType可以确保类型被注册
    两个qRegisterMetaType 的联系

    无参的qRegisterMetaType函数会通过该成员调用带参数的qRegisterMetaType()

知道原因了吧?注意看上面 Q_DECLARE_METATYPE() 代码,
对了。类中的成员函数qt_metatype_id中包含对qRegisterMetaType(typeName)的调用
这儿就是辗转调用了这个带参数的qRegisterMetaType函数 

如果是自己定义的类型如果想使用signal/slot来传递的话,则没有这么简单。直接使用的话,会产生下面这种错误:
QObject::connect: Cannot queue arguments of type 'TextAndNumber' (Make sure 'TextAndNumber' is registed using qRegisterMetaType().) 
原因:当一个signal被放到队列中(queued)时,它的参数(arguments)也会被一起一起放到队列中(queued起来),这就意味着参数在被传送到slot之前需要被拷贝、存储在队列中(queue)中;为了能够在队列中存储这些参数(argument),Qt需要去construct、destruct、copy这些对象,而为了让Qt知道怎样去作这些事情,参数的类型需要使用qRegisterMetaType来注册(如错误提示中的说明)
步骤:(以自定义 WPARAM 类型为例)
自定一种类型,在这个类型的顶部包含:#include <QMetaType>
在类型定义完成后,加入声明:Q_DECLARE_METATYPE(WPARAM);
在main()函数中注册这种类型:
    qRegisterMetaType<WPARAM>("WPARAM");
    qRegisterMetaType<LPARAM>("LPARAM"); 
如果还希望使用这种类型的引用,可同样要注册:
    qRegisterMetaType<WPARAM>("WPARAM&");
    qRegisterMetaType<LPARAM>("LPARAM&"); 

#include <QMetaType>     
//必须包含QMetaType,否则会出现下面错误:     
//error: expected constructor, destructor, or type conversion before ‘;’ token     
#include <QString>     
class TextAndNumber {     
public:     
    TextAndNumber();     
    TextAndNumber(int, QString);     
    int count();     
    QString text();     
private:     
    int m_count;     
    QString m_text;     
};     
Q_DECLARE_METATYPE(TextAndNumber);     
#endif // TEXTANDNUMBER_H    

int main(int argc, char *argv[])     
{     
QApplication app(argc, argv); 
    qRegisterMetaType<TextAndNumber>("TextAndNumber");     
    qRegisterMetaType<TextAndNumber>("TextAndNumber&");     
    TextDevice device;     
    TextThread foo("foo"), bar("bar");     
    QObject::connect(&foo, SIGNAL(writeText(TextAndNumber&)), &device, SLOT(write(TextAndNumber&)));     
    QObject::connect(&bar, SIGNAL(writeText(TextAndNumber&)), &device, SLOT(write(TextAndNumber&)));  
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值