解析Qt元对象系统(二) Q_OBJECT

Q_OBJECT宏的定义在qobjectdefs.h:

#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, "")

这个宏居然用这么多行替代,下面逐一分析。

QT_WARNING_PUSH宏的定义:

#  define QT_WARNING_PUSH    QT_DO_PRAGMA(GCC diagnostic push)
/* Warning/diagnostic handling */
#define QT_DO_PRAGMA(text)        _Pragma(#text)

这个宏的作用就是告诉编译器GCC diagnostic push,实际是用了_Pragma这个C++ 11语法。

Q_OBJECT_NO_OVERRIDE_WARNING是个空宏,增强代码可读性。

static const QMetaObject staticMetaObject; \

这句定义了关键的静态元对象 staticMetaObject,这个对象会保存该类的元对象系统信息。使用静态元对象,说明该类的所有实例都会共享这个静态元对象,而不需要重复占用内存。

virtual const QMetaObject *metaObject() const; \

这个虚函数是获取当前类对象里面内部元对象的公开接口,通常情况下都会返回类的静态元对象 staticMetaObject,如果当前类的对象内部使用了动态元对象(仅 QML 程序才有),才会出现返回非静态元对象。

virtual void *qt_metacast(const char *); \

qt_metacast 是程序运行时的对象指针转换,它可以将派生类对象的指针安全地转为基类对象指针,这是 Qt 不依赖编译器特性,自己实现的运行时类型转换。qt_metacast 参数是基类名称字符串,返回值是转换后的基类对象指针,如果转换不成功,返回 NULL。

virtual int qt_metacall(QMetaObject::Call, int, void **); \

qt_metacall 是非常重要的虚函数,在信号到槽的执行过程中,qt_metacall 就是负责槽函数的调用,属性系统的读写等也是靠 qt_metacall 实现。

QT_TR_FUNCTIONS宏用于国际化功能,实际是两个内联函数tr和trUtf8

Q_OBJECT_NO_ATTRIBUTES_WARNING是个空宏,还是用于增强代码可读性。

Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \

这一个私有的静态函数,Q_DECL_HIDDEN_STATIC_METACALL 是个空宏,没啥用,就是提醒程序员这是一个隐蔽的私有静态函数。前面的qt_metacall调用该私有静态函数实现槽函数调用,真正调用槽函数的就是 qt_static_metacall

QT_WARNING_POP宏同上面的QT_WARNING_PUSH类似,不再解释。

struct QPrivateSignal {};
QPrivateSignal 是一个私有的空结构体,对函数功能来说没啥用,就是在信号被触发时,挂在参数里提醒程序员这是一个私有信号的触发。

最后是QT_ANNOTATE_CLASS(qt_qobject, “”),这个宏的定义是这样的:

#  define QT_ANNOTATE_CLASS(type, ...)

实际上还是空宏。

下面来看关键字 signals 和 slots,源码中的定义是这样的:

#   ifndef QT_NO_SIGNALS_SLOTS_KEYWORDS
#     define slots Q_SLOTS
#     define signals Q_SIGNALS
#   endif

# define    Q_SLOTS         QT_ANNOTATE_ACCESS_SPECIFIER(qt_slot)
# define    Q_SIGNALS       public QT_ANNOTATE_ACCESS_SPECIFIER(qt_signal)
# define QT_ANNOTATE_ACCESS_SPECIFIER(x)    //又是空宏

之所以又定义了两个中间的宏,是因为某些编译器或相关库有可能占用了关键字 slots 和 signals。也就是说,slots相当于不存在,signals相当于public
既然signals相当于public,那么我们在头文件声明信号就是声明了一个公有函数,但是我们从来不去实现它,因为是moc工具自动实现的。如果我们自己强行实现,程序编译时会报重定义的错误,因为重名的函数参数还一样。

信号的触发使用的是 emit 关键字,其定义如下:

# define Q_EMIT
#ifndef QT_NO_EMIT
# define emit
#endif

emit又是一个空宏。

再看宏Q_INVOKABLE,它可以用来修饰signal和slot,称之为元方法,保存到类的静态数据里面。这些元方法都可以通过QMetaObject::invokeMethod()来调用,这个函数可以用于跨线程调用函数。
Q_INVOKABLE不能修饰普通函数,因为它属于元对象系统的部分,普通函数是不会被moc处理的,在moc_.cpp中看不到普通成员函数的信息。

#define Q_INVOKABLE  QT_ANNOTATE_FUNCTION(qt_invokable)
# define QT_ANNOTATE_FUNCTION(x)

可见这也是一个空宏。

你可能感兴趣的:(Qt)