一、Meta-Object简介
Meta-Object即是Qt的元对象系统,下文都以元对象系统进行描述。在Qt中,具有标志性特征的则是信号和槽函数机制,该机制的背后实现本质上则是元对象系统。编写Qt代码的时候,在定义类的时候,需要放置一个Q_OBJECT,为什么呢?后文会描述到,例如如下代码:
image-20230207220020441
Q_OBJECT本质上是一个宏定义,在进行Qt开发时,所有QObject的派生类都推荐在头文件中放置Q_OBJECT宏定义,该宏定义如下(出自qobjectdefs.h文件):
#defineQ_OBJECT public: QT_WARNING_PUSH Q_OBJECT_NO_OVERRIDE_WARNING staticconstQMetaObjectstaticMetaObject; virtualconstQMetaObject*metaObject()const; virtualvoid*qt_metacast(constchar*); virtualintqt_metacall(QMetaObject::Call,int,void**); QT_TR_FUNCTIONS private: Q_OBJECT_NO_ATTRIBUTES_WARNING Q_DECL_HIDDEN_STATIC_METACALLstaticvoidqt_static_metacall(QObject*,QMetaObject::Call,int,void**); QT_WARNING_POP structQPrivateSignal{}; 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=newMyWidget;
类型为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")); } elseif(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宏。
审核编辑:汤梓红
-
函数
+关注
关注
3文章
4350浏览量
63059 -
代码
+关注
关注
30文章
4841浏览量
69209 -
Meta
+关注
关注
0文章
293浏览量
11455 -
编译器
+关注
关注
1文章
1642浏览量
49344 -
Qt
+关注
关注
1文章
309浏览量
38122
原文标题:Qt“灵魂”之Meta-Object系统
文章出处:【微信号:嵌入式小生,微信公众号:嵌入式小生】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
【AWorks试用体验】学习笔记(3)一个关于 Qt 的 demo
如何对meta-toolchain-qt5进行bitbake?
Qt 跨平台C++图形用户界面应用程序开发框架
Yocto添加meta-webkit层时出现do_compile错误怎么解决?
【米尔MYD-JX8MMA7开发板-ARM+FPGA架构试用体验】十一、QT-HMI V2.0系统
bitbake imx-image-full时出现qt6错误怎么解决?
Java Object Serialization Spec
什么是CORBA (Common Object Reques
Qt图形编程基础之使用Qt编写“Hello,World”程序实验
![<b class='flag-5'>Qt</b>图形编程基础<b class='flag-5'>之</b>使用<b class='flag-5'>Qt</b>编写“Hello,World”程序实验](https://file.elecfans.com/web2/M00/4A/10/pYYBAGKhvJGABSLEAABT8qiN-PM266.png)
嵌入式linux应用开发之QT
Object类中的所有方法
![<b class='flag-5'>Object</b>类中的所有方法](https://file1.elecfans.com/web2/M00/A9/C1/wKgZomUovfqACzDIAAB-sGpEaUk706.jpg)
评论