目录
总体思路:
sig---信号
slot---插槽
信号.connect(&插槽对象,&插槽类::插槽类成员函数)
简介:
信号/插槽机制:A对象声明一个信号(sig),B对象实现对应参数的插槽(slot),将他们连接起来(connect),当A对象触发带上参数的信号时,B对象所连接的函数就会相应。有点像回调。
和回调的区别:
-
回调是个函数指针,缺点有两个,一是类型不安全,你不知道使用者调用回调的对象和参数是否正确;二是过于耦合,处理函数通常要和回调函数一起。
-
信号插槽机制优点:一是不用担心空指针,插槽对象析构时,会自动断开连接(disconnect);二是线程安全,带锁,不用担心多线程之间的冲突。当然缺点也有,没有返回值即只能是void函数,而且顺序不能调整插槽对象的响应先后。
使用场景:
两个独立的对象或者模块之间,想要直接沟通,但是又不想过多暴露接口,如果联系比较简单,这时可以考虑这个轻量级的消息框架---sigslot,整个库就一个头文件sigslot.h,集成非常灵活。
使用方法:
-
sig声明时指定参数列表,参数个数是0-8个
-
slot类必须继承has_slots<>,slot函数必须是void返回值,参数列表和sig声明一样。
-
常用函数:connect、disconnect、disconnect_all
实例:
抽象一个Sender类作为sig,抽象一个Receiver类作为slot,实例中有两种常用用法,第一种是模拟两个独立模块建立一个简单连接,第二种是模拟Receiver作为Sender类相关的嵌入模块。
0.辅助头文件
#ifndef __SIGSLOT_BASEDEFINE_H__
#define __SIGSLOT_BASEDEFINE_H__
#define MAX_NAME_LENGTH 32
typedef struct Info_ {
int num;
char name[MAX_NAME_LENGTH];
} Info;
typedef struct Message_ {
int msgid;
Info info;
} Message;
#endif // __SIGSLOT_BASEDEFINE_H__
1.sig----Sender类
// sender.h
#ifndef __SIGSLOT_SENDER_H_
#define __SIGSLOT_SENDER_H_
#include "sigslot.h"
#include "receiver.h"
class Sender {
private:
int id;
public:
Sender();
~Sender();
sigslot::signal1<Message *> SendMessage;
int broadcast(Info &info);
bool addReceiver(Receiver *);
};
#endif // __SIGSLOT_SENDER_H_
//sender.cpp
#include "sender.h"
#include <cstring>
Sender::Sender(/* args */): id(0) {
}
Sender::~Sender() {
}
int Sender::broadcast(Info &info) {
Message msg;
msg.msgid = id++;
msg.info.num = info.num;
strncpy(msg.info.name, info.name, MAX_NAME_LENGTH);
SendMessage(&msg);
return 0;
}
bool Sender::addReceiver(Receiver *new_receiver) {
SendMessage.connect(new_receiver, &Receiver::onMessage);
return true;
}
2.slot----Receiver类
// receiver.h
#ifndef _SIGSLOT_RECEIVER_H_
#define _SIGSLOT_RECEIVER_H_
#include "sigslot.h"
#include "basedefine.h"
#include <iostream>
class Receiver: public sigslot::has_slots<> {
private:
std::string m_name;
public:
Receiver();
Receiver(std::string name);
~Receiver();
void onMessage(Message *msg);
};
#endif // _SIGSLOT_RECEIVER_H_
//sender.cpp
#include "receiver.h"
Receiver::Receiver() {
}
Receiver::Receiver(std::string name): m_name(name) {
}
Receiver::~Receiver() {
}
void Receiver::onMessage(Message *msg) {
printf("[%s:%d] --> %s get message, num:%d name:%s \n",
__FUNCTION__, __LINE__, m_name.c_str(),msg->info.num, msg->info.name);
}
3.测试用例
//test_sigslot.cpp
#include "basedefine.h"
#include "receiver.h"
#include "sender.h"
#include <cstring>
int main(int argc, char const *argv[]) {
Receiver receiver1("receiver1");
Sender sender;
// 1.模拟两个独立的模块建立连接
sender.SendMessage.connect(&receiver1, &Receiver::onMessage);
// 2.模拟Receiver模块作为Sender模块相关的嵌入模块
// 通过开放接口建立连接,隐藏内部两个模块之间交互细节
Receiver receiver2("receiver2");
sender.addReceiver(&receiver2);
Receiver receiver3("receiver3");
sender.addReceiver(&receiver3);
Receiver *p_receiver4 = new Receiver("receiver4");
sender.addReceiver(p_receiver4);
//test sender send msg
Info info;
info.num = 4869;
strncpy(info.name, "miaopasi", MAX_NAME_LENGTH);
printf("[%s:%d] --> send msg after add r1,r2,r3 \n", __FUNCTION__, __LINE__);
sender.broadcast(info);
// 主动断开r2的连接
sender.SendMessage.disconnect(&receiver2);
// 销毁r4,sigslot内部会被动断开连接
if (p_receiver4) {
delete p_receiver4;
p_receiver4 = NULL;
}
printf("[%s:%d] --> send msg after disconnet r2 and destory r4 \n", __FUNCTION__, __LINE__);
sender.broadcast(info);
// 断开所有连接
sender.SendMessage.disconnect_all();
printf("[%s:%d] --> send msg after disconnet all \n", __FUNCTION__, __LINE__);
sender.broadcast(info);
return 0;
}
问题:
1)GCC编译时遇到继承has_slots<>时,提示typename问题。
用到的const_iterator是个模板,需要前提声明typename,自己改起来会很麻烦。建议参考https://www.cnblogs.com/choday/p/3429975.html的头文件。
参考:https://blog.csdn.net/business122/article/details/80988373