Qt中以Q_DECLARE开头的几个宏的用法说明

目录

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
class CTest /* : public QObject */
{
public:

	CTest() {}
	CTest(const CTest&) {}
	~CTest() {}
};

 

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

#include 
#include
#include
#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" 的成员	

        调用该宏时,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();

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

  • 指向从QObject派生的对象的指针。
  • QList、QVector、 QQueue、QStack, QSet、QLinkedList,这里T是一个注册的Qt元类型。
  • QPointer, QSharedPointer, QWeakPointer,这里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 
  #include

  // 对std::deque进行声明,否则下面的会报错
  Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(std::deque)

  void someFunc()
  {
     std::deque container;
    QVariant dequeVar = QVariant::fromValue(container); // 将std::deque放到QVariant,以便后期取出来
    auto dequeCotainter = dequeVar.value< std::deque >();
    if (!dequeCotainter.empty())
    {
        dequeCotainter.front(); // 取出第1个QFile指针对象
    }
  }

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

4. Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE 

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

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


  #include

  Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(std::unordered_map)

  void someFunc()
  {
     std::unordered_map 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 >(); // 取出存入到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 

  Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr)

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

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

你可能感兴趣的:(QT,qt,Q_DECLARE)