下面说说一下成员函数作回调函数的使用,并使用了观察者模式
怎么说呢,推荐一篇博文,参考
dankye的C++回调机制实现
选取里面的signal—slot(信号与槽)来说,QT实现了信号与槽的整套机制,有兴趣的朋友可以去查看下源码信息
任何对象的槽可以绑定到一个对象的信号上,一个信号可以拥有多个槽。
介绍下将要登场的一些成员: slot类,类似一个连接器,将receiver 绑定到 signal上去,这样 sender就可以发signal,signal就会执行响应的slot里面的方法,slot直接调用绑定到slot里面的reciver
一个简单的signal/slot实现
template <typename T, typename T1> class slot { public: slot(T *pObj, void (T::*pMemberFunc)(T1)) { m_pObj = pObj; m_pMemberFunc = pMemberFunc; } void Execute(T1 para) { (m_pObj->*m_pMemberFunc)(para); } private: T * m_pObj; void (T::*m_pMemberFunc)(T1 para); }; template<typename T, typename T1> class signal { public: void bind(T* pObj, void (T::*pMemberFunc)(T1)) { m_slots.push_back(new slot<T, T1>(pObj, pMemberFunc)); } ~signal() { vector<slot<T,T1> *>::iterator ite = m_slots.begin(); while (ite != m_slots.end()) { delete *ite; ite++; } } void operator()(T1 para)//重载操作符() { vector<slot<T,T1> *>::iterator ite = m_slots.begin(); while (ite != m_slots.end()) { (*ite)->Execute(para); ite++; } } private: vector<slot<T,T1> *>m_slots; }; class receiver { public: void callback1(int a) { cout << "receiver1:" << a << endl; } void callback2(int a) { cout << "receiver2:" << a << endl; } }; //构造sender类产生signal信号 class sender { public: sender(int value) :m_value(value) {} ~sender() {} int get_value() { return m_value; } void set_value(int new_value) { if (new_value != m_value) { m_value = new_value; m_sig(new_value); } } signal<receiver, int> m_sig; private: int m_value; }; int main() { receiver r; sender s(5); s.m_sig.bind(&r, &receiver::callback1); s.m_sig.bind(&r, &receiver::callback2); s.set_value(6); return 0; }
简述:上面的对应关系很简单 将sender 发送signal信号 receiver 定义自己的callback 通过 slot将信号与 receiver对应起来。
因为成员函数在类外不可直接调用,必须使用对象来进行调用,所以引如了一个对象,来对回调函数进行调用。
首先将receiver 通过 slot 绑定到 signal上。然后sender发送一个signal ,执行调用。相当于信号触发,来执行signal里绑定的槽的方法。
总体的思路挺清晰的。
上面的参数模板T,T1 T代表receiver类,T1代表int类型参数,
问题:
sender类在实例化signal的时候必须提供两个模板参数,可是调用方哪会事先就知道receiver接收方的类型 呢,而且从概念上讲,事件发送方与接收方只需遵循一个共同的接口函数接口就可以了,与类没什么关系,
上个程序要求在实例化时就得填充receiver的类型, 也就决定了receiver只能一对一,而不能一对多,于是作出改进,将signal的参数T去掉,将T类型的推导延迟到绑定(bind)时,signal没有参数T,slot也就不能有,
可是参数T总得找个地方落脚啊,怎么办?让slot包含slotbase成员,slotbase没有参数T的,但slotbase只定义接口,真正的实现放到slotimpl中,slotimpl就可以挂上参数了,boost中any,share_ptr就是用此手法,
#include <vector> #include <iostream> using namespace std; /* version:2.0 */ #include <vector> #include <iostream> using namespace std; template<typename T1> class slotbase { public: virtual void Execute(T1 para) = 0;//定义虚函数 作接口用 }; template<typename T,typename T1> class slotimpl :public slotbase<T1> { public: slotimpl(T* pObj, void (T::*pMemberFunc)(T1 para)) { m_pObj = pObj; m_pMemberFunc = pMemberFunc; } virtual void Execute(T1 para) { (m_pObj->*m_pMemberFunc)(para); } private: T* m_pObj; void (T::*m_pMemberFunc)(T1 para); }; template <typename T1> class slot { public: template<typename T> slot(T*Obj, void (T::*pMemberFunc)(T1 para)) { m_Slotbase = new slotimpl<T, T1>(Obj, pMemberFunc); } ~slot() { delete m_Slotbase; } void Execute(T1 para) { m_Slotbase->Execute(para); } private: slotbase<T1> *m_Slotbase; }; template <typename T1> class signal { public: template <typename T> void bind(T* pObj, void(T::*pMemberFunc)(T1 para)) { m_slots.push_back(new slot<T1>(pObj, pMemberFunc)); } ~signal() { vector<slot<T1>*>::iterator ite = m_slots.begin(); for (; ite != m_slots.end(); ite++) { delete *ite; } } void operator()(T1 para) { vector<slot<T1>*>::iterator ite = m_slots.begin(); //for (; ite != m_slots.end(); ite++) //{ // (*ite)->Execute(para); //} while (ite != m_slots.end()) { (*ite)->Execute(para); ite++; } } private: vector<slot<T1>*> m_slots; }; #define CONNECT(sender,signal,receiver,slot) sender.signal.bind(receiver,slot) class receiver { public: void callback1(int a ) { cout << "receiver1: " << a << endl; } }; class receiver2 { public: void callback2(int a) { cout << "receiver2: " << a << endl; } }; class sender { public: sender() :m_value(0) {}; int get_value() { return m_value; } void set_value(int new_value) { if (new_value != m_value) { m_value = new_value; m_valueChanged(m_value); } } signal<int>m_valueChanged; private: int m_value; }; int main() { receiver r; receiver2 r2; sender s; CONNECT(s, m_valueChanged, &r, &receiver::callback1); CONNECT(s, m_valueChanged,&r2,&receiver2::callback2); s.set_value(1); return 0; }