使用c++函数式编程实现Qt信号槽机制

问题背景

在下面的代码中,Input输入器 输入数据,希望A和B 接收数据。但使用的赋值,导致in.a和a只是拷贝数据,而不是同一个对象,使得数据不同步。

#include <iostream>
struct A
{
    int age = 32;
};
struct B
{
    int age = 10;
};

struct Input
{
    void fun(int i){
        a.age = i;
        b.age = i;
    }
    A a;
    B b;
};
int main(int argc, char *argv[])
{
    Input in;
    A a;
    B b;
    in.a = a;
    in.b = b;
    in.fun(3);
    std::cout<<a.age<<" "<<b.age<<std::endl;//32 10
    return 0;
}

解决方法1:如下所示,当希望修改in.a的age时能修改到A a的age,需要传指针A,而且还要手动指定in.a = &a

#include <iostream>
struct A
{
    int age = 32;
};
struct Input
{
    void fun(int i){
        a->age = i;
        std::cout<<a->age<<std::endl;
    }
    A* a;
};
int main(int argc, char *argv[])
{
    Input in;
    A a;
    in.a = &a;
    in.fun(4);//4
    return 0;
}

解决方法2:使用function实现回调函数,将fun函数的赋值操作写在回调函数中

#include <iostream>
#include<functional>
struct A
{
    int age = 32;
};
struct Input
{
    void fun(int i){
        callback(i);
    }
    std::function<void(int)> callback;
};
int main(int argc, char *argv[])
{
    Input in;
    A a;
    in.callback = [&a](int i){
        a.age = i;
        std::cout<<a.age<<std::endl;
    };
    in.fun(4);//4
    return 0;
}

方法3:将添加回调函数和执行回调函数抽离出来,实现成Signal信号的形式

#include <iostream>
#include<functional>
#include <vector>
struct Signal
{
    std::vector<std::function<void(int)>> m_callbacks;
    void connect(std::function<void(int i)> callback){
        m_callbacks.push_back(std::move(callback));
    }
    void emit(int i){
        for(auto&& callback: m_callbacks){
            callback(i);
        }
    }
};
struct A
{
    int age = 32;
};
struct Input
{
    void fun(int i){
        on_input.emit(i);
    }
    Signal on_input;
};
int main(int argc, char *argv[])
{
    Input in;
    A a;
    in.on_input.connect([&a](int i){
        a.age = i;
        std::cout<<a.age<<std::endl;
    });
    in.on_input.connect([&a](int i){
        a.age = i;
        std::cout<<a.age<<std::endl;
    });
    in.fun(4);//4
    return 0;
}

4:如果类A需要注册一个退出事件on_exit,有如下实现。但实际上我们并不希望在此信号传递参数int i。

#include <iostream>
#include <functional>
#include <vector>
struct Signal
{
    std::vector<std::function<void(int)>> m_callbacks;
    void connect(std::function<void(int i)> callback){
        m_callbacks.push_back(std::move(callback));
    }
    void emit(int i){
        for(auto&& callback: m_callbacks){
            callback(i);
        }
    }
};
struct A
{   
    void on_input(int i) const {
        std::cout<<"input "<<age<<std::endl;
    }
    void on_exit() const {
        std::cout<<"exit "<<std::endl;
    }
    int age = 32;
};
struct Input
{
    //调用该函数就发出 进入事件和退出事件的信号
    void fun(int i){
        on_input.emit(i);
        on_exit.emit(i);
    }
    Signal on_input;//进入事件
    Signal on_exit;//退出事件
};
int main(int argc, char *argv[])
{
    Input in;
    A a;
    in.on_input.connect([&a](int i){
        a.age = i;
        a.on_input(i);
    });
    in.on_exit.connect([&a](int i){
        a.on_exit();
    });
    in.fun(4);//4
    return 0;
}

5:为了信号更加通用,使用变长模板参数来实现。注意:…在左边表示定义,在右边表示使用

