详细分析Qt中moc文件

详细分析Qt中moc文件
  一直想写一片详细分析Qt中moc文件的文章,今天终于是完成了。迫不及待的分享给大家,希望大家可以赏个脸,认真的看完,希望对大家的学习也有帮助。请看下面的分析Qt中moc文件的详细内容。
  Qt 不是使用“标准的”C++语言编写,而是对其进行了一定程度的扩展。我们可以从Qt增加的关键字看出来:signals、slots或emit。但是使用gcc编译时,编译器并不认识这些非标准c++的关键字,那么就需要Qt自己将扩展的关键字处理成标准的C++代码。Qt在编译之前会分析源文件,当发现包含了 Q_OBJECT 宏,则会生成另外一个标准的C++源文件,这个源文件中包含了 Q_OBJECT 宏的实现代码,这个源文件名字是将原文件名前面加上 moc_ 构成,这个新的文件同样将进入编译系统,最终被链接到二进制代码中去,此时,Qt将自己增加的扩展转换成了标准的C++文件,moc 全称是 Meta-Object Compiler,也就是“元对象编译器”。这就是moc文件的由来。
  下面我们来分析一下Moc文件:
  一 示例代码如下:
  #include
  class CTestMoc : public QObject
  {
  Q_OBJECT
  public:
  CTestMoc(){}
  ~CTestMoc(){}
  signals:
  void Test1();
  void Test2(int iTemp);
  private slots:
  void OnTest1();
  void OnTest2(int iTemp);
  };
  二 Q_OBJECT宏
  #define Q_OBJECT
  public:
  Q_OBJECT_CHECK
  static const QMetaObject staticMetaObject;
  virtual const QMetaObject *metaObject() const;
  virtual void *qt_metacast(const char *);
  QT_TR_FUNCTIONS
  virtual int qt_metacall(QMetaObject::Call, int, void **);
  private:
  Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
  struct QPrivateSignal {};
  此宏在QObjectdefs.h头文件中定义
  1 Q_OBJECT_CHECK 定义如下:
  #define Q_OBJECT_CHECK
  template inline void qt_check_for_QOBJECT_macro(const ThisObject &_q_argument) const
  { int i = qYouForgotTheQ_OBJECT_Macro(this, &_q_argument); i = i + 1; }
  宏展开最终会调用qYouForgotTheQ_OBJECT_Macro这个内联函数。这个函数始终返回0,但是很不明白,为什么之后还要添加一句 i=i?,刨根之后,发现Q_OBJECT_CHECK宏并没有做什么工作。
  inline int qYouForgotTheQ_OBJECT_Macro(T, T) { return 0; }
  2 static const QMetaObject staticMetaObject 静态的元对象,这个对象在moc文件里会构建,在那里就能看到整个信号&槽的全貌。
  3 virtual const QMetaObject *metaObject() const; 返回一个元对象。
  4 virtual void *qt_metacast(const char *); 元对象中的字符数据转换。
  5 virtual int qt_metacall(QMetaObject::Call, int, void **); 元对象调用入口,注意此函数是public的,槽函数调用也是由这个函数开始。
  6 static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); 由qt_metacall函数调用,槽函数调用真正处理函数。Q_DECL_HIDDEN_STATIC_METACALL这个宏看到最后和linux系统有关,其它系统这个宏是一个空的宏。
  三 Moc文件分析
  先说结论在这里。
  1 Qt的信号&槽之间的调用不是通过指针方式调用的而是通过索引方式来调用的.
  2 信号也是一个函数。
  Moc文件有几个重要数据结构,把这几个结构之间关系讲清楚大家就清楚Qt的信号槽机制是如何工作的了。
  第一个结构是 qt_meta_stringdata_CTestMoc_t 定义如下:
  struct qt_meta_stringdata_CTestMoc_t {
  QByteArrayData data[7];
  char stringdata[45];
  };
  data字段是一个由byte数组组成的数组,数组大小根据信号&槽个数有关,这个数组在调用QObject的connect函数时用来匹配信号名或槽名。
  stringdata 存放的是字符资源,存放全部的信号名、槽名、类名。
  static const qt_meta_stringdata_CTestMoc_t qt_meta_stringdata_CTestMoc = {
  {
  QT_MOC_LITERAL(0, 0, 8),
  QT_MOC_LITERAL(1, 9, 5),
  QT_MOC_LITERAL(2, 15, 0),
  QT_MOC_LITERAL(3, 16, 5),
  QT_MOC_LITERAL(4, 22, 5),
  QT_MOC_LITERAL(5, 28, 7),
  QT_MOC_LITERAL(6, 36, 7)
  },
  “CTestMoc\0Test1\0\0Test2\0iTemp\0OnTest1\0”
  “OnTest2\0”
  };
  qt_meta_stringdata_CTestMoc 这个就是一个 qt_meta_stringdata_CTestMoc_t结构体的实例。
  QT_MOC_LITERAL(0, 0, 8), 这个宏生成一个byte数组,第一参数是索引,大家可以看到索引是由 0 - 6 共7个组成,对应的是data字段的长度7,第二个参数是在stringdata字段中的开始位置,第三个参数是长度。
  例如 QT_MOC_LITERAL(0, 0, 8) 索引是0, 开始位置是0, 长度是8,对应的字符是"CTestMoc",后面的以此类推。
  第二个结构是 static const uint qt_meta_data_CTestMoc[]
  这个结构体描述的是信号&槽在调用时的索引、参数、返回值等信息。
  static const uint qt_meta_data_CTestMoc[] = {
  // content:
  7, // revision
  0, // classname
  0, 0, // classinfo
  4, 14, // methods
  0, 0, // properties
  0, 0, // enums/sets
  0, 0, // constructors
  0, // flags
  2, // signalCount
  // signals: name, argc, parameters, tag, flags
  1, 0, 34, 2, 0x06,
  3, 1, 35, 2, 0x06,
  // slots: name, argc, parameters, tag, flags
  5, 0, 38, 2, 0x08,
  6, 1, 39, 2, 0x08,
  // signals: parameters
  QMetaType::Void,
  QMetaType::Void, QMetaType::Int, 4,
  // slots: parameters
  QMetaType::Void,
  QMetaType::Void, QMetaType::Int, 4,
  0 // eod
  };
  这个数组的前14个uint 描述的是元对象的私有信息,定义在qmetaobject_p.h文件的QMetaObjectPrivate结构体当中,QMetaObjectPrivate结构体我们不做深入分析,但是,在这个结构体中4, 14, // methods这个信息描述的是信号&槽的个数和在表中的偏移量,即14个uint之后是信息&槽的信息
  qt_meta_data_CTestMoc这个表中我们可以看到每描述一个信号或槽需要5个uint
  例如,从表的第14个uint开始描述的信号信息
  // signals: name, argc, parameters, tag, flags
  1, 0, 34, 2, 0x06,
  3, 1, 35, 2, 0x06,
  name:对应的是qt_meta_stringdata_CTestMoc 索引,例如1 对应的是Test1
  argc:参数个数
  parameters : 参数的在qt_meta_data_CTestMoc这个表中的索引位置。
  例如 // signals: parameters
  QMetaType::Void,
  QMetaType::Void, QMetaType::Int, 4,
  void 是信号的返回值,QMetaType::Int是参数类型, 4 是参数名,在qt_meta_stringdata_CTestMoc中的索引值。
  tag:这个字段的数值对应的是qt_meta_stringdata_CTestMoc 索引,在这个moc文件里对应的是一个空字符串,具体怎么用,在源代码里没看懂。
  flags:是一个特征值,是在 enum MethodFlags 枚举中定义。
  enum MethodFlags {
  AccessPrivate = 0x00,
  AccessProtected = 0x01,
  AccessPublic = 0x02,
  AccessMask = 0x03, //mask
  MethodMethod = 0x00,
  MethodSignal = 0x04,
  MethodSlot = 0x08,
  MethodConstructor = 0x0c,
  MethodTypeMask = 0x0c,
  MethodCompatibility = 0x10,
  MethodCloned = 0x20,
  MethodScriptable = 0x40,
  MethodRevisioned = 0x80
  };
  大家可以看到,槽对应的是MethodSlot 0x08, 信号对应的是MethodSignal 和AccessPublic 相或。
  第三部分 QObject 中静态函数 qt_static_metacall 实现
  void CTestMoc::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
  {
  if (_c == QMetaObject::InvokeMetaMethod) {
  CTestMoc *_t = static_cast(_o);
  switch (_id) {
  case 0: _t->Test1(); break;
  case 1: _t->Test2((reinterpret_cast< int()>(_a[1]))); break;
  case 2: _t->OnTest1(); break;
  case 3: _t->OnTest2((reinterpret_cast< int()>(_a[1]))); break;
  default: ;
  }
  …
  }
  现在看这个就比较直观了,qt_metacall方法通过索引调用其它内部方法。Qt动态机制不采用指针,而由索引实现。实际调用方法的工作由编译器实现。这使得信号和槽的机制执行效率比较高。
  参数由一个指向指针数组的指针进行传递,并在调用方法时进行适当的转换。当然,使用指针是将不同类型的参数放在一个数组的唯一办法。参数索引从1开始,因为0号代表函数返回值。
  第四部分 QObject 中静态staticMetaObject的赋值
  const QMetaObject CTestMoc::staticMetaObject = {
  { &QObject::staticMetaObject, qt_meta_stringdata_CTestMoc.data,
  qt_meta_data_CTestMoc, qt_static_metacall, 0, 0}
  };
  ok,通过这个静态变量就保存了moc文件的信号&槽的调用索引信息。在信号&槽绑定的时候就是通过这些信息一步一步建立的绑定关系。
  第四部分 信号就是函数。
  大家可以Moc文件中看到信号的实现。
  以上就是详细分析Qt中moc文件的内容,不知道大家是否坚持看完了,相信看完后对你很有帮助。更多参考资料可以在华清远见的官网进行查看,华清远见提供免费的教程,供大家学习!

  • 28
    点赞
  • 94
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
用c++/qt写的项目,项目都经测试过,真实可靠,可供自己学习c++/qtQt是一个用标准C++编写的跨平台开发类库,它对标准C++进行了扩展,引入了元对象系统、信号与槽、属性等特性,使应用程序的开发变得更高效。 Qt类库大量的类以模块形式分类组织的,包括基本模块和扩展模块等。一个模块通常就是一个编程主题,如数据库、图表、网络等。 一、Qt核心特点 1.1.概述 Qt本身并不是一种编程语言,它本质上是一个跨平台的C++开发类库,是用标准C++编写的类库,它为开发GUI应用程序和非GUI应用程序提供了各种类。 Qt对标准C++进行了扩展,引入了一些新概念和功能,例如信号和槽、对象属性等。Qt的元对象编译器(Meta-Object Compiler,MOC)是一个预处理器,在源程序被编译前先将这些Qt特性的程序转换为标准C++兼容的形式,然后再由标准C++编译器进行编译。这就是为什么在使用信号与槽机制的类里,必须添加一个Q_OBJECT宏的原因,只有添加了这个宏,moc才能对类里的信号与槽的代码进行预处理。 Qt Core模块是Qt类库的核心,所有其他模块都依赖于此模块,如果使用qmake来构建项目,Qt Core模块则是被自动加入的。 Qt为C++语言增加的特性就是在Qt Core模块里实现的,这些扩展特性由Qt的元对象系统实现,包括信号与槽机制、属性系统、动态类型转换等。 1.2.元对象系统 Qt的元对象系统(Meta-Object-System)提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。 元对象系统由以下三个基础组成: 1.QObject类是所有使用元对象系统的类的基类; 2.在一个类的private部分声明Q_OBJECT宏,使得类可以使用元对象的特性,如动态属性、信号与槽。 3.MOC(元对象编译器)为每个QObject的子类提供必要的代码来实现元对象系统的特征。 构建项目时,MOC工具读取C++源文件,当它发现类的定义里有Q_OBJECT宏时,它就会为这个类生成另外一个包含有元对象支持代码的C++源文件,这个生成的源文件连同类的实现文件一起被编译和连接。 除了信号和槽机制外,元对象还提供如下一些功能。 1.QObject::metaObject()函数返回类关联的元对象,元对象类QMetaObject包含了访问元对象的一些接口函数,例如QMetaObject::className()函数可在运行时返回类的名称字符串。 QObject obj=new QPushButton; obj->metaObject()->className(); 2.QMetaObject::newInstance()函数创建类的一个新的实例。 3.QObject::inherits(const charclassName)函数判断一个对象实例是否是名称为className的类或QObject的子类的实例。 1.3.属性系统 1.属性定义 Qt提供一个Q_PROPERTY()宏可以定义属性,它也是属于元对象系统实现的。Qt的属性系统与C++编译器无关,可以用任何标准的C++编译器编译定义了属性的Qt C++程序。 2.属性的使用 不管是否用READ和WRITE定义了接口函数,只要知道属性名称,就可以通过QObject::property()读取属性值,并通过QObject::setProperty()设置属性值。 3.动态属性 QObject::setProperty()函数可以在运行时为类定义一个新的属性,称之为动态属性。动态属性是针对类的实例定义的。 动态属性可以使用QObject::property()查询,就如在类定义里用Q_PROPERTY宏定义的属性一样。 例如,在数据表编辑界面上,一些字段是必填字段,就可以在初始化界面时为这些字段的关联显示组件定义一个新的required属性,并设置值为“true"。 4.类的附加信息 属性系统还有一个宏Q_CLASSINFO(),可以为类的元对象定义”名称——值“信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值