Qt中:Q_DECLARE开头的 宏的用法

原文链接:https://blog.csdn.net/danshiming/article/details/130954094

1. 前言

2. Q_DECLARE_METATYPE

3. Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE

4. Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE 

5. Q_DECLARE_SMART_POINTER_METATYPE

1. 前言

       Qt通过Q_DECLARE开头的几个宏及几个qRegister开头的函数向Qt元系统注册一些非基本类型。一旦注册后,在Qt元系统中就可以很方便的利用这些非基本类型,这样对编程中的数据交互很方便。非基本类型是指除了一些基本类型之外的类型,如:除了int、float、qint......之外的类型,如:自定义类、结构体、枚举等。qRegister开头的函数用法参见如下:

Qt中以qRegister开头的几个函数的用法说明。

2. Q_DECLARE_METATYPE  

     这个宏定义如下:

Q_DECLARE_METATYPE(Type)
      这个宏能使Qt元系统的QMetaType知道Type类型,但前提是Type类型必须提供一个公有的默认构造函数、公有的默认拷贝构造函数和公有的默认析构函数。当在QVariant中使用自定义类型时,利用该宏向Qt元系统声明自定义类型是必须的,否则编译会报错。

         如下为自定义的类:

#pragma once
#include<QObject>
class CTest /* : public QObject */
{
public:
 
    CTest() {}
    CTest(const CTest&) {}
    ~CTest() {}
};

在main中,设置CTest对象到QVariant中:

#include <QtCore/QCoreApplication>
#include<unordered_map>
#include<QDebug>
#include "test.h"
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
 
    CTest s;
    QVariant varTest;
    varTest.setValue(s); // 将s设置到 QVariant类对象中,以便后期取出来使用
 
    return a.exec();
}

则报错如下:

C2338    static_assert failed: 'Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system'    
C2039    "qt_metatype_id": 不是 "QMetaTypeId<T>" 的成员    


        调用该宏时,Type必须是完全定义的类型,也就是说,调用该宏的时候Type类型对象是已经完全构造出来的。当一个类对象还在构造函数里面,即构造函数还没调用完成,则称为不是完全定义类型,就像动物或人怀孕在妈妈肚子里面还没生出来时,这个时候严格来说还不能叫真正的动物或人,因为还没成型。

      通常理想的做法是:把这个宏放在类或结构体声明的下(后)方,如果做到这样很难,也可以放在一个专门的头文件中,当每次在QVariant中用Type类型时,包含该头文件即可。

       添加一个Q_DECLARE_METATYPE()宏,能使type类型让所有基于模板的函数识别,包括QVariant。注意:如果是在基于队列的信号、槽连接或基于QObject的属性系统中,依然需要用qRegisterMetaType()进行注册该类型,因为这些名称是在运行时被决定的。

       下面的例子,显示了Q_DECLARE_METATYPE()的典型用法:

 struct MyStruct
 {
     int i;
     ...
 };
 Q_DECLARE_METATYPE(MyStruct)  // 该宏放在类或结构体声明的最后面
 

如果MyStruct在命名空间中,Q_DECLARE_METATYPE()必须在命名空间外部,如下:

namespace MyNamespace
{
     ...
}
Q_DECLARE_METATYPE(MyNamespace::MyStruct) //在命名空间的作用域外部,且放在命名空间声明的最下面

现在MyStruct能被QMetaType识别了,它也能被用于QVariant中:

MyStruct s;
QVariant var;
var.setValue(s); // 拷贝 s 到QVariant
  ...
 
// 取出QVariant中的值
MyStruct s2 = var.value<MyStruct>();

一些类型被自动注册,所以这些类型不需要这个宏对其声明:

指向从QObject派生的对象的指针。
QList<T>、QVector<T>、 QQueue<T>、QStack<T>, QSet<T>、QLinkedList<T>,这里T是一个注册的Qt元类型。
QPointer<T>, QSharedPointer<T>, QWeakPointer<T>,这里T是从QObject派生的类型。
 用Q_ENUM 、Q_FLAG注册的枚举类型。
 包含有Q_GADGET宏的类。
对于第1条说明如下:

将上面的CTest改为从QObject派生,且不用Q_DECLARE_METATYPE(CTest)进行元类型声明时,设置一个CTest的指针到QVariant则不会报错,如下:

 CTest* s;  // 指向CTest的指针
 QVariant var;
 var.setValue(s); // 拷贝 s 到QVariant,不会报错

