IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++实现信号槽 -> 正文阅读

[C++知识库]c++实现信号槽

本人虽不用Qt框架,但对其中的信号槽机制很感兴趣,近期读了csdn几篇关于信号槽的文章,从自身角度理解记录如下:

用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";
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-18 15:47:14  更:2021-12-18 15:47:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 13:43:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码