纯cpp如何模拟qt的信号与槽

纯cpp如何模拟qt的信号与槽

  • 我之前是如何使用bind的?
  • 一.demo示例
  • 二.简单来讲,c++自带的bind与function函数,如何实现类似信号与槽的机制
    • 1. 简单语法
    • 2. function与bind联动
      • 尝试1
      • 尝试2
      • 真正实现
      • 流程图
  • 自我反思

我之前是如何使用bind的?

在这里插入图片描述

一.demo示例

using MsgHander = std::function<void(const TcpConnectionPtr &conn, json &js, Timestamp)>;
unordered_map<int, MsgHander> _msgHandlerMap; // 消息id对应的处理操作/*****************************************/
// 注册消息回调_server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));
/*****************************************/// 注册消息以及对应的Handler回调函数
ChatService::ChatService()
{_msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::login, this, _1, _2, _3)});_msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::reg, this, _1, _2, _3)});_msgHandlerMap.insert({ONE_CHAT_MSG, std::bind(&ChatService::oneChat, this, _1, _2, _3)});_msgHandlerMap.insert({ADD_FRIEND_MSG, std::bind(&ChatService::addFirend, this, _1, _2, _3)});// 群组业务管理相关事件处理回调注册_msgHandlerMap.insert({CREATE_GROUP_MSG, std::bind(&ChatService::createGroup, this, _1, _2, _3)});_msgHandlerMap.insert({ADD_GROUP_MSG, std::bind(&ChatService::addGroup, this, _1, _2, _3)});_msgHandlerMap.insert({GROUP_CHAT_MSG, std::bind(&ChatService::groupChat, this, _1, _2, _3)});
}/*****************************************/// 上报读写时间相关信息的回调函数
void ChatServer::onMessage(const TcpConnectionPtr &conn, Buffer *buffer, Timestamp time)
{string buf = buffer->retrieveAllAsString();// 数据的反序列化json js = json::parse(buf);// 达到的目的:完全解耦网络模块的代码和业务模块的代码// 通过js["msgid"]获取->业务的hander->conn js timeauto msgHandler = ChatService::instance()->getHandler(js["msgid"].get<int>());// 回调消息绑定好的事件处理器,并执行相应的业务处理msgHandler(conn, js, time);
}/*****************************************///  获取消息对应的处理器
MsgHander ChatService::getHandler(int msgid)
{// 记录错误日志,msgid没有对应的事件处理回调auto it = _msgHandlerMap.find(msgid);if (it == _msgHandlerMap.end()){// 返回一个默认的处理器,是一个空操作return [=](const TcpConnectionPtr &conn, json &js, Timestamp){LOG_ERROR<< "Can not find handler:[" << msgid << "]!";};}else{return _msgHandlerMap[msgid];}
}

二.简单来讲,c++自带的bind与function函数,如何实现类似信号与槽的机制

1. 简单语法


#include <functional>
#include <iostream>void print(int arg)
{std::cout<<arg<<std::endl;
}void add(int a, int b)
{std::cout<< a+b <<std::endl;
}int cut(int a , int b)
{return a - b; 
}class A{
public:int number;A(){}A(int num){number = num;}Add(int a,int b);
private:};
A::Add(int a , int b)
{return a + b ;
}int main()
{/******************** 用法一 :* 相当于给一个(返回值是void,参数是int)的函数起一个(别名)* **********************/std::function<void(int)> myPrint = print;myPrint(100);/*****************************************/std::function<void(int,int)> myAdd = add;myAdd(1,2);/****************************************/std::function<int(int,int)> myCut = cut;int ret = myCut(1,2);std::cout<<ret<<std::endl;/****************************************//********* 用法二:* *************/A a1;std::function<int(A&,int,int)> func_ref_add = &A::Add;ret = func_ref_add(a1,3,4);std::cout<<ret<<std::endl;/****************************************************/const A a2(999);std::function<int (A const&)>class_number_call = &A::number;ret = class_number_call(a2);std::cout<<ret<<std::endl;return 0;
}

2. function与bind联动

