当前位置导航:炫浪网>>网络学院>>编程开发>>C++教程>>C++基础入门教程

C++实现委托和消息反馈模板

摘要:本文简单介绍并比较了用于实现消息反馈的几种常见技术,其中详细介绍了利用C++模板技术来实现类型安全的委托的要点和限制,可以作为理解qt/gtk+等UI库的信号反馈机制的入门文章。

    疑问:很多时候这样做实在很烦,特别是做小程序的时候,或者需要快速做原型的时候,一眼望去小小的程序一上来就继承了一大堆东西,颇为不爽。只是想着能省事一点,希望能像那些脚本语言一样快速绑定消息响应,而不是以继承开始工作——我已经害怕看到长长的类继承树了,很多时候根本不必要继承整个类;又或者某些类只提供一个接口而不是具体的类又或者需要多重继承,处理都有一定麻烦;最麻烦的莫过于有时候需要改变响应处理,难道继承好几个下来么——这么多虚表也是浪费啊。

  点评:为了使用Socket就必须继承Socket,这可以说是Socket的设计的问题。如果需要实现类似的功能的话,可以写成如下,虽然和继承 Socket 没有多少本质的差别,不过确实把消息处理类和Socket的实现扯开了。:
 strUCt Socket { // base class
virtual void OnRecv();
};
stuct MySocket { // your event-handle class
virtual void OnRecv() { /* do sth here ... */ }
}

    疑问:很多时候这样做实在很烦,特别是做小程序的时候,或者需要快速做原型的时候,一眼望去小小的程序一上来就继承了一大堆东西,颇为不爽。只是想着能省事一点,希望能像那些脚本语言一样快速绑定消息响应,而不是以继承开始工作——我已经害怕看到长长的类继承树了,很多时候根本不必要继承整个类;又或者某些类只提供一个接口而不是具体的类又或者需要多重继承,处理都有一定麻烦;最麻烦的莫过于有时候需要改变响应处理,难道继承好几个下来么——这么多虚表也是浪费啊。

  点评:为了使用Socket就必须继承Socket,这可以说是Socket的设计的问题。如果需要实现类似的功能的话,可以写成如下,虽然和继承 Socket 没有多少本质的差别,不过确实把消息处理类和Socket的实现扯开了。

 struct SocketEventHandler {
virtual void OnRecv() { /* ... */ }
virtual void OnSend() { /* ... */ }
};
struct Socket {
void set_handler( SocketEventHandler* h ) { handler_ = h; }
private:
SocketEventHandler* handler_;
};
struct MyHandler : SocketEventHandler {
void OnRecv() { ... }
};
Socket s;
MyHandler h;
s.set_handler( &h );

    突然之间,我感到一阵迷茫,非常渴望一种简单明确的表达方法。丢开继承,我们还有什么把戏?我不禁想起了c时代的回调函数……

  回调函数(CallBack)

  非常简单,就是一个函数指针。刚才的OnRecv可以写成这样
 struct Socket {
void OnRecv() { if(OnRecvHandle!=NULL) OnRecvHandle(); }
void (*OnRecvHandle) ();
};
    客户程序只需要编写一个MyOnRecv函数,并且赋值给OnRecvHandle就可以了
 void MyOnRecv(); // your event-handle function
Socket foo;
foo.OnRecvHandle = MyOnRecv;
    疑问:非常简单,不需要继承类就可以处理,而且随时可以替换不同的处理函数。其实多态的本质也是函数指针,只不过多态是用vtable统一管理函数指针。回调函数要特别注意函数指针是否为空的问题,因此最好外面在包装一层判断过程。回调函数最大问题在于类型不安全,显式指针这东西……不说也罢……翻了一下智能指针和模版,我发现了一根稻草…… 委托(Delegation)

  委托是什么呢?这个名词似乎是时尚的代名词,我仿佛看到学Java/c#的兄弟们在嘲笑我们的落后……其实,property不也可以算是一种委托吗?说白了不就是智能指针么?

    我觉得委托最本质的是提供一种类型安全的动态消息响应转移机制。

  以前,我对委托一无所知,我觉得无非就是一个类型安全的智能指针,而所谓的Multi-Cast Delegation无非就是一个智能指针数祖……是不是还有Any-Cast Delegation呢?我不知道,也许有吧,无非就是智能指针数祖+随机数发生器……

  但是,实际上并不是那么简单。你可以把我刚才说的函数指针封装一下弄一个类封装起来,不过,这直接导致某个消息的响应只能是固定死的函数指针类型,甚至不能是可爱的Functor或者是某个类的成员函数。你可能会跟我抬杠说这怎么可能,不是可以用template实现么?我们来看一个例子

  假设某个委托类 Dummy_Delegation 拥有一个成员函数用来连接处理函数 template<class T> void Dummy_Delegation::Connect(T _F); 没错,_F可以不一定函数指针,也可以是Functor,我们利用_F()来呼叫响应函数,一切看起来是多么美好——但是,很不幸,这个_F无法保存下来供消息产生的时候呼叫……

  一切都因为这个该死的template<class T>,你无法在Dummy_Delegation内定义一个T类型的变量或者指针来保存_F。退一万步说,你把T作为整个Dummy的模版,还是避免不了在模版实例化的时候定死类型。于是,整个Delegation的通用性大打折扣……

  实际上,我们希望有这么一种Delegation,他可以把消息响应动态绑定到任何一个类的成员函数上只要函数类型一致。注意,这里说的是任何一个类。这就要求我们屏蔽信号发生器和响应类之间的耦合关系,即,让他们相互都不知道对方是谁甚至不知道对方的类型信息。

  这个方法可行么?Yes!

  桥式委托(Bridge Delegation) ---- 利用泛型+多态来实现

  请允许我杜撰一个名词:桥式委托(Bridge Delegation)

  实现这么一个东西真的很有意思,其实,像gtk+/qt很多需要"信号/反馈"(signal/slot)的系统都是这么实现的。

  说到GP和Template,那真的可以算是百家争鸣了,就像boost和loki还在争夺新的C++标准智能指针的地位打得不可开交。而Functor这个东西有是很多GP algo的基础,比如sort/for_each等等。

  整个桥式委托的结构如下图:

    Signal <>-------->* Interface
    ^

    Implementation<Receiver> -------------> Receiver

  我们搭建了一个Interface/Implementation的桥用来连接Singal和Receiver,这样就可以有效隔开双方的直接耦合。用之前我们的Socket类来演示如下:

共4页 首页 上一页 1 2 3 4 下一页 尾页 跳转到
相关内容
赞助商链接