Qt的元对象系统

一、元对象系统介绍

Qt中的元对象系统(Meta-Object System)提供了内部对象间通讯的信号和槽机制、运行时类型信息和动态属性系统。

整个元对象系统基于三个东西建立:

  1. QObject类为所有对象提供了一个基类,只要继承此类,那创建出的对象便可以使用元对象系统。
  2. 在声明类时,将Q_OBJECT宏放置于类的私有区域就可以在类中使能元对象特性,诸如动态属性,信号,以及槽。一般实际使用中,我们总是把Q_OBJECT宏放置在类声明时的开头位置,除此之外我们的类还需要继承QObject类。
  3. 元对象编译器(Meta-Object Compiler,缩写moc),为每个QObject的子类提供必要的代码去实现元对象特性。我们可以认为Qt对C++进行了一些拓展,moc则是负责将这些拓展语法翻译成原生的C++语法,之后交给C++编译器去编译。
     

二、moc

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

三、其他特性

元对象系统主要是为了实现信号和槽机制才被引人的,不过除了信号和槽机制以外,元对象系统还提供了其他一些特性,属性系统,动态运行时信息:

QObject : : metaObject()函数可以返回一个类的元对象;
QMetaObject : :className()可以在运行时以字符串形式返回类名,而不需要C++编辑器原生的运行时类型信息(RTTI)的支持;
QObject : : inherits()函数返回一个对象是否是QObject继承树上一个类的实例的信息;
QObject : : tr()和 QObject : : trUtf8()进行字符串翻译来实现国际化;
QObject : : setProperty()和 QObject : : property()通过名字来动态设置或者获取对象属性;
QMetaObject : : newInstance()构造类的一个新实例。

除了这些特性,还可以使用qobject_cast(QObject *object)函数对QObject类进行动态类型转换,这个函数的功能类似于标准C++中的dynamic_cast()函数,但它不再需要RTTI的支持。这个函数尝试将它的参数转换为尖括号中的类型的指针,如果是正确的类型,则返回一个非零的指针:如果类型不兼容,则返回0。例如:


  QObject *obj = new QTimer;          // QTimer inherits QObject

  QTimer *timer = qobject_cast(obj);
  // timer == (QObject *)obj

  QAbstractButton *button = qobject_cast(obj);
  // button == 0>(obj):

四、 Q_OBJECT 宏的解析

/* qmake ignore Q_OBJECT */
#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, "")

可见,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 类,为此需要包含此头文件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 命令, 再构建程序。

错误的运行

Qt的元对象系统_第1张图片

 正确的运行

Qt的元对象系统_第2张图片

 参考:

QT核心机制1:元系统_lczdk的博客-CSDN博客

Qt笔记 元对象系统 - 橘崽崽啊 - 博客园

你可能感兴趣的:(Qt,qt,开发语言)