龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 软件开发 > C/C++开发 >

细说C++委托和消息反馈模板(1)(3)

时间:2011-04-12 23:18来源:未知 作者:admin 点击:
分享到:
桥式委托的进一步研究 看过上面的桥式委托之后,可能会有点怀疑他的性能,需要一个interface指针一个functor类/函数指针,调用的时候需要一次查vtable,然

桥式委托的进一步研究

看过上面的桥式委托之后,可能会有点怀疑他的性能,需要一个interface指针一个functor类/函数指针,调用的时候需要一次查vtable,然后再一次做operator()调用。其实,这些消耗都不算很大的,整个桥式委托的类结构是简单的,相对于前面说的继承整个类之类的做法开销还是比较小的,而且又比函数指针通用而且类型安全。最重要的是,刚才的Signal可以方便地改写为Multi-Cast Delegation即一个信号引发多个响应——把Singal内部的DelegationInterface*指针改为一个指针队列就可以了。

不过,我们刚才实现的桥式委托只能接收函数指针和functor,不能接收另外一个类的成员函数,有时候这是非常有用的动作。比如设置一个按钮Button的OnClick事件的响应为一个MsgBox的Show方法。当然,MsgBox还有其他非常多的方法,这样就可以不用局限于把MsgBox当成一个functor了。

我们要改写刚才的整个桥来实现这个功能,在这里需要你对指向成员函数得指针有所了解。

  1. // 新版的桥式委托,可以接收类的成员函数作为响应  
  2. struct DelegationInterface {   
  3. virtual ~DelegationInterface() {};  
  4. virtual void Run() = 0;  
  5. };  
  6.  
  7. templateclass T>  
  8. struct DelegationImpl : public DelegationInterface {  
  9. typedef void (T::* _pF_t)(); // 指向类T成员函数的指针类型  
  10.  
  11. DelegationImpl(T* _PP, _pF_t pF) :_P(_PP), _PF(pF) {}  
  12. virtual void Run() {  
  13. if(_P) { (_P->*_PF)(); } // 成员函数调用,很别扭的写法(_P->*_PF)();  
  14. }  
  15.  
  16. T* _P; // Receiver类  
  17. _pF_t _PF; // 指向Receiver类的某个成员函数  
  18. };  
  19. struct Signal  
  20. {  
  21. DelegationInterface* _PI;  
  22. Signal() :_PI(NULL) {}  
  23. void operator() () { if(_PI) _PI->Run(); }  
  24.  
  25. // 新的ConnectSlot需要指定一个类以及这个类的某个成员函数  
  26. templateclass T>  
  27. void ConnectSlot(T& recv, void (T::* pF)()) { // pF这个参数真够别扭的  
  28. _PI = new DelegationImpl<T>(&recv, pF);  
  29. }  
  30. }; 

注意:ConnectSlot方法的pF参数类型非常复杂,也可以简化如下,即把这个类型检测推到DelegationImpl类去完成,而不在Connect这里进行么?编译器可以正确识别。对于模板来说,很多复杂的参数类型都可以用一个简单的类型代替,不用关心细节,就象上面用一个F代替void (T::*)()。有时候能改善可读性,有时候象反。

  1. templateclass T, class F>  
  2. void ConnectSlot( T& recv, F pF ) {  
  3. PI_ = new DelegationImpl<T>(&recv,pF);  

这个新版怎么用呢,很简单的。比如你的MsgBox类有一个成员函数Show,你可以把这个作为响应函数:

  1. MsgBox box;  
  2. Socket x; // Socket还跟旧的版本一样  
  3. x.OnRecv.ConnectSlot(box, &MsgBox::Show); 

注意上面这里引用成员函数指针的写法,一定不能写成box.Show,呵呵,希望你还记得成员函数是属于类公共的东西,不是某个实例的私有产品。大家不妨进一步动一下脑筋,把新版的Signal和旧版的Signal结合一下,你就可以获得一个功能超强的Delegation系统了。

点评:用signal的办法确实可以方便地动态替换处理函数,不过这是以每个可能被处理的消息都要在每个对象中占用一个 signal 的空间为代价的。而且,需要动态改变处理函数的应用我已经不记得什么时候见过了。即使有,也可以通过在override的virtual函数里自己处理实现,虽说麻烦,但也是可能的。此外,以上代码并不够规范,下划线加大写字母开头的标识符是保留给语言的实现用的。


精彩图集

赞助商链接