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

news2024/11/25 20:33:50

纯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 time
    auto 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 signals
    obj.signal.connect("signal1", std::bind(&Object::slot1, &obj));
    obj.signal.connect("signal2", std::bind(&Object::slot2, &obj, 42));

    // Emit signals
    obj.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 signal
        std::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 parameters
    Signal signal;
    signal.data = "Hello";
    signal.parameters["param1"] = "10";
    signal.parameters["param2"] = "value";

    // Pass the signal to the chatbot
    
    Chatbot 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() {//槽函数1
        std::cout << "Slot 1 called" << std::endl;
    }

    void slot2(int value) {//槽函数1
        std::cout << "Slot 2 called with value: " << value << std::endl;
    }

    void signal1() {//信号函数1
        this->slot1();
    }

    void signal2(int value) {//信号函数2
        this->slot2(value);
    }
};

int main() {
    Object obj;

    // Connect slots to signals
    obj.signal.connect("signal1", std::bind(&Object::signal1, &obj));//bind就是对象方法+对象实例
    obj.signal.connect("signal2", std::bind(&Object::signal2, &obj, 42));

    // Emit signals
    obj.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.coloradmin.cn/o/1265136.html

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

相关文章

使用elasticsearch-head插件修改elasticsearch数据

1、先使用elasticsearch-head插件基本查询功能找到要修改的数据&#xff0c;看看是否存在 2、切换到elasticsearch-head复合查询界面&#xff0c;输入数据修改地址&#xff1a; http://es的ip地址:端口号/索引名称/文档类型&#xff08;没特殊设置过就是_doc&#xff09;/文档…

springboot 自定义starter逐级抽取

自定义starter 背景:各个组件需要引入starter 还有自己的配置风格 –基本配置原理 &#xff08;1&#xff09;自定义配置文件 导入配置可以在配置文件中自动识别&#xff0c;提示 导入依赖后可以发现提示 &#xff08;2&#xff09;配置文件实现 –让配置文件对其他模块生…

15.Docker-Compose的概念理解及安装

1.Docker-Compose是什么&#xff1f; Docker-Compose是实现对Docker容器集群的快速编排的工具软件。它是Docker官方开源的一个工具软件&#xff0c;可以管理多个Docker容器组成一个应用。你需要定义一个YAML格式的配置文件docker-compose.yml.写好多个容器间的调用关系&#x…

父进程隐藏——ConsoleApplication903项目

首先我发现用calc来做进程隐藏实验是失败的&#xff0c;父进程一直都是svhost.exe 那么我用我自己生成的cs木马beacon903.exe试试 试试explorer.exe 再试试cmd.exe 可以看到成功变成cmd.exe 可以看到我们可以通过这种方式虚假父进程 以上我们是直接获得的pid&#xff0c;那…

使用shell快速查看电脑曾经连接过的WiFi密码

此方法只能查看以前连接过的wifi名称和对应的密码 查看连接过的WiFi名称netsh wlan show profiles查看具体的WiFi名称netsh wlan show profile name"你的wifi名称" keyclear

万宾科技可燃气体监测仪科技作用全览

燃气管网在运行过程中经常会遇到燃气管道泄漏的问题&#xff0c;燃气泄漏甚至会引起爆炸&#xff0c;从而威胁人民的生命和财产安全&#xff0c;因此对燃气管网进行定期巡检是十分必要的工作。但是传统的人工巡检已不能满足城市的需要&#xff0c;除了选择增加巡检人员之外&…

ssm购物商城系统

摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和管理就很关键。因此商城购物信息的…

WordPress插件大全-免费的WordPress插件汇总

随着互联网的不断发展&#xff0c;网站建设变得日益普及。对于大多数人而言&#xff0c;WordPress是一个熟悉且易于使用的网站建设平台。然而&#xff0c;有时候我们可能会觉得WordPress的功能还不够满足我们的需求&#xff0c;这时候&#xff0c;插件就成了解决问题的得力工具…

js数组map()的用法

JavaScript Array map() 方法 先说说这个方法浏览器的支持&#xff1a; 支持五大主流的浏览器&#xff0c; 特别注意&#xff1a;IE 9 以下的浏览器不支持&#xff0c;只支持IE 9以上的版本的浏览器 特别注意&#xff1a;IE 9 以下的浏览器不支持&#xff0c;只支持IE 9以上的…

<JavaDS> 二叉树遍历各种遍历方式的代码实现 -- 前序、中序、后序、层序遍历

目录 有以下二叉树&#xff1a; 一、递归 1.1 前序遍历-递归 1.2 中序遍历-递归 1.3 后序遍历-递归 二、递归--使用链表 2.1 前序遍历-递归-返回链表 2.2 中序遍历-递归-返回链表 2.3 后序遍历-递归-返回链表 三、迭代--使用栈 3.1 前序遍历-迭代-使用栈 3.2 中序遍…

flask web开发学习之初识flask(一)

一、概念 flask是一个使用python编写的轻量级web框架&#xff0c;作者为Armin Ronacher&#xff08;中文名&#xff1a;阿尔敏罗纳彻&#xff09;&#xff0c;它广泛被应用于web开发和API。flask提供了简洁而灵活地方式来构建web应用&#xff0c;它不会强加太多约束&#xff0…

docker安装Sentinel zipkin

文章目录 引言I Sentinel安装1.1 运行容器1.2 DOCKERFILE 参考1.3 pom 依赖1.4 .yml配置(整合springboot)II 资源保护2.1 Feign整合Sentinel2.2 CommonExceptionAdvice:限流异常处理类III zipkin引言 消息服务和请求第三方服务可不配置Sentinel。 I Sentinel安装 Sentinel …

智能优化算法应用:基于人工蜂群算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于人工蜂群算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于人工蜂群算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.人工蜂群算法4.实验参数设定5.算法结果6.参考…

玻色量子真机测试完整报告

​ 真机测试 2023年 2023.8 量子计算突破云渲染资源调度&#xff01;真机测试完整报告公开&#xff01; 2023.8 量子计算突破金融信用评分&#xff01;真机测试完整报告公开&#xff01; 组合优化问题专题 2023年 2023.7 玻色量子“揭秘”之旅行商问题与Ising建模 2023.…

食谱菜谱大全API接口

食谱菜谱大全API接口 一、食谱菜谱大全API接口二、使用步骤1、接口2、请求参数3、请求参数示例4、接口 返回示例 三、 如何获取appKey和uid1、申请appKey:2、获取appKey和uid 四、重要说明 一、食谱菜谱大全API接口 包含所有家用或者商用的食谱菜谱的API接口 二、使用步骤 1…

MySQL索引使用总结

索引(index) 官方定义&#xff1a;一种提高MySQL查询效率的数据结构 优点&#xff1a;加快查询速度 缺点&#xff1a; 1.维护索引需要消耗数据库资源 2.索引需要占用磁盘空间 3.增删改的时候会影响性能 索引分类 索引和数据库表的存储引擎有关&#xff0c;不同的存储引擎&am…

uniapp地图基本使用及解决添加markers不生效问题?

uniapp地图使用 App端 通过 nvue 页面实现地图 文章目录 uniapp地图使用效果图templatejs添加 marker使用地图查看位置移到到当前位置 效果图 template <template><view class"mapWrap"><!-- #ifdef APP-NVUE --><map class"map-containe…

Java---权限修饰符、final、static

文章目录 1. 权限修饰符2. final(最终态)3. static(静态) 1. 权限修饰符 修饰符同一个类中同一个包中的子类和无关类不同包的子类不同包的无关类private√默认√√protected√√√public√√√√ 2. final(最终态) 1. final关键字是最终的意思&#xff0c;可以修饰成员方法、…

量子力学技术前沿:探索、挑战与未来

量子力学技术前沿:探索、挑战与未来 一、引言 量子力学,这门揭示微观世界规律的学科,自诞生以来就在科技领域发挥着举足轻重的作用。随着科技的飞速发展,量子力学的应用也在不断拓展和深化。今天,我将带领大家一起领略量子力学技术的魅力,探讨其发展趋势和挑战。 二、量…

【Vue】绝了!这生命周期流程真...

hello&#xff0c;我是小索奇&#xff0c;精心制作的Vue系列持续发放&#xff0c;涵盖大量的经验和示例&#xff0c;如果对您有用&#xff0c;可以点赞收藏哈~ 生命周期 Vue.js 组件生命周期&#xff1a; 生命周期函数&#xff08;钩子&#xff09;就是给我们提供了一些特定的…