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)
可见这也是一个空宏。