一、meta-object简介
meta-object即是qt的元对象系统,下文都以元对象系统进行描述。在qt中,具有标志性特征的则是信号和槽函数机制,该机制的背后实现本质上则是元对象系统。编写qt代码的时候,在定义类的时候,需要放置一个q_object,为什么呢?后文会描述到,例如如下代码:
image-20230207220020441
q_object本质上是一个宏定义,在进行qt开发时,所有qobject的派生类都推荐在头文件中放置q_object宏定义,该宏定义如下(出自qobjectdefs.h文件):
#define q_object public: qt_warning_push q_object_no_override_warning static const qmetaobject staticmetaobject; virtual const qmetaobject *metaobject() const; virtual void *qt_metacast(const char *); virtual int qt_metacall(qmetaobject::call, int, void **); qt_tr_functions private: q_object_no_attributes_warning q_decl_hidden_static_metacall static void qt_static_metacall(qobject *, qmetaobject::call, int, void **); qt_warning_pop struct qprivatesignal {}; qt_annotate_class(qt_qobject, )
qt中,元对象系统包含了支持元对象系统的程序,宏定义,基类,接口函数。这些东西共同构成了qt的元对象系统:
(1)qobject为想要使用元对象系统的对象提供了基类。
(2)q_object宏用于启动元对象特性,例如:动态属性、信号和槽函数机制等。
(3)元对象编译器(moc)为每个qobject子类生成实现元对象特性所需要的代码。
在实际qt程序设计中,在派生自qobject的类定义中加上q_object后,则可以使用元对象系统所支持的特性了。
二、元对象系统背后机制
如果在派生自qobject的类定义中加上了q_object后,在编译构建过程中,元对象系统的moc工具(本文以windows平台为例,该工具则位于具体qt版本目录下的bin目录中)
在windows命令行下运行,可获知如下信息:
在qtcreator集成开发环境中,当点击构建按钮后,qtcreator会自动调用moc工具,该工具会读取一个c++源文件,如果它发现一个或多个包含q_object宏的类声明,那么则会生成另外一个c++源文件,源文件中包含每个类的元对象代码。接着,生成的源文件要么被#include包含到类的源文件中,要么被编译并链接到类的实现中。例如下列一个简单的项目工程,源码结构如下:
从上图可知,工程中包含了一个main.cpp、一个主窗口描述文件mainwindow.cpp/.h、一个stylesheeteditor.cpp/.h文件,由于mainwindow.cpp/.h、stylesheeteditor.cpp/.h支持qt的元对象系统,在编译构建过程中,则会生成支持元对象系统的中间文件,如下图所示:
从上图可知,这些文件都以moc_xxx方式进行命名,最后结合其他的文件生成了程序可执行体(stylesheet.exe),整个过程可如下图所示(windows平台):
三、再谈元对象系统
除了提供对象之间通信的信号和槽函数机制(这是引入该系统的主要原因),元对象系统还提供以下的功能:
(1)object::metaobject():返回类的关联元对象。
(2)qmetaobject::classname():在运行时以字符串的形式返回类名,而不需要通过c++编译器提供本地运行时类型信息(rtti)支持。
(3)qobject::inherits():函数返回一个对象是否是在qobject继承树中继承指定类实例。
(4)qobject::tr()和qobject::trutf8()为国际化翻译字符串。
(5)qobject::setproperty()和qobject::property()根据名称动态设置和获取属性。
(6)qmetaobject::newinstance():构造类的新实例。
除了上述所列的功能,还可以使用qobject_cast()对qobject类执行动态强制类型转换,qobject_cast()函数的行为类似于标准c++ 的dynamic_cast(),它的优点是不需要rtti支持,并且可以跨动态库工作。该函数尝试将其参数转换为尖括号中指定的指针类型,如果对象的类型正确(在运行时确定),则返回非零指针;如果对象的类型不兼容,则返回nullptr。
例如,假设有一个mywidget继承自qwidget,并使用了q_object宏声明,然后使用new创建该实例:
qobject *obj = new mywidget;
类型为qobject *的obj变量实际上引用了一个mywidget对象,所以我们可以对它进行适当的类型转换,如下代码:
qwidget *widget = qobject_cast(obj);
从qobject到qwidget的转换是成功的,因为该对象实际上是一个mywidget,它是qwidget的一个子类。既然知道obj是一个mywidget,我们也可以将它cast到mywidget *,如下代码:
mywidget *mywidget = qobject_cast(obj);
上述代码对mywidget的转换是也成功的,因为qobject_cast()在内置qt类型和自定义类型之间没有区别。
然而对于下列代码:
qlabel *label = qobject_cast(obj);
对qlabel的强制转换将失败,会将指针设置为0。因此可以在运行时处理不同类型的对象,例如:
if (qlabel *label = qobject_cast(obj)) { label->settext(tr(iriczhao)); } else if (qpushbutton *button = qobject_cast(obj)) { button->settext(tr(嵌入式小生)); }
上述代码使用qobject_case()对obj进行了向qlabel和qpushbutton的强制转换,如果转换成功,则设置对应的显示文本。
四、小生总结
在实际qt开发过程中,虽然可以在没有q_object宏和元对象代码的情况下将qobject作为基类,但如果没有使用q_object宏,则信号和槽函数机制或在本文中描述的其他特性都不能使用。从元对象系统的角度来看,一个没有元代码的qobject子类等价于它最近的有元对象代码的祖先。这意味着,例如,qmetaobject::classname()将不会返回自己类的实际名称,而是这个祖先的类名称。
因此,在实际qt开发过程中,无论实际上是否使用了信号和槽函数机制,都强烈建议qobject的所有子类都使用q_object宏。
数字资产点对点交易场外模式系统平台开发
深圳全面完成了5G SA组网,中国的5G网络建设步入了第二个阶段
全球能源转型的四种趋势和特征分析
Zigbee技术参数和通信距离
医疗服务机器人“艾菲仕”已在多家医院试点使用,可实现远程会诊
Qt“灵魂”之Meta-Object系统
数字通信系统中信噪比在电路设计中的应用分析
MAX5803-MAX5805单通道,低功耗数模转换器
九城签定私募约定书 将获3000万美元融资坐实私有化传言
安卓系统4G智能执法记录仪的功能优势
小米6、华为P10也许成为它的陪衬!国产“性能黑马”, 搭载helio X30惊艳归来
电阻的稳定性
斩获“卓越贡献企业”,东山精密、强力巨彩等有怎样的故事?
特斯拉电池技术高安全性背后的秘密是什么
中关村苹果授权店将起航
华数机器人全新高防护工业协作机器人HSR-CR605重磅发布
一种可用于单片机的中断高效处理与事件机制方法
盘点北斗智联2021年感动时刻
善睐物联:消费级路由器——让家庭网络更快速稳定的选择
PI将为半导体集成化发展提供“源”动力