详细分析Qt中moc文件

本文深入剖析了Qt中moc文件的生成原理及其在信号与槽机制中的作用。揭示了Qt如何通过moc将自定义关键字转换为标准C++代码,以及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文件的内容,不知道大家是否坚持看完了,相信看完后对你很有帮助。更多参考资料可以在华清远见的官网进行查看,华清远见提供免费的教程,供大家学习!

<think>嗯,用户遇到了Qtmoc文件不自动更新的问题。从提供的参考资料看,这个问题在VS2015+Qt5环境下尤其常见,错误表现是链接时出现QMetaObject相关的外部符号无法解析。 用户给出的四条引用信息很有价值: 引用1提到两种解决方法:手动设置自定义生成工具或降级Qt插件版本。其中降级插件到2.0.0以下的方法被特别推荐。 引用2解释了moc文件的作用——它是Qt元对象编译器自动生成的文件,用于支持信号槽等Qt特性。 引用3指出问题通常与Q_OBJECT宏缺失有关,因为moc处理依赖于这个宏。 引用4展示了典型的错误信息,这些符号正是moc应该生成的内容。 用户的核心诉求很明确:需要解决moc不自动生成的问题。结合引用1的两种方案,考虑优先推荐更稳定的插件降级方案,因为手动配置容易出错。但用户环境可能有版本限制,所以需要准备备选方案。 注意到用户开发环境是VS2015+Qt5,这个组合确实存在已知的插件兼容性问题。解决方案需要考虑: - Qt插件版本控制(关键点) - Q_OBJECT宏的检查(基础但易忽略) - 构建系统的清理(可能残留旧文件) - 项目配置检查(确保moc被执行) 准备把解决方案分层级呈现:从最简单的宏检查开始,逐步到插件调整。这样用户可以先尝试低成本的修复。对于插件降级操作,考虑强调版本号2.0.0这个关键数字,因为引用1明确指出这是分界点。 补充的"进阶建议"其实是隐藏重点: 1. 编译器选择匹配问题(MinGW/MSVC的ABI兼容性) 2. 影子构建可能导致的路径问题 3. CMake的现代解决方案(虽然用户没问但值得提) 相关问题设计原则: 1. 错误关联:LNK2019是典型症状 2. 原理延伸:帮助用户理解机制 3. 环境迁移:考虑用户可能升级VS 4. 排除流程:系统性排查方法 <think> 针对Qt项目中moc文件不自动更新的问题,结合引用资料提供以下解决方案: ### 核心解决方案 1. **检查Qt插件版本(推荐)** - 若使用**Visual Studio插件**,确认插件版本 ≤ 2.0.0(高版本存在兼容性问题) - 操作步骤: ```bash 控制面板 → 卸载程序 → 找到"Qt Visual Studio Tools" → 卸载后安装v2.0.0以下版本 ``` - *原因*:高版本插件在VS2015+Qt5环境中可能失效[^1] 2. **手动配置生成规则** - 步骤: 1. 右键需生成moc的`.h`文件 → 属性 2. **常规 → 项类型 → 改为“自定义生成工具”** 3. 配置自定义生成工具参数: ```makefile 命令行:moc.exe %(FullPath) -o moc_%(Filename).cpp 输出:moc_%(Filename).cpp 附加依赖项:%(FullPath) ``` - *适用场景*:无法降级插件时[^1] 3. **验证Q_OBJECT宏** - 确保所有使用信号/槽的类声明中包含 `Q_OBJECT` 宏: ```cpp class MyClass : public QObject { Q_OBJECT // 必须存在 public slots: ... signals: ... }; ``` - *原理*:缺失宏会导致moc忽略该文件[^3] --- ### 进阶排查步骤 1. **清理构建缓存** - 删除项目目录中的 `build`、`*.user` 文件及 `moc_*.cpp` 等中间文件 - 执行 **qmake → 重新构建**(避免缓存干扰) 2. **检查Qt工具链配置** - 确认Qt版本路径正确(VS菜单:`QtQt Options → Versions`) - 验证moc路径:`<Qt安装路径>\bin\moc.exe` 是否存在 3. **迁移到CMake(长期方案)** ```cmake cmake_minimum_required(VERSION 3.5) project(MyQtApp) set(CMAKE_AUTOMOC ON) # 关键!启用自动moc find_package(Qt5 COMPONENTS Core Widgets REQUIRED) add_executable(app main.cpp myclass.cpp myclass.h) target_link_libraries(app Qt5::Core Qt5::Widgets) ``` *优势*:避免IDE插件兼容性问题[^2] --- ### 典型错误修复(引用[4]) 当出现 `LNK2001` 未解析符号错误时: - 原因:moc未生成元对象代码(`moc_*.cpp`未编译) - 解决: 1. 检查是否完成上述配置 2. 确保 **头文件未被排除构建**(文件属性 → 从生成中排除 → 设为“否”) > **关键原理**:mocQt扩展语法(如信号槽)转换为标准C++代码,此过程必须由构建系统自动触发[^2][^3]。若自动化失效,需手动介入配置。 --- §§相关问题§§ 1. 如何验证Qtmoc是否正常工作? 2. CMake项目中如何为特定头文件禁用自动moc? 3. Qt6与VS2022配合时moc失效的解决方法? 4. 信号槽连接失败是否与moc生成有关?如何调试? [^1]: VS2015 Qt不自动生成moc文件的最终解决方法 [^2]: qt里面的moc文件作用及生成机制 [^3]: Qtmoc问题与Q_OBJECT宏的关联 [^4]: 未解析符号错误与moc的关联
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值