#include <iostream>
#include <functional>
#include <vector>
template<class ...T>//定义T
struct Signal
{
    std::vector<std::function<void(T...)>> m_callbacks;//使用T
    void connect(std::function<void(T...)> callback){
        m_callbacks.push_back(std::move(callback));
    }
    void emit(T... t){//使用T, 定义t
        for(auto&& callback: m_callbacks){
            callback(t...);//使用t
        }
    }
};
struct A
{   
    void on_input(int i) const {
        std::cout<<"input "<<age<<std::endl;
    }
    void on_exit() const {
        std::cout<<"exit "<<std::endl;
    }
    int age = 32;
};
struct Input
{
    //调用该函数就发出 进入事件和退出事件的信号
    void fun(int i){
        on_input.emit(i);
        on_exit.emit();
    }
    Signal<int> on_input;//进入事件
    Signal<> on_exit;//退出事件
};
int main(int argc, char *argv[])
{
    Input in;
    A a;
    in.on_input.connect([&a](int i){
        a.age = i;
        a.on_input(i);
    });
    in.on_exit.connect([&a](){
        a.on_exit();
    });
    in.fun(4);//4
    return 0;
}

6:上述代码main函数中connect时传入的lambda表达式,下面在Signal中将其封装为bind方法,并提供对应的connect函数,使其更类似于Qt信号的connect。实际上Qt中是使用字符串来查找匹配的类型名,而这里我们使用模板更加高效

#include <iostream>
#include <functional>
#include <vector>

template<class Self, class MemFn>
auto bind(Self *self, MemFn memfn){//第2个参数为成员函数指针:void(A::*)(int i),这里使用模板来避免写成该复杂类型
    //调用成员函数指针(a->*memfn)()。如果是普通函数指针,就是(*memfn)()这样调用
    return [self, memfn](auto... t){
        (self->*memfn)(t...);
    };
}

template<class ...T>//定义T
struct Signal
{
    std::vector<std::function<void(T...)>> m_callbacks;//使用T
    void connect(std::function<void(T...)> callback){
        m_callbacks.push_back(std::move(callback));
    }
    //提供一个bind版本的connect,类似qt语法
    template<class Self, class MemFn>
    void connect(Self *self, MemFn memfn){
        m_callbacks.push_back(bind(self, memfn));
    }

    void emit(T... t){//使用T, 定义t
        for(auto&& callback: m_callbacks){
            callback(t...);//使用t
        }
    }
};
struct A
{   
    void on_input(int i){
        age = i;
        std::cout<<"input "<<age<<std::endl;
    }
    void on_exit(std::string s) const {
        std::cout<<"exit "<<s<<std::endl;
    }
    int age = 32;
};
struct Input
{
    //调用该函数就发出 进入事件和退出事件的信号
    void fun(int i){
        on_input.emit(i);
        on_exit.emit("byebye");
    }
    Signal<int> on_input;//进入事件
    Signal<std::string> on_exit;//退出事件
};


int main(int argc, char *argv[])
{
    Input in;
    A a;
    in.on_input.connect(&a, &A::on_input);
    in.on_exit.connect(&a, &A::on_exit);
    in.fun(4);//4
    return 0;
}

下面写了重载函数test_fun作为要connect的函数,此时必须写明要使用哪个函数,因此下面使用static_cast进行转换

void test_fun(int m){
    std::cout<<"int "<<m<<std::endl;
}
void test_fun(std::string m){
    std::cout<<"string "<<m<<std::endl;
}
//function要求必须有唯一的重载,这样必须指定使用哪个
in.on_input.connect(static_cast<void(*)(int)>(test_fun));

7:为了避免connect的对象提前析构,下面代码使用智能指针

template<class Self, class MemFn>
auto bind(Self self, MemFn memfn){
    return [self, memfn](auto... t){
        ((*self).*memfn)(t...);
    };
}

void test2(Input &input){
    auto a = std::make_shared<A>();//使用智能指针而不是A a,避免对象提前析构
    input.on_input.connect(a, &A::on_input);//这里智能指针a发生拷贝
    input.on_exit.connect(a, &A::on_exit);
}

int main(int argc, char *argv[])
{
    Input in;
    test2(in);
    in.fun(3);
    return 0;
}

如果是connect lambda表达式,注意按值捕获,否则智能指针和a对象都会提前析构掉

