详解c++实现信号槽

 更新时间:2021年12月19日 16:41:06   作者:newcly2003  
这篇文章主要为大家介绍了c++实现信号槽,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

用c++实现信号槽机制(signal-slot)

信号槽机制的个人理解:信号槽是在两个c++类对象之间建立联系的通道,其中一个对象可称之为信号发送者(sender),另一个对象可称之为信号接收者(recver),sender通过信号槽发出信号后,recver就可以执行函数进行某些操作。也就是说应用程序通过信号槽可以在两个互不相关的对象之间建立起逻辑关系,使程序开发变得简洁、方便。

信号槽本质是由c++定义的类组成,分为两个部分:槽类和信号类

槽类(slot):可理解为插槽,其内部有两个私有成员,存储待执行的类对象和对象中的方法指针,可理解将信号接收者(recver)插入了插槽中。槽的另一端连接信号,通过在信号(signal)类的实例中存储槽(slot)指针的方式实现,若在signal对象中插入多个slot,则代表一个信号与多个recver建立了联系,当信号来临时,可以根据slot的插入先后顺序轮流执行事件方法。

信号类(signal):其内部有一个容器,存储连接到信号类实例的槽类指针,同时提供一个执行方法,当方法调用时,在方法内部调用所存储的槽类指针所指向的槽内部保存的recver的方法。

若要使用信号槽,则信号发送者(sender)类需要在内部包含信号类(signal)实例,同时包含一个方法来调用signal上的执行方法。该方法称之为发出信号。

综上,作为sender的任意object类包含有signal成员,通过func发出信号,func内部调用object.signal上的执行方法,此处通过重载()实现,执行方法内部是循环调用保存在signal中的slot指针列表,而slot指针指向的slot实例中存储有recver和recver.func,通过slot的exec方法则可以执行func,这就实现了一个触发信号的完整流程。

为了实现任意sender和任意recver关联,slot和signal类并不知道要关联对象以及执行方法的具体类型和参数类型,因此要使用泛型编程

代码及说明如下(整理借鉴自csdn):

