通过c++11的std::bind及std::function实现类方法回调,模拟Qt实现信号槽

c++11引入了std::bind及std::function,实现了函数的存储和绑定,即先将可调用的对象保存起来,在需要的时候再调用。网上有很多介绍
Qt信号槽实现信号的发送和接收,类似观察者。简单说明:
sender:发出信号的对象
signal:发送对象发出的信号
receiver:接收信号的对象
slot:接收对象在接收到信号之后所需要调用的函数(槽函数)
emit:发送信号
下面实现第一种:

#include <functional>
#include <iostream>

class SignalObject
{
public:
    void connect(std::function<void(int)> slot)
    {
        _call = slot;
    }
    void emitSignal(int signal)
    {
        _call(signal);
    }
private:
    std::function<void(int)>  _call;
};


class SlotObject
{
public:
    SlotObject(){}
public:
    void slotMember(int signal)
    {
        std::cout<<"signal:"<<signal<<" recv:"<<this<<std::endl;
    }
};

定义了SignalObject信号类和SlotObject槽类,其中信号类中的 std::function<void(int)> _call就是要绑定的槽函数,即回调函数,下面是信号槽绑定:

    SignalObject signalObject;
    SlotObject   slotObject;
    std::cout<<"slotObject:"<<&slotObject<<std::endl;
    signalObject.connect(std::bind(&SlotObject::slotMember,slotObject,std::placeholders::_1));
    signalObject.emitSignal(1);

输出:

slotObject:0x62fe3b
signal:1 recv:0x1040f08

可以发现成功调用了回调函数,并正确接收到了信号,我们的成员函数可以通过回调实现了调用。
但是接收者的地址并不是我们定义的slotobject,即connect的是别的对象,开篇链接介绍知,connect过程发生了拷贝构造。
修改我们的信号类,可以避免拷贝构造

class SignalObject2
{
public:
    void connect(SlotObject* recver,std::function<void(SlotObject*,int)> slot)
    {
        _recver = recver;
        _call = slot;
    }
    void emitSignal(int signal)
    {
        _call(_recver,signal);
    }
private:
    SlotObject* _recver;
    std::function<void(SlotObject*,int)>  _call;
};

即我们在connect时把recv保存起来

    SlotObject   slotObject;
    std::cout<<"slotObject:"<<&slotObject<<std::endl;
    
    SignalObject2 signalObject2;
    std::function<void(SlotObject*,int)> slot = &SlotObject::slotMember;
    signalObject2.connect(&slotObject,slot);
    signalObject2.emitSignal(2);

输出如下:

slotObject:0x62fe3b
signal:2 recv:0x62fe3b

当一个槽slot和多个信号signal连接者,我们并不知道是谁调用的,Qt中我们知道可以通过sender()返回一个QObject*来判断,这里模仿实现sender方法

class Object
{
public:
    Object* self()
    {
        return this;
    }
    std::function<Object*(void)>  _sender;
};

class SlotObject3:public Object
{
public:
    SlotObject3(){}
public:
    void slotMember(int signal)
    {
        if(_sender){
            std::cout<<"sender:"<<_sender()<<std::endl;
        }
        std::cout<<"signal:"<<signal<<" recv:"<<this<<std::endl;
    }
};
class SignalObject3:public Object
{
public:
    void connect(SlotObject3* recver,std::function<void(SlotObject3*,int)> slot)
    {
        _recver = recver;
        _call = slot;
    }
    void emitSignal(int signal)
    {
        _recver->_sender = std::bind(&SignalObject3::self,this);
        _call(_recver,signal);
        _recver->_sender = NULL;
    }
private:
    SlotObject3* _recver;
    std::function<void(SlotObject3*,int)>  _call;
};

即定义一个基类Object和一个回调变量sender,在每次发送时绑定上发送者即可

    SignalObject3 signalObject3;
    SlotObject3   slotObject3;
    std::cout<<"signalObject3:"<<&signalObject3<<std::endl;
    std::cout<<"slotObject3:"<<&slotObject3<<std::endl;
    std::function<void(SlotObject3*,int)> slot3 = &SlotObject3::slotMember;
    signalObject3.connect(&slotObject3,slot3);
    signalObject3.emitSignal(3);

输出:

signalObject3:0x62fdf0
slotObject3:0x62fde0
sender:0x62fdf0
signal:3 recv:0x62fde0

虽然代码很简单,但还是附上源码

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值