Qt -信号槽实现原理

一 简介

QT信号槽的实现实质是什么?
“回调函数”

简要说一下信号与槽的底层原理。
信号与槽的实现是借助了Qt 的元对象系统,元对象系统有一个元对象编译器,程序编译之前会有一个预处理过程,预处理将一个类/对象中的信号,槽的字符串值分别保存在一个容器中,可能是字符串或者其他的有序容器。

二 例子

简单实现

#include 
# define slot
# define siginal protected
# define emit

class Object;

struct MetaObject//元对象 每个Object有一个,保存自己拥有的信号和槽函数
{
	const char* sig_names;
	const char* slts_names;
	static void active(Object* sender, int idx);
};

struct Connection//保存接收方及其序号
{
	Object* receiver;
	int method;
};

typedef std::multimap<int, Connection> ConnectionMap;
//<信号,接收方>每个Object有一个,保存自己的信号发出时需要通知的接收方。每个信号的接收方可能不止一个,用multimap
typedef std::multimap<int, Connection>::iterator ConnectionMapIt;

static int find_string(const char* str, const char* substr)
{
	if (strlen(str) < strlen(substr))
		return -1;
	int idx = 0;
	int len = strlen(substr);
	bool start = true;
	const char* pos = str;
	while (*pos) 
	{
		if (start && !strncmp(pos, substr, len) && pos[len] == '\n')
			return idx;
		start = false;
		if (*pos == '\n') 
		{
			idx++;
			start = true;
		}
		pos++;
	}
	return -1;
}

class Object
{
	friend class MetaObject;
public:
	Object() {};
	virtual ~Object() {};
	static void connect(Object*sender, const char*sig, Object*receiver, const char*slt);
	void testSignal() { emit sig1(); }//测试函数

siginal:
	void sig1() //信号
	{ 
		cout << "信号发出" << endl;
		MetaObject::active(this, 0);
	}

public slot:
	void slot1() { cout << "槽函数执行" << endl; }//槽函数

private:
	static MetaObject meta;//保存自己拥有的信号和槽函数
	ConnectionMap connections;//multimap
	void metacall(int idx);//根据key去查询对应的槽函数
};

static const char sig_names[] = "sig1\n";//保存对象中所有信号的字符串的容器
static const char slts_names[] = "slot1\n";//保存对象中所有槽函数字符串的容器
MetaObject Object::meta = { sig_names, slts_names };//初始化元对象

void Object::metacall(int idx)//根据key去查询对应的槽函数
{
	switch (idx) 
	{
	case 0:
		slot1();
		break;
	default:
		break;
	};
}

void Object::connect(Object*sender, const char*sig, Object*receiver, const char*slt)//静态成员函数  connect建立信号与槽的连接
{
	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 };//保存接收方及其槽函数id
		sender->connections.insert(std::pair<int, Connection>(sig_idx, c));//发送方保存接收方信息
	}
}

void MetaObject::active(Object* sender, int idx)//信号发出,根据发出者找出它的接收方
{
	ConnectionMapIt it;
	std::pair<ConnectionMapIt, ConnectionMapIt> ret;
	ret = sender->connections.equal_range(idx);//equal_range主要是找在multimap中的key相等的value,也是一个迭代器
	for (it = ret.first; it != ret.second; ++it) 
	{
		Connection c = (*it).second;//获取之前保存的接受方消息
		c.receiver->metacall(c.method);//接收方执行之前绑定的槽函数
	}
}

int main()
{
	Object obj1, obj2;
	Object::connect(&obj1, "sig1", &obj2, "slot1");//连接信号
	obj1.testSignal();

	system("pause");
	return 0;
}

结果
Qt -信号槽实现原理_第1张图片

参考文章

QT信号槽

你可能感兴趣的:(Qt)