编译器,元对象系统,反射,信号槽
详解在c++中提到编译器,大家直观上就认为编译器就是把代码转化为二进制的工具。
这很容易让人产生误解,其实moc编译器的功能是把某些特殊的宏转化为c++代码。
Qt 的 moc 会完成以下工作:
- 为
Q_OBJECT
宏展开后所声明的成员函数的成生实现moc_xxx.cpp
代码; - 识别 Qt 中特殊的关键字,比如识别出
Q_PROPERTY
、Q_INVOKABLE
、slot
、signals
宏等。
这些关键字用于辅助moc识别信号函数和槽函数,而且必须放在头文件中:
// Qt4中为 protected. Qt5为支持connect函数指针写法, 定义为 public
#define signals public
// slots被替换成空
#define slots
// emit被替换成空, 其作用仅仅是给qt的用户进行提示
#define emit
用于对信号和槽进行编号+命名
#define SLOT(a) "1"#a #define SIGNAL(a) "2"#a
Q_OBJECT
用于为自定义类添加一些函数声明,MOC
编译器会为每个带有Q_OBJECT
的头文件xxx.h
自动生成moc_xxx.cpp
,并在文件中实现Q_OBJECT
中声明的函数。
从
QObject
派生的含有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, "")
staticMetaObject
类的静态成员,存储当前类的元信息;const QMetaObject *metaObject() const
返回当前类的QMetaObject,即staticMetaObject
成员;void *qt_metacast(const char *)
被QObject::inherits
直接调用,用于判断是否是继承自某个类。
判断时,需要传入父类的字符串名称;
int qt_metacall(QMetaObject::Call, int, void **)
调用函数回调,内部调用了qt_static_metacall
函数;static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **)
根据函数索引进行调用槽函数。
信号槽注意到有三个函数是虚函数,它们是
QObject
的虚函数成员,当我们在自定义对象中添加Q_OBJECT
宏的时候,相当于重载了这三个虚函数。
信号槽本质就是回调函数+发布订阅模式。
原理Qt为提供了5种类型的回调方式,如下:
Qt::AutoConnection
自动连接,根据sender和receiver是否在一个线程里来决定使用哪种连接方式,同一个线程使用直连,否则使用队列连接
Qt::DirectConnection
直连
Qt::QueuedConnection
队列连接
Qt::BlockingQueuedConnection
阻塞队列连接,顾名思义,虽然是跨线程的,但是还是希望槽执行完之后,才能执行信号的下一步代码
Qt::UniqueConnection
唯一连接
从最简单的直连,了解信号槽的实现原理。
QObject
中有一个用于存储信号和槽的数据结构:
//每一个信号与一个 QObjectPrivate::ConnectionList链表关联
class QObjectConnectionListVector : public QVector<QObjectPrivate::ConnectionList>
connect
connect构造了一个QObjectPrivate::Connection
对象,然后存储在了发送者对象的ConnectionList
中
signal
->QMetaObject::activate
->qt_static_metacall
->slot
QMetaObject::activate
函数内部实根据ConnectionList
查找得到信号对应的qt_static_metacall
,然后一一调用槽函数。
使用 Qt 反射机制的条件反射:是指在运行时,能获取任意一个类对象的所有类型信息、属性、成员函数等信息的一种机制。
元对象:是指用于描述另一个对象结构的对象。
1、需要继承自 QObject
类,并需要在类之中加入 Q_OBJECT
宏。
2、注册成员函数:若希望普通成员函数能够被反射,需要在函数声明之前加入
Q_INVOKABLE
宏。
3、注册成员变量:若希望成员变量能被反射,需要使用 Q_PROPERTY
宏。
moc编译器会根据Q_OBJECT
关键字,把类名存入staticMetaObject
静态成员中;根据 Q_INVOKABLE
把方法名存入staticMetaObject
静态成员中;根据 Q_PROPERTY
把属性名存入staticMetaObject
静态成员中。
注意到staticMetaObject
,它就是所谓的元对象,是静态成员,因此同一个类的所有对象共享一个元对象。
qobject_cast<>
和inherits()
QObject::setProperty()
QT的元对象系统,并不能直接通new "className"
的方式创建对象,需要我们自己实现。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)