Loading

Qt笔记 元对象系统

元对象系统

一、元对象系统基本概念

1、Qt 的元对象系统提供的功能有:对象间通信的信号和槽机制、运行时类型信息和动态属性系统等。

2、元对象系统是 Qt 对原有的 C++进行的一些扩展,主要是为实现信号和槽机制而引入的, 信号和槽机制是 Qt 的核心特征。

3、要使用元对象系统的功能,需要满足以下三个条件

①、该类必须继承自 QObject 类。

②、必须在类声明的私有区域添加 Q_OBJECT 宏,该宏用于启动元对象特性,然后便可使用动态特性、信号和槽等功能了。

③、元对象编译器(moc)为每个 QObject 的子类,提供实现了元对象特性所必须的代码。

4、元对象系统具体运行原则

①、因为元对象系统是对 C++的扩展,因此使用传统的编译器是不能直接编译启用了元对象系统的 Qt 程序的,对此在编译 Qt 程序之前,需要把扩展的语法去掉,该功能就是moc 要做的事。

②、moc 全称是 Meta-Object Compiler(元对象编译器),它是一个工具(类似于 qmake), 该工具读取并分析 C++源文件,若发现一个或多个包含了 Q_OBJECT 宏的类的声明, 则会生成另外一个包含了 Q_OBJECT 宏实现代码的 C++源文件(该源文件通常名称为moc_*.cpp) ,这个新的源文件要么被#include 包含到类的源文件中,要么被编译键接到类的实现中(通常是使用的此种方法)。注意:新文件不会“替换”掉旧的文件, 而是与原文件一起编译。

5、其他概念

①、元对象代码:指的是 moc 工具生成的源文件的代码,其中包含有 Q_OBJECT 宏的实现代码

②、moc 工具的路径为:F:\app\Qt5.8.0MinGw\5.8\mingw53_32\bin

二、Q_OBJECT 宏

列出该宏代码的目的主要是为了讲解使用 Qt Creator 时易犯的错误。

#define Q_OBJECT \ 
public: \ 
    …… 
    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: \ 
    static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); 
    ……

1、选中 Q_OBJECT 然后按下 F2 可追踪到该宏的如上定义(仅列出关键代码)

2、可见,Q_OBJECT 宏为声明的类之中增加了一些成员,而且❷、❸、❹是虚函数成员(注意,这些虚函数没有定义),按 C++语法,虚函数必须定义或者被声明为纯虚函数,moc 工具的工作之一就是生成以上成员的定义,并且还会生成一些其他必要的代码。此处讲解 Q_OBJECT 宏代码的目的主要是为了讲解使用 Qt Creator 时易犯的错误及怎样正确使用 Q_OBJECT 宏,读者可查看由 moc 生成的 moc_*.cpp 的内容以了解更详细的信息。

三、使用Qt Creator启动元对象系统

