本文档中出现的代码不一定是Qt的原生代码,可能是根据原来思维写的类似伪代码,如会将Qt原生的slots、signals关键字换成db_signals和db_slots,以表示这是伪代码,而不是Qt原生的代码,只用于表述信号槽的原理。
首先对象内部会议2个char*型的字符串,专门记录类中定义的信号与槽,如:
class Object
{
public:
Object();
virtual ~Object();
db_signals:
void sig1();
void sig2();
public db_slots:
void slot1();
void slot2();
};
然后有2个字符串为:
static const char sig_names[] = "sig1/sig2";
static const char slts_names[] = "slot1/slot2";
元对象系统中可能有一个类里面定义这2个字符串,如:
struct MetaObject
{
const char * sig_names;
const char * slts_names;
};
信号与槽是通过connect函数连接的,connect内部如下所示:
void Object::db_connect(Object* sender, const char* sig, Object* receiver, const char* slt)
{
int sig_idx = find_string(sender->meta.sig_names, sig);
int slt_idx = find_string(receiver->meta.slts_names, slt);
if (sig_idx == -1 || slt_idx == -1) {
perror("signal or slot not found!");
} else {
Connection c = {receiver, slt_idx};
sender->connections.insert(std::pair<int, Connection>(sig_idx, c));
}
}
可以看到connect内部是使用find_string,即字符串匹配,而一旦匹配到了,就会将信号索引sig_idx和对应的Connection对象合成一对,并且发送方有个专门的pair对象(connections)存储这些数据,而Connection对象存储着槽函数的对象receiver和槽函数的索引methodIndex
Connection对象定义如下所示:
struct Connection
{
Object * receiver;
int methodIndex;
};
接下来便是元对象编译器生成moc_xxx.cpp文件,给各个信号加上函数体,以下是我随便找的moc_mainview.cpp截取的部分代码,一个是信号testSignal的源码,另一个是qt_static_metacall函数的部分源码。
信号testSignal源码
qt_static_metacall函数的部分源码
可以看到根据信号生成的函数体就是调用了QMetaObject::activate这一个函数而已,而这个函数的第3个参数是信号索引sig_idx,是专门用于识别是哪个信号的,图片中是0,代表是第0个信号。QMetaObject::activate函数的源码我没有看到,但是很容易猜测出来里面发生了什么:
估计QMetaObject::activate函数里面会遍历前面connect函数中记录信号与槽连接情况的pair对象(connections),找到对应对象连接的Connection的对象,然后使得Connection对象中的receiver调用其qt_static_metacall函数,而qt_static_metacall函数根据上图所示主要结构就是一个switch-case语句,根据信号或槽的索引值调用对应的信号或槽函数。
上诉原理都只讲述的是connect函数第5个参数是Qt::DirectConnection情况下的情况,若是其他情况,如Qt::QueuedConnection,肯定会在qt_static_metacall函数中做不同处理,但都大同小异,很容易猜测到发生了什么。
最后给出Qt信号与槽的使用条件以及需要这些条件的原因。
- 类继承自QObject或其子类。因为connect函数、承载信号与槽的字符串,记录信号与槽连接情况的pair对象等都是在QObject中定义的
- 类定义时声明有Q_OBJECT宏。前面提到,信号与槽的实现依托于moc_xxx.cpp文件,这个文件是Qt元对象编译器生成的,但是Qt元对象编译器总得知道哪些类或文件需要生成对应的moc文件吧,那么Q_OBJECT宏便是这个标志,声明了Q_OBJECT宏便是告诉Qt元对象编译器这个类需要生成对应的moc文件