#include<iostream>
#include<functional>using namespace std;
using namespace std::placeholders;int add(int a,int b)
{return a + b;
}class A{
public:int number;A(){}A(int num){number = num;}Add(int a,int b);
private:};
A::Add(int a , int b)
{return a + b ;
}int main()
{int ret = add(1,1);cout<<ret<<endl;/********* std::placeholders::_1相当于一个占位符,* 如果调用func_add_1只用调用一个参数了,另一个参数是5* ******/function<int(int)>func_add_1 = bind(add,std::placeholders::_1,5);ret = func_add_1(3);cout<<ret<<endl;/********** 还可以直接使用auto* ***************/auto func_add_2 = bind(add,std::placeholders::_1,5);ret = func_add_2(4);cout<<ret<<endl;A classA;//A类的方法,A类的对象,该函数的一些参数设置...auto member_func_bind = std::bind(&A::Add,&classA,std::placeholders::_1,66);ret  = member_func_bind(34);cout<<ret<<endl;return 0;
}

尝试1

#include <iostream>
#include <functional>
#include <map>/*** @brief Signal 类充当信号的角色* */
class Signal {
public:using Slot = std::function<void()>;/**   连接*   signalName   信号            const std::string&*   slot         槽              const std::function<void()>&* 注意:signalName和slot根据根据需求设计出任何函数类型*/void connect(const std::string& signalName, const Slot& slot) {slots_[signalName] = slot;}void emit(const std::string& signalName) {auto it = slots_.find(signalName);if (it != slots_.end()) {it->second();//找到对应的函数,并调用} else {std::cerr << "Signal not connected: " << signalName << std::endl;}}private:std::map<std::string, Slot> slots_;
};/*** @brief Object 类包含了槽函数。* */
class Object {
public:Signal signal;void slot1() {std::cout << "Slot 1 called" << std::endl;}void slot2(int value) {std::cout << "Slot 2 called with value: " << value << std::endl;}
};int main() {Object obj;// Connect slots to signalsobj.signal.connect("signal1", std::bind(&Object::slot1, &obj));obj.signal.connect("signal2", std::bind(&Object::slot2, &obj, 42));// Emit signalsobj.signal.emit("signal1");obj.signal.emit("signal2");return 0;
}
/*
*   Slot 1 called
*   Slot 2 called with value: 42
*/

尝试2

#include <iostream>
#include <string>
#include <map>class Signal {
public:std::string data;std::map<std::string, std::string> parameters;
};class Chatbot {
public:void getResponse(const Signal& signal) {// Access data and parameters from the signalstd::string data = signal.data;std::map<std::string, std::string> parameters = signal.parameters;// Process the signal and provide a response// ...std::cout << "Received signal with data: " << std::endl << data << std::endl;std::cout << "Parameters: " << std::endl;for (const auto& pair : parameters) {std::cout << pair.first << ": " << pair.second << std::endl;}}
};int main() {// Create a signal with data and parametersSignal signal;signal.data = "Hello";signal.parameters["param1"] = "10";signal.parameters["param2"] = "value";// Pass the signal to the chatbotChatbot chatbot;chatbot.getResponse(signal);return 0;
}
/*
Received signal with data: 
Hello
Parameters:
param1: 10
param2: value
*/

真正实现

/**********************上面两个方法只是尝试,现在是真正的实现,如下:************************/
/*** 逻辑:* bind绑定 (string 标识符,信号)* emit 标识符 -> 槽函数(信号)*/
#include <iostream>
#include <functional>
#include <map>/*** @brief Signal 类充当信号的角色* */
class Signal {
public:using SIGNAL = std::function<void()>;/**   连接*   signalName   信号标识符            const std::string&*   slot         信号函数              const std::function<void()>&* 注意:signalName和slot根据根据需求设计出任何函数类型*/void connect(const std::string& signalName, const SIGNAL& signal) {signals_[signalName] = signal;}void emit(const std::string& signalName) {auto it = signals_.find(signalName);if (it != signals_.end()) {it->second();//通过对应信号标识符 调用 信号函数} else {std::cerr << "Signal not connected: " << signalName << std::endl;}}private:std::map<std::string, SIGNAL> signals_;
};/*** @brief Object 类包含了槽函数。* */
class Object {
public:Signal signal;void slot1() {//槽函数1std::cout << "Slot 1 called" << std::endl;}void slot2(int value) {//槽函数1std::cout << "Slot 2 called with value: " << value << std::endl;}void signal1() {//信号函数1this->slot1();}void signal2(int value) {//信号函数2this->slot2(value);}
};int main() {Object obj;// Connect slots to signalsobj.signal.connect("signal1", std::bind(&Object::signal1, &obj));//bind就是对象方法+对象实例obj.signal.connect("signal2", std::bind(&Object::signal2, &obj, 42));// Emit signalsobj.signal.emit("signal1");obj.signal.emit("signal2");return 0;
}
/*
Slot 1 called
Slot 2 called with value: 42
*/

流程图

在这里插入图片描述

自我反思

模拟qt的信号与槽就是在信号的部分多进行一步封装,可以分为三层:信号标识符,信号函数与槽函数,信号标识符可以是int,也可以是string。通过信号标识符与信号函数进行连接,然后通过信号标识符找到信号函数,再使用信号函数调用槽函数。
为什么不直接用信号标识符连接槽函数?因为信号标识符无法携带任何参数,而信号函数可以,我们然后通过信号函数的参数再去调用槽函数,这样就对应了qt的机制emit函数,其实就是:在已经注册了的“信号与槽”中寻找对应的信号标识符,然后再通过map映射找到信号函数,然后调用信号函数,信号函数再去调用槽函数,这样就形成了一个闭环

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/180545.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构-交换排序(冒泡、快速)

冒泡排序 基本思想 先将第一个记录与第二个记录比较&#xff0c;将较大的记录放到第二个位置上&#xff0c;之后再将第二个记录与第三 个记录比较&#xff0c;将较大的记录放到第三个位置上&#xff0c;如此类推&#xff0c;知道比较完最后一个位置&#xff0c;此时注意到 …

红队攻防之hash登录RDP

没什么好害怕&#xff0c;孩子放心去飞吧&#xff0c;在你的身后有个等你的家 Restricted Admin Mode 受限管理模式是一项 Windows 功能&#xff0c;可防止将 RDP 用户的凭据存储在建立 RDP 连接的计算机的内存中。 这是用来防止用户&#xff08;管理员&#xff09;在 RDP 进…

shopee数据:如何获取Shopee平台的数据

在如今快速发展的跨境电商市场中&#xff0c;Shopee&#xff08;虾皮&#xff09;作为一个备受关注的平台&#xff0c;主要服务于东南亚市场。对于卖家来说&#xff0c;了解Shopee平台的数据是非常重要的&#xff0c;因为这可以帮助他们更好地了解市场趋势、优化商品策略并提高…

Ubuntu 20.0 + mysql 8.0 用户和密码修改

第一步 下载&#xff08;简单,注意联网&#xff09;Ubuntu 终端输入以下两行命令 (1) 数据库的服务端及客户端数据库的开发软件包 sudo apt-get install mysql-server mysql-client (2) 数据库的开发软件包 sudo apt-get install libmysqlclient-dev 第二步 查看是否安装成功 …

血的教训--redis被入侵之漏洞利用复现--总览

血的教训–redis被入侵之漏洞利用复现–总览 相信大家对于自己的服务器被入侵&#xff0c;还是比较憎恨的&#xff0c;我的就被攻击了一次&#xff0c;总结经验&#xff0c;自己也是整理了这一个系列&#xff0c;从最基础到最后面的自己总结被攻破的步骤&#xff0c;非常清晰的…

20世纪30年代的大危机

背景 1929年9月&#xff0c;美国财政部部长安德鲁梅隆向公众保证“现在没有担心的理由&#xff0c;这一繁荣的高潮将会继续下去”。 当时流行的一首儿歌&#xff1a;“梅隆拉响汽笛&#xff0c;胡佛敲起钟&#xff0c;华尔街发出信号&#xff0c;美国往地狱里冲&#xff01;”…

Elasticsearch:向量搜索 (kNN) 实施指南 - API 版

作者&#xff1a;Jeff Vestal 本指南重点介绍通过 HTTP 或 Python 使用 Elasticsearch API 设置 Elasticsearch 以进行近似 k 最近邻 (kNN) 搜索。 对于主要使用 Kibana 或希望通过 UI 进行测试的用户&#xff0c;请访问使用 Elastic 爬虫的语义搜索入门指南。你也可以参考文章…

聚观早报 |红魔9 Pro开卖;真我GT5 Pro定档

【聚观365】11月29日消息 红魔9 Pro开卖 真我GT5 Pro定档 一加12镜头细节公布 Redmi K70 Pro将搭载夜枭算法 苹果Vision Pro头显下月量产 红魔9 Pro开卖 红魔电竞旗舰最新力作——红魔9 Pro系列正式发布。作为一款全能电竞旗舰&#xff0c;该机搭载了第三代骁龙8移动平台…

计网Lesson4 - 计算机组网模型

文章目录 计算机的连接方式1. 两台计算机的互联2. 多台计算机的互联&#xff08;旧式&#xff09;3. 多台计算机的互联 --- 集线器&#xff08;Hub&#xff09;4. 网桥5. 多台计算机的互联 --- 交换器&#xff08;Switch&#xff09; 计算机的连接方式 1. 两台计算机的互联 网…

web框架,django,路由控制,视图层(补充)

web框架 是什么&#xff1f; web 框架是什么---》别人帮咱们写了一些基础代码---》我们只需要在固定的位置写固定的代码--》就能实现一个web应用 Web框架&#xff08;Web framework&#xff09;是一种开发框架&#xff0c;用来支持动态网站、网络应用和网络服务的开发。这大多…

漏洞扫描-德迅云安全漏洞扫描服务

漏洞扫描是指基于漏洞数据库&#xff0c;通过扫描等手段对指定的远程或者本地计算机系统的安全脆弱性进行检测&#xff0c;发现可利用漏洞的一种安全检测的行为。 漏洞扫描的主要目的是发现系统、网络或应用程序中可能存在的安全漏洞和缺陷&#xff0c;以便及时修复这些漏洞和缺…

python实验3 石头剪刀布游戏

实验3&#xff1a;石头剪刀布游戏 一、实验目的二、知识要点图三、实验1. 石头剪刀布2. 实现大侠个人信息 一、实验目的 了解3类基本组合数据类型。理解列表概念并掌握Python中列表的使用。理解字典概念并掌握Python中字典的使用。运用jieba库进行中文分词并进行文本词频统计。…

C/C++ 发送与接收HTTP/S请求

HTTP&#xff08;Hypertext Transfer Protocol&#xff09;是一种用于传输超文本的协议。它是一种无状态的、应用层的协议&#xff0c;用于在计算机之间传输超文本文档&#xff0c;通常在 Web 浏览器和 Web 服务器之间进行数据通信。HTTP 是由互联网工程任务组&#xff08;IETF…

上海亚商投顾:沪指震荡反弹 汽车产业链掀涨停潮

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数昨日震荡反弹&#xff0c;北证50指数跌超4%&#xff0c;近50只北交所个股跌超10%。 新能源车产业链掀…

不同路径 II(力扣LeetCode)动态规划

不同路径 II 题目描述 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物。…

【Android Jetpack】Hilt 依赖注入框架

文章目录 依赖注入DaggerHiltKoin添加依赖项Hilt常用注解的含义HiltAndroidAppAndroidEntryPointInjectModuleInstallInProvidesEntryPoint Hilt组件生命周期和作用域如何使用 Hilt 进行依赖注入 依赖注入 依赖注入是一种软件设计模式&#xff0c;它允许客户端从外部源获取其依…

智能井盖传感器怎么监测井盖倾斜?

城市道路上的井盖是常见的安全隐患&#xff0c;由于井盖质量不过关、安装不合理等原因导致的井盖位移或倾斜等事故&#xff0c;每年都处于不断增加的状态。为了减少此类案件的发生并维护社会治安&#xff0c;国家相关部门已经制定了多项政策法规对井盖进行统一监管。鼓励各个城…

有哪些不错的golang开源项目?

前言 下面是github上的golang项目&#xff0c;适合练手&#xff0c;可以自己选择一些项目去练习&#xff0c;整理不易&#xff0c;希望能多多点赞收藏一下&#xff01;废话少说&#xff0c;我们直接进入正题>>> 先推荐几个教程性质的项目&#xff08;用于新手学习、…

C Primer Plus讲解前置说明

说明 本来是准备接着写下去&#xff0c;写着就发现思路整理的有点杂乱无章。果然是“想一千次&#xff0c;不如去做一次”。所以这次准备基于《C Primer Plus》第六版给大家分享一下&#xff0c;也是自己梳理的一个过程。有关C语言的书很多&#xff0c;我选这本也恰巧是因为以前…

Django二转Day02

http #1 http 是什么#2 http特点#3 请求协议详情 -请求首行---》请求方式&#xff0c;请求地址&#xff0c;请求协议版本 -请求头---》key:value形式 -referer&#xff1a;上一次访问的地址 -user-agenet&#xff1a;客户端类型 -name&#x…