但如果不用Q_DECLARE_METATYPE(CTest)进行元类型声明,设置一个CTest的非指针对象且到QVariant则会报错,如下会报错:

CTest s;  //CTest是从QObject派生的子类
QVariant var;
var.setValue(s);//拷贝 s 到QVariant,如果不用Q_DECLARE_METATYPE(CTest)进行元类型声明,会报错

3.Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE

        这个宏对序列容器进行声明,以便Qt的元类型能知道该序列容器,这样就可以将一个序列容器的实例放到QVariant中,前提是Qt的元类型对序列容器中的每个元素是知道的,即序列容器中的每个元素是Qt的元类型或声明过或注册过的类型。

        Qt的所有序列容器(如:QVector、QList等)都内置对该宏的支持,所以对于Qt的所有序列容器不需要用该宏进行声明,std::vector 、 std::list也内置对该宏的支持。

#include <deque>
#include<QFile>
 
// 对std::deque进行声明,否则下面的会报错
Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(std::deque)
 
void someFunc()
{
    std::deque<QFile*> container;
    QVariant dequeVar= QVariant::fromValue(container);//将std::deque放到QVariant,以便后期取出来
    auto dequeCotainter = dequeVar.value< std::deque<QFile*> >();
    if (!dequeCotainter.empty())
    {
        dequeCotainter.front(); // 取出第1个QFile指针对象
    }
}


     上述代码, 利用Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE宏对std::deque声明,这样就可以将std::deque保存到QVariant中了,如果不用该宏声明,则编译会报错。。

4.Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE 

        这个宏对关联容器进行声明,以便Qt的元类型能知道该关联容器,这样就可以将一个关联容器的实例<T, U>放到QVariant中,前提是Qt元类型对关联容器中的每个元素T, U是知道的,即关联容器中的每个元素的键值对类型是Qt的元类型或声明过或注册过的类型。

        Qt的所有关联容器(如:QMap)都内置对该宏的支持,所以对于Qt的所有关联容器不需要用该宏进行声明,std::map也内置对该宏的支持。

#include<unordered_map>
 
Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(std::unordered_map)
 
void someFunc()
{
    std::unordered_map<int, bool> unorderedMap;
    unorderedMap.insert_or_assign(1, true); // 插入几个值到关联容器
    unorderedMap.insert_or_assign(2, true);
 
    QVariant var = QVariant::fromValue(unorderedMap); // 将关联容器存入QVariant对象
    auto myCotainter = var.value< std::unordered_map<int, bool> >();//取出存入到QVariant中的关联容器对象
    for (auto [id, isExist] : myCotainter) // 遍历输出关联容器的键值对,注意:这里用到了C++17
    {
        qDebug() << id << " ---- > " << isExist << "\r\n";
    }
    // ...
}

      上述代码, 利用Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE宏对std::unordered_map声明,这样就可以将std::unordered_map保存到QVariant中了,如果不用该宏声明,则编译会报错。

5.Q_DECLARE_SMART_POINTER_METATYPE     

         这个宏向Qt的元系类型声明智能指针类型,以便让智能指针类型能让Qt元类型知道。该宏使智能指针对象能存入到QVariant对象中,前提是智能指针的T类型必须从QObject派生的。

        注意:QWeakPointer、QSharedPointer 、 QPointer已经内置了对这个宏的支持,所以它们不需要利用该宏进行声明。下面的代码片段显示了该宏的用法:

#include <memory>
 
Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr)
 
void someFunc()
{
     auto smart_ptr = std::make_shared<QFile>();// 一个封装QFilel类型的智能指针
     QVariant var = QVariant::fromValue(smart_ptr);// 将智能指针保存到QVariant中
     // ...
     if (var.canConvert<QObject*>()) 
     {
         QObject *sp = var.value<QObject*>();// 取出刚才保存的QFilel类型对象
         qDebug() << sp->metaObject()->className(); // Prints 'QFile'.
     }
}

上述代码, 利用Q_DECLARE_SMART_POINTER_METATYPE宏对智能指针声明,这样就可以将智能指针保存到QVariant中了,如果不用该宏声明,则编译会报错。

原文链接:https://blog.csdn.net/danshiming/article/details/130954094

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值