wzzzx:来源: https://woboq.com/blog/qmetatype-knows-your-types.html
QMetaType
是Qt
用来获取类型动态运行信息的工具。通过它能够使诸如QVariant
的类能够封装自定义类型,复制信号队列中的参数等事情。
如果你想知道Q_DECLARE_META_TYPE
和qRegisterMetaType
是做什么的,该什么时候使用,可以读一下。这篇文章会告知关于QMetaType
的相关信息:为什么需要这么一个类,什么时候使用它,它是如何工作的。
为什么 Qt 需要运行时动态类型信息
首先需要回顾一下Qt
的历史。QMetaType
在Qt4.0
被引入。创建这样一个类是为了解决异步的信号机制(Qt::QueuedConnection)
。为了使队列中等待调用的槽函数能够正常工作,信号中参数会被复制和存储起来,以便事件循环函数能够正确调用。
为了能够正常调用队列中的槽函数,信号中的参数会被复制,存储在稍后处理的事件中。同时,当完成槽函数的调用后,还需要删除这些副本。但是使用Qt::DirectConnection
参数的连接就不需要了,槽函数可以直接使用栈上的参数。
在QMetaObject::activate
中用于分发信号的代码有一个指针数组指向信号参数。但是Qt
知道的参数类型都是moc
所导出的名称字符串。
QMetaType
提供了一种方式能够直接从字符串,例如"QPoint"
,去获取副本和销毁对象的方法。Qt
能够使用void *QMetaType::create(int type, void *copy)
和QMetaType::destroy(int type, void *data)
来拷贝和销毁参数。对于int
类型,则可以使用QMetaType::type(const char *typeName)
来获取,这些都是moc
所提供的能力。QMetaType
还提供了方法让开发者能够注册任意类型到元对象数据库中。
另一个例子是QVariant
。在Qt3.x
版本中的QVariant
仅支持内置类型,因为QVariant
所包含的类型需要跟QVariant
一起被拷贝和销毁。但是有QMetaType
后,QVariant
能够封装任意已注册的类型,因为QVariant
能够跟拷贝和销毁这些对象实例。
QMetaType 持有什么信息
Qt4.0
之后很多东西发生了改变。QtScript
和QML
加强了对动态类型集成的使用,所以需要额外的进行很多优化。
下面是每个类型在元对象系统中会持有的信息:
- 类型名 类型名需要被注册。并且有一个用于快速查找元类型 id 的名称索引。在
Qt4.7
之后,能够被相同的类型注册不同的名字,这个特性对于typedef
非常有用。 - 拷贝构造函数,析构函数
- 大小 大小用来在栈上分配空间,内联成员的构造
- Flags
flags
用于指定一些跟QTypeInfo
相同的信息或转换方式 - 自定义转换函数 通过
QMetaType::registerConverter
设置 - QMetaObject 含有元
QObject
数据。此外,如果存在类型信息的话,也会保存其中 - 。。。
QTypeInfo
QTypeInfo
是一个与QMetaType
相互正交的trait class,它允许开发者通过Q_DECLARE_TYPEINFO
手动指定类型是否可以借助memmove
移动或类型的构造函数 /析构函数是否能够运行。QTypeInfo
主要用于优化容器的存储。
例如,隐式分享类可以通过memmove
移动。而普通的拷贝则需要在拷贝构造函数中增加引用计数,在析构函数用减少引用计数。
C++11
引入移动构造函数和标准traits
类型来解决这个问题,但是QTypeInfo
是在C++11
之前设计出来的,并且需要兼容一些旧版本的编译器,所以只好将就使用。
工作方式
因为一些历史原因,对于内置类型和自定义类型有不同的处理方式。对于QtCore
中的内置类型,每个meta-type
函数都指定了特定的函数进行处理。不过在Qt5.0
使用模板进行了重构。但让我们感兴趣的是对自定义类型处方式。
QVector<QCustomTypeInfo>
这个类含有相关信息和一系列函数指针。
Q_DECLARE_METATYPE
宏
这个宏针对于特殊的类型特化了模板QMetaTypeId
。实际上,它特化了模板类QMetaTypeId2
和大多数QMetaTypeId2
用到的函数。这里并不清楚这背后的原因。也许这样Qt
可以添加更多的内置类型而不会破坏以前使用Q_DECLARE_METATYPE
的代码。
QMetaTypeId
通过调用qMetaType<T>()
中的QMetaTypeId::qt_metatype_id
在编译时指定类型所对应的元类型id。第一次调用的时候,这个函数会调用QMetaType
的内部函数来给自定义类型注册和分配元类型id,同时使用调用这个宏时所填写的名称。这些信息会被存储在一个static
变量中。
除了类型名称,其他的信息会通过模板在编译自动推导。
qRegisterMetaType
使用Q_DECLARE_METATYPE
注册的类型将在首次使用qMetaTypeId()
时进行实际注册并分配一个id。使用QVariant
封装一个类型就是典型的例子。但是在连接信号和槽函数的时候并不会注册,这需要你使用qRegisterMetaType
进行手动注册。
自动注册
开发者经常在看到编译错误或者运行错误的是才想起来他们忘了注册自定义类型。如果不需要这一步骤,那不是很好吗?其实,Q_DECLARE_METATYPE
存在的唯一原因就是需要来获取类型的名称。但是在某些情况下,我们并不需要这个宏也可以在运行时获取到类型名称。例如,对于QList<T>
,如果T
是已经注册的类型,我们可以在类型系统里检索到,也可以使用"QList<" + QMetaType::name(qMetaTypeId<T>()) + ">"
来进行构建。我们可以在一系列模板类上做这种操作,例如QList, QVector, QSharedPointer, QPointer, QMap, QHash...
。我们甚至可以在moc
的帮助下,直接定义指向QObject
子类的指针,使用T::staticMetaObject.className() + "*"
即可。在Qt5.5
,还能自动声明Q_GADGET
和Q_ENUM
。
这就是Q_DECLARE_METATYPE
所作的工作,但是要在Q_PROPERTY
或者信号参数中使用自定义类型,还是需要使用qRegisterMetaType
注册。但是从Qt5.x
开始,如果moc
可以确定该类型可以注册为元类型,则moc
生成的代码将自动调用qRegisterMetaType
。
探索
在Qt5.0
之前,我尝试过能够在不需要名称的情况下摆脱Q_DECLARE_METATYPE
的使用,大概的做法如下:
template<typename T> QMetaTypeId {
static int qt_metatype_id() {
static int typeId = QMetaType::registerMetaType(/*...*/);
return typeId;
}
};
按照C++
的标准实现,对于每个类型都会有一个对应的实例来生成QMetaTypeId::qt_metatype_id()::typeId
。但是实际上一些编译器和链接器并不遵守这些规则。特别的,在Windows
上,即便使用了导出宏,对于每个库还是只会生成一份实例。因此我们始终需要一个我们没有的名称标识符,所有在Qt5
上,还是需要通过注册类型的方式来获取名称。
wzzzx:哈哈,有翻译的不好的地方请多多指教~
Qt 怎么学?HeapOverflow:最近对集成系统的前端有了些兴趣。之前写过 windows form,wpf,uwp,最近两年在写 flutter,然后最近试着学了 swiftUI,感觉 app 开发者太多了,饭碗不够硬。就想到了 Qt,想学学集成系统的前端开发,比如说汽车的中控,电子仪表盘这类的。有什么好的学习路径吗?
[翻译] const 是否会对程序的优化产生影响wzzzx:来源:http://www.gotw.ca/gotw/081.htm 1. 函数参数声明为const和返回值声明为const,是否会对编译器优化代码产生影响? // Example 1 // const Y& f( const X& x ) { // ... do something with x and find a Y obj…
Jump Desktop 通过 DRP 连接 windows10,只要没操作,马上就断开连接,怎么破ubuntuGary:家里整了台小 nas,用 windows10 lts 系统,平时不在家自己睡眠,回家用 Jump Desktop 连接会网络唤醒并登陆,但是没操作一两分钟,就会断开,很烦,可有解决方法?
通过 MapReduce 降低服务响应时间kevinwan:通过 MapReduce 降低服务响应时间 在微服务中开发中,api 网关扮演对外提供 restful api 的角色,而 api 的数据往往会依赖其他服务,复杂的 api 更是会依赖多个甚至数十个服务。虽然单个被依赖服务的耗时一般都比较低,但如果多个服务串行依赖的话那么整个 api 的耗时将会大大增加。 那么通过什么手段来优化呢?我们首先…
不懂就问, PDA 设备(扫码设备,扫条形码) 通过 web 方式怎么获取扫描的条形码visonme:节前一问如题, 可以按传统的条码扫描枪那样对待吗? 就是让然 PDA 加载页面,给它一个带焦点的文本输入框,然后扫描时候,条形码会自动显示在文本框中吗?因为第一次接触做 PDA 设备,也不知道这种做法靠不靠谱,手上也没设备, 有过类似案例的 V 友能提供点思路码? 查过设备厂家,厂家有提供 SDK 针对安卓和 windowCE 的,还有提供一…