void test2(Input &input){
    auto a = std::make_shared<A>();
    input.on_input.connect([a](int i){//注意按值捕获a
        return a->on_input(i);
    });
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

为什么人员定位系统很有必要性?

人员定位系统在现代社会和企业环境中具有极高的必要性&#xff0c;这主要体现在以下几个方面&#xff1a; 一、安全保障 二、提升效率 三、管理优化 四、增强合规性 综上所述&#xff0c;人员定位系统通过提供实时、准确的位置信息&#xff0c;为企业带来了安全保障、效率提升…

香橙派AIpro实测:YOLOv8便捷检测,算法速度与运行速度结合

香橙派AIpro实测&#xff1a;YOLOv8便捷检测&#xff0c;算法速度与运行速度结合 文章目录 香橙派AIpro实测&#xff1a;YOLOv8便捷检测&#xff0c;算法速度与运行速度结合一、引言二、香橙派AIpro简介三、YOLOv8检测效果3.1 目标检测算法介绍3.1.1 YOLO家族3.1.2 YOLOv8算法理…

500mA、低压差、低噪声、超快、无需旁路电容的CMOS LDO稳压器RT9013

一般描述 RT9013 SOT23-5封装的外观和丝印 RT9013 是一款高性能的 500mA LDO 稳压器&#xff0c;具有极高的 PSRR 和超低压差。非常适合具有苛刻性能和空间要求的便携式射频和无线应用。 RT9013的静态电流低至25μA&#xff0c;进一步延长了电池的使用寿命。RT9013 也适用于低…

Element中的日期时间选择器DateTimePicker和级联选择器Cascader

简述&#xff1a;在Element UI框架中&#xff0c;Cascader&#xff08;级联选择器&#xff09;和DateTimePicker&#xff08;日期时间选择器&#xff09;是两个非常实用且常用的组件&#xff0c;它们分别用于日期选择和多层级选择&#xff0c;提供了丰富的交互体验和便捷的数据…

私域和社群的差别是什么?

社群就是拉很多人建群就可以了&#xff0c;但是私域不是&#xff0c;这里有三点不同 1、私域的用户来源&#xff0c;不仅仅是微信&#xff0c;而是基于一定的联系形成的链接&#xff0c;比如买了商家的货&#xff0c;反复购买觉得好&#xff0c;推荐给亲朋好友的二次开发用户&…

nanodiffusion代码逐行理解之Attention

目录 一、注意力中的QKV二、注意力中的位置嵌入三、注意力中的多头四、注意力和自注意力五、注意力中的encode和decoder 一、注意力中的QKV 简单来说&#xff1a; Q: 要查询的信息 K: 一个索引&#xff0c;要查询的向量 V: 我们查询得到的值 复杂一点的解释&#xff1a; Query…

如何快速选择短剧系统源码:高效构建您的在线短剧平台

在数字化时代&#xff0c;短剧作为一种新兴的娱乐形式&#xff0c;受到了广泛的欢迎。随着市场需求的增长&#xff0c;构建一个在线短剧平台成为了很多创业者和开发者的目标。而选择正确的短剧系统源码则是实现这一目标的关键步骤。本文将为您提供一些实用的指导&#xff0c;帮…

论文解析——Transformer 模型压缩算法研究及硬件加速器实现

作者及发刊详情 邓晗珂&#xff0c;华南理工大学 摘要 正文 实验平台 选取模型&#xff1a; T r a n s f o r m e r b a s e Transformer_{base} Transformerbase​ 训练数据集&#xff1a;WMT-2014 英语-德语翻译数据集、IWSLT-2014 英语-德语互译数据集 Transformer模…

策略模式入门:基本概念与应用

目录 策略模式策略模式结构策略模式应用场景策略模式优缺点练手题目题目描述输入描述输出描述题解 策略模式 策略模式&#xff0c;又称政策模式&#xff0c;是一种行为型设计模式&#xff0c;它能让你定义一系列算法&#xff0c;并将每种算法分别放入独立的类中&#xff0c;以…

基于Spring Boot的高校智慧采购系统

1 项目介绍 1.1 摘要 随着信息技术与网络技术的迅猛发展&#xff0c;人类社会已跨入全新信息化纪元。传统的管理手段因其内在局限&#xff0c;在处理海量信息资源时日渐捉襟见肘&#xff0c;难以匹配不断提升的信息管理效率和便捷化需求。顺应时代发展趋势&#xff0c;各类先…

http数据传输确保完整性和保密性整流程方案(含源码)

往期文章回顾 【深度学习】 【深度学习】物体检测/分割/追踪/姿态估计/图像分类检测演示系统【含源码】【深度学习】YOLOV8数据标注及模型训练方法整体流程介绍及演示【深度学习】行人跌倒行为检测软件系统【深度学习】火灾检测软件系统【深度学习】吸烟行为检测软件系统【深度…

rtpengine_mr12.0 基础建设容器运行

目录 Dockerfile rtpengine.conf 容器内编译安装 RTPEngine 正常提供功能 1. 启动RTPEngine服务 2. 删除 RTPEngine服务 3. 加载内核模块 检查所有进程是否正在运行 上传到仓库 博主wx&#xff1a;yuanlai45_csdn 博主qq&#xff1a;2777137742 后期会创建粉丝群&…

实验二 图像的代数运算

一、实验目的&#xff1a; 1&#xff0e;了解图像的算术运算在数字图像处理中的初步应用。 2&#xff0e;体会图像算术运算处理的过程和处理前后图像的变化。 二、实验内容&#xff1a; 1&#xff0e;图像的加法运算 图像相加一般用于对同一场景的多幅图像求平均效果&…

【QT】概述|对象树模型|两种控件模式|信号和槽|lambda

目录 什么是QT 特点 QT程序 main函数 QT按钮 纯代码模式 图形化模式 对象树模型 信号和槽 连接与断开 自动连接 断开连接 信号的发射 lambda表达式 基本语法 捕获列表 Lambda表达式用于信号与槽的连接 例如 什么是QT Qt是一个跨平台的C图形用户界面应用…

如何在TikTok上获得更多观看量:12个流量秘诀

TikTok作为热门海外社媒&#xff0c;在跨境出海行业中成为新兴的推广渠道&#xff0c;但你知道如何让你的TikTok赢得更多关注次数吗&#xff1f;如果您正在寻找增加 TikTok 观看次数的方法&#xff0c;接下来这12种策略&#xff0c;你需要一一做好&#xff01; 1. 在内容中添加…

女性经济崛起,天润融通用客户感知挖掘市场潜力

每逢一年一度的国际妇女节&#xff0c;“女性”话题都会被郑重地讨论。 从消费市场上来说&#xff0c;最近几年女性群体正在拥有越来越大的影响力&#xff0c;甚至出现了“她经济”这样的专属词汇在最近几年被市场反复讨论。 毫无疑问&#xff0c;女性消费群体的崛起已经成为…

2.8亿东亚五国建筑数据分享

数据是GIS的血液&#xff01; 我们现在为你分享东亚5国的2.8亿条建筑轮廓数据&#xff0c;该数据包括中国、日本、朝鲜、韩国和蒙古5个东亚国家完整、高质量的建筑物轮廓数据&#xff0c;你可以在文末查看领取方法。 数据介绍 虽然开源的全球的建筑数据已经有微软的建筑数据…

【android】【adb shell】写一个shell脚本,监听进程pid变化

前言 当前业务&#xff0c;需要写一个脚本&#xff0c;不断监视com.android.phone 进程是否异常死掉 脚本 #!/system/bin/sh last_pid"" current_pid"" while(true){current_pidps -A | grep com.android.phone | awk {print $2}if [ -n "$current…

大牛“私藏”宝刊,易Accept!中科院1区-Top,偏爱国人,2个月可录!

本周投稿推荐 SCI • 能源科学类&#xff0c;1.5-2.0&#xff08;来稿即录25天&#xff09; • 计算机类&#xff0c;2.0-3.0&#xff08;纯正刊29天录用&#xff09; EI • 各领域沾边均可&#xff08;2天录用&#xff09; CNKI • 7天录用-检索&#xff08;急录友好&a…

职场办公受欢迎的电脑桌面便签,手机电脑同步的备忘录

在快节奏的职场生活中&#xff0c;有效的时间管理和信息记录变得尤为重要。为了帮助大家更好地应对工作挑战&#xff0c;好用的电脑桌面便签和手机电脑同步的备忘录&#xff0c;好用便签应运而生&#xff0c;成为了当前职场办公中的得力助手。 好用便签是一款备受青睐的电脑桌…