1、此时 moc 工具是通过 Qt Creator 来使用的,因此必须保证 moc 能发现并处理项目中包含有 Q_OBJECT 宏的类,为此,需要遵守以下规则

  • 从QObject 派生的含有 Q_OBJECT 宏的类的定义必须在头文件中。
  • 确保 pro 文件中,是否列举了项目中的所有源文件(SOURCES 变量)和头文件(HEADERS 变量)
  • 应在头文件中使用逻辑指令(比如#ifndef)防止头文件被包含多次。
  • QObject 类应是基类列表中的第一个类。
  • 由以上规则可见,使用 Qt Creator 编写代码时,类应定义在头文件中,成员函数的定义应位于源文件中(这样可避免头文件被包含多次产生的重定义错误),虽然这样编写程序比较麻烦,但这是一种良好的代码组织方式。

2、不按规则 1 的方法编写程序,则 moc 工具就不能正确生成代码,这时的错误原因通常是未定义由 Q_OBJECT 展开后在类中声明的虚函数引起的,其错误信息如下:

  • 若使用 MinGw 编译,产生的错误信息类似如下:undefined reference to 'vtable for A'.表示类 A 的虚函数表(vtable)不能正常生成,通常是有虚函数未定义。
  • 若使用 VC++2015 编译,产生的错误信息类似如下:LNK2001: 无法解析的外部符号 "public: virtual struct QMetaObject const * thiscall A::metaObject(void)const " (……).表示虚函数 A::metaObject 未定义。

3、以下错误不易被发现:若定义了 QObject 类的派生类,并进行了构建,在这之后再添加 Q_OBJECT 宏,则此时必须执行一次 qmake 命令(“构建”>“执行 qmake”),否则 moc 不能生成代码。

示例:使用 Qt Creator 启动元对象系统

新建空项目、添加一个 m.h 和 m.cpp 文件。

/*m.h 文件内容。要在 Qt Creator 中启动元对象系统,包含 Q_OBJECT 宏的类的定义必须位于头文件中,否则 moc 工具不能生成元对象代码*/
#ifndef M_H        //用于防止头文件被多次包含的逻辑指令#define M_H
#include <QObject> //因为要使用 QObject 类,为此需要包含此头文件class B{};
//错误,moc 不能启动。因为多重继承时 QObject 必须位于基类列表中的第一位。
//class A:public B,public QObject{ Q_OBJECT };
class B
{
};
class A : public QObject, public B
{           /*要使用元对象系统必须继承自 QObject 类,且 QObject 应位于基类继承列表中的第一位。*/
   Q_OBJECT //启动元对象系统。Q_OBJECT 必须位于私有区域。
       public : A()
   {
      qDebug("DDDD\n");
   }
};     //qDebug 函数用于在控制台输出一些信息
#endif // M_H


//m.cpp 文件内容。
#include "m.h"
int main(int argc, char *argv[])
{
    A ma;
    return 0;
}

运行以上程序,可在 debug 目录下找到一个 moc_m.cpp 的源文件,该源文件就是使用 moc 工具生成的,该源文件中的代码就是元对象代码,读者可查看其代码。若在该目录没有 moc_m.cpp 文件,说明 moc 工具未能正常启动,这时需在 Qt Creator 中执行 qmake 命令, 再构建程序。

四、在命令行启动元对象系统

1、在命令行需使用 moc 工具,并在源文件中包含 moc 工具生成的 cpp 文件。

2、此时包含 Q_OBJECT 的类不需要位于头文件中,假设位于 m.cpp 文件内,内容为:

#include <QObject>
class A : public QObject
{
    Q_OBJECT
public:
    A() {}
};
int main(int argc, char *argv[])
{
    A ma;
    return 0;
}

3、打开 Qt 5.8 for Desktop (MinGW 5.3.0 32 bit)命令行工具,输入如下命令moc d:\qt\m.cpp -o d:\qt\mm.cpp。以上命令会根据 m.cpp 生成 mm.cpp 文件,mm.cpp 文件中的代码就是元对象代码,此处 m.cpp 和mm.cpp 都位于 d:\qt 文件夹下。

4、然后再次打开 m.cpp,在其中使用#include 把 mm.cpp 包含进去,如下

#include <QObject>
// #include "mm.cpp"	//不能把 mm.cpp 包含在类 A 的定义之前,原因见下面的注释
class A : public QObject
{
    Q_OBJECT
public:
    A() {}
};
#include "mm.cpp" /*必须把 mm.cpp 包含在类A 的定义之后,因为 mm.cpp 源文件中有对类A 的成员的定义,此时必须见到类A 的完整定义。*/
int main(int argc, char *argv[])
{
    A ma;
    return 0;
}

5、然后再使用 qmake 生成项目文件和 makefile 文件,再使用 mingw32-make 命令即可。

posted @ 2021-09-17 09:23  橘崽崽啊  阅读(580)  评论(0编辑  收藏  举报