Qt信号槽简单实现

信号槽简介

  • 在Qt中对象间通信,广泛使用了信号槽机制。信号槽机制本质上就是回调函数的应用,Qt中通过扩展C++语法来实现,对比回调函数使用更便利,定制性更好。
  • C++语言扩展是通过元对象编译器moc来实现,Qt 将源代码交给标准 C++ 编译器,如 gcc,mingw,msvc之前,需要事先将这些扩展的语法去除掉,完成这一操作的就是 moc。

元对象编译器及其作用

  • moc 全称是 Meta-Object Compiler,也就是“元对象编译器”。
  • Qt程序在交给标准编译器预编译之前要使用 moc 分析 C++ 源文件。
  • 如头文件中包含宏 Q_OBJECT,则生成一个包含Q_OBJECT 宏的实现代码的C++源文件。
  • 这个新的文件名字将会是原文件名前面加上 moc_ 构成。
  • 新生成的源文件将参与到标准编译器的编译中。
  • qt程序编译执行过程:moc预编译=》标准编译器预编译=》标准编译器编译=》链接执行

信号槽使用注意

  • 对象类必须直接或间接继承QObject。
  • 对象类的私有声明区必须声明Q_OBJECT宏。
  • signals下必须定义相应的信号函数。
  • slots下必须定义并实现相应的槽函数。
  • 使用connect进行正确关联。

connect函数第五个参数

  • Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型;如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
  • Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这种连接类型在多线程环境下比较危险,可能会造成崩溃。
  • Qt::QueuedConnection:槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这种连接类型。
  • Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在线程间同步的场合下可能需要这种连接类型。
  • Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置,且某个信号和槽已经连接时,再进行重复连接的话就会失败。也就是避免了重复连接,即发送同一个信号,槽函数多次执行的情况。

信号槽简易实现

  • 依次包含KObject,TestClassA,TestClassB,模拟qt中信号槽实现,详细代码如下:
#ifndef KOBJECT_H
#define KOBJECT_H

#include<map>
#define kslots
#define ksignals public
#define kemit

class KObject;

struct MetaObject
{
	static void active(KObject * sender, int idx);
};

struct Connection
{
	KObject * receiver;
	int sltID;
};

typedef std::map<int, Connection> ConnectionMap;

class KObject
{
	friend class MetaObject;
	static MetaObject meta;
	
public:
	KObject();
	virtual ~KObject();
	static void kconnect(KObject*, int, KObject*, int);
	
protected:
	virtual void metacall(int sltID) = 0;

private:
	ConnectionMap connections;
};

#endif
#include "KObject.h"

KObject::KObject(){}

KObject::~KObject(){}

void MetaObject::active(KObject* sender, int sigID)
{
	Connection c = sender->connections[sigID];
	c.receiver->metacall(c.sltID);
}

void KObject::kconnect(KObject* sender, int sigID, KObject* receiver, int sltID)
{
	Connection c = {receiver, sltID};
	sender->connections.insert(std::pair<int, Connection>(sigID, c));
}
#ifndef TESTCLASSA_H
#define TESTCLASSA_H

#include "KObject.h"
class TestClassA : public KObject
{
	
public:
	TestClassA();

protected:
	void metacall(int sltID);

ksignals:
	typedef enum {
		SIG_HELLOWORLD
	}SIG_ID;

void sigTestA(int sigID);

};

#endif
#include "TestClassA.h"

TestClassA::TestClassA()
{
}

void TestClassA::metacall(int sltID)
{
}

void TestClassA::sigTestA(int sigID)
{
	MetaObject::active(this, sigID);
}
#ifndef TESTCLASSB_H
#define TESTCLASSB_H

#include "KObject.h"

class TestClassB : public KObject
{
public:
	TestClassB();

void metacall(int sltID);

public kslots:
	typedef enum{
		SLT_HELLOWORLD = 2
	}SLT_ID;

void slotTestB();

};
#endif
#include "TestClassB.h"
#include <iostream>

TestClassB::TestClassB()
{
}

void TestClassB::metacall(int sltID)
{
	switch (sltID) 
	{
		case SLT_HELLOWORLD:
			slotTestB();
			break;
		default:
			break;
	};
}

void TestClassB::slotTestB()
{
	std::cout << "hello world TestB";
}
#include "TestClassA.h"
#include "TestClassB.h"

int main() 
{
	TestClassA a;
	TestClassB b;
	
	KObject::kconnect(&a, TestClassA::SIG_HELLOWORLD, &b, TestClassB::SLT_HELLOWORLD);
	
	a.sigTestA(TestClassA::SIG_HELLOWORLD);

	return 0;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星火撩猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值