/// <summary>
/// 先实现槽基类,包含两个虚函数对象,主要为后续调用提供接口
/// 子类负责实现exec方法
/// </summary>
/// <typeparam name="TParam">Recver中待执行函数的参数类型</typeparam>
template <class TParam>
class SlotBase
{
public:
    virtual void Exec(TParam param) = 0;
    virtual ~SlotBase() {};
};
/// <summary>
/// Slot子类,负责实现exec方法,通过exec调用recver.func,同时Slot构造函数负责初始化两个内部变量,将需要关联的recver和recver.func插入槽
/// </summary>
/// <typeparam name="TRecver">recver类的具体类型</typeparam>
/// <typeparam name="TParam">recver.func的参数类型</typeparam>
template <class TRecver,class TParam>
class Slot:public SlotBase<TParam>
{
public:
    Slot(TRecver* pObj, void (TRecver::* func)(TParam)) 
    {
        m_pRecver = pObj;
        m_func = func;
    };
    VOID Exec(TParam param)
    {
        (m_pRecver->*m_func)(param);
    };
private:
    TRecver* m_pRecver = NULL;
    void (TRecver::* m_func)(TParam);
};
/// <summary>
/// 信号类
/// 私有成员m_pSlotSet,存储槽指针的vector
/// 重载(),在括号调用参数时,循环调用m_pSlotSet中存储的槽指针,将参数传递给槽的exec方法
/// bind():将一个槽与信号类实例关联起来
/// 
/// </summary>
/// <typeparam name="TParam">待执行方法的参数类型</typeparam>
/// <typeparam name="TRecver">recver类的类型</typeparam>
template<class TParam>
class Signal
{
public:
    template<class TRecver>
    void Bind(TRecver* pObj, void (TRecver::* func)(TParam))
    {
        m_pSlotSet.push_back(new Slot<TRecver,TParam>(pObj, func));
    };
    void operator()(TParam param)
    {
        for(int i=0;i<m_pSlotSet.size();i++)
        {
            m_pSlotSet[i]->Exec(param);
        }
    };
    ~Signal() 
    {
        for (int i = 0; i < m_pSlotSet.size(); i++)
            delete m_pSlotSet[i];
    };
private:
    std::vector<SlotBase<TParam>*> m_pSlotSet;
};
//开始模拟
class RecverOne
{
public:
    void functionOne(int param) 
    {
        std::cout << "这是接收者1中的某个方法执行:" << param << std::endl;
    };
};
class RecverTwo
{
public:
    void functionTwo(int param)
    {
        std::cout << "这是接收者2中的某个方法执行:" << param << std::endl;
    };
};
class SenderObj
{
public:
    //模拟值改变发出信号
    void testSginal(int param) 
    {
        valueChanged(param);
    };
public:
    Signal<int> valueChanged;//定义一个当值改变时触发的信号
};
//为了更方便地将sender中的signal与槽和recver关联,可以定义一个宏
#define connect(sender,signal,recver,method) ((sender)->signal.Bind(recver,method))
int main()
{
    //开始测试
    //先定义两个接收者
    RecverOne* R1 = new RecverOne;
    RecverTwo* R2 = new RecverTwo;
    //定义一个发送者
    SenderObj* sd = new SenderObj;
    //将R1和R2中的函数插入sd中的信号槽
    connect(sd, valueChanged, R1, &RecverOne::functionOne);
    connect(sd, valueChanged, R2, &RecverTwo::functionTwo);
    //发送信号
    sd->valueChanged(5);
    std::cout << "Hello World!\n";
}
 

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • 详解原码、反码与补码存储与大小

    详解原码、反码与补码存储与大小

    这篇文章主要介绍了详解原码、反码与补码存储与大小的相关资料,需要的朋友可以参考下
    2017-06-06
  • C语言数据结构之使用链表模拟栈的实例

    C语言数据结构之使用链表模拟栈的实例

    这篇文章主要介绍了C语言数据结构之使用链表模拟栈的实例的相关资料,需要的朋友可以参考下
    2017-08-08
  • C++11新特性之右值引用与完美转发详解

    C++11新特性之右值引用与完美转发详解

    C++11标准为C++引入右值引用语法的同时,还解决了一个短板,即使用简单的方式即可在函数模板中实现参数的完美转发。本文就来讲讲二者的应用,需要的可以参考一下
    2022-09-09
  • 实例讲解C语言编程中的结构体对齐

    实例讲解C语言编程中的结构体对齐

    这篇文章主要介绍了C语言编程中的结构体对齐,值得注意的是一些结构体对齐的例子在不同编译器下结果可能会不同,需要的朋友可以参考下
    2016-04-04
  • Qt QString的使用实现

    Qt QString的使用实现

    本文主要介绍了Qt QString的使用实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • C++数据结构深入探究栈与队列

    C++数据结构深入探究栈与队列

    栈和队列,严格意义上来说,也属于线性表,因为它们也都用于存储逻辑关系为 "一对一" 的数据,但由于它们比较特殊,本章讲解分别用队列实现栈与用栈实现队列
    2022-05-05
  • C++实现学校运动会管理系统

    C++实现学校运动会管理系统

    这篇文章主要为大家详细介绍了C++实现学校运动会管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • C++ Cartographer源码中关于传感器的数据传递实现

    C++ Cartographer源码中关于传感器的数据传递实现

    这篇文章主要介绍了C++ Cartographer源码中关于传感器的数据传递实现,前面已经谈到了Cartographer中添加轨迹的方法和传感器的数据流动走向。发现在此调用了LaunchSubscribers这个函数来订阅相关传感器数据
    2023-03-03
  • C++ 如何实现一个日期类

    C++ 如何实现一个日期类

    通过对类和对象的学习,理解了类是对象的抽象描述,实现日期类涉及定义年月日属性及成员函数如打印日期、日期加减,重点介绍了运算符重载的概念和作用,通过代码示例展示了如何实现一个日期类,包括头文件和源文件的分离编写
    2024-10-10
  • C语言实现二叉树遍历的迭代算法

    C语言实现二叉树遍历的迭代算法

    这篇文章主要介绍了C语言实现二叉树遍历的迭代算法,包括二叉树的中序遍历、先序遍历及后序遍历等,是非常经典的算法,需要的朋友可以参考下
    2014-09-09

最新评论