WEB开发网
开发学院软件开发VC 事件编程(二) 阅读

事件编程(二)

 2007-03-15 21:54:00 来源:WEB开发网   
核心提示: 如果模板使你伤脑筋,我就再讲清楚一些吧,事件编程(二)(6),CEventMgr 是一个参数化的模板类,其参数是事件接口 I,对于事件来说,通过指针仍然只有一个函数调用,因此 CEventMgr<IPrimeEvents> 根据 IPrimeEvents 实例化一个事件管理器,

如果模板使你伤脑筋,我就再讲清楚一些吧。CEventMgr 是一个参数化的模板类,其参数是事件接口 I。因此 CEventMgr<IPrimeEvents> 根据 IPrimeEvents 实例化一个事件管理器。它保存数据成员 m_clients,该成员是一个 IPrimeEvents 指针列表:list<IPrimeEvents*>。CEventMgr 中是一个模板成员函数:Raise<F>,它将仿函数参数 F 传递给 for_each。所以当你写下面这条语句时:

m_eventmgr.Raise(IPrimeEvents::Progress(nFound));

编译器明白你试图以 IPrimeEvents::Progress 类型参数调用 CEventMgr::Raise,于是它用模板产生成员函数 CEventMgr::Raise(IPrimeEvents::Progress)。实现代码将仿函数实例传递给 for_each,它为客户机列表中的每个 I* 对象调用仿函数的 operator()。仿函数调用对象的 OnProgress 处理例程——这就是我想要的!模板不是很酷吗?

我们已经快到终点了。仿函数让我参数化事件方法并使用 for_each,但它们还是太长,我讨厌敲入太多的东西。所以最后一步是引入一些宏来解决这个问题。下面就是 IPrimeEvents 最终的浓缩定义。

class IPrimeEvents {
 DECLARE_EVENTS(IPrimeEvents);
public:
 DEFINE_EVENT1(IPrimeEvents, Progress, UINT /*nFound*/);
 DEFINE_EVENT0(IPrimeEvents, Done);
};
IMPLEMENT_EVENTS(IPrimeEvents);  
  完整的源代码参见 Figure 4 —— 从代码中你可以体会到我竭尽全力进行精简和浓缩。只留下最基本的信息:每个事件处理器的名字和签名。宏假设 Foo 的事件处理器是 OnFoo。一些编程的唯美主义者不喜欢宏,但我不那样。有工具为什么不使用呢?DECLARE_EVENTS 声明构造函数和析构函数;IMPLEMENT_EVENTS 实现内联析构。宏 DEFINE_EVENT0,DEFINE_EVENT1 以及 DEFINE_EVENT2 分别声明和定义了 OnFoo 事件处理器以及不带参数,带一个参数和带两个参数的 Foo 事件仿函数。如果你需要更多的参数,可以定义一个结构,用一个事件参数来传递此结构的指针:MumbleArgs args;
args.a = 1;
args.b = 2;
// etc.
m_eventMgr.Raise(IMyEvents::Mumble(&args));

还有一种选择,你可以实现 DEFINE_EVENT3。但是记住:仿函数对象通过值传递的,所以它们应该很小。当可以传递指针时,为什么要在堆区和栈区来回拷贝一大堆参数呢?如果事件处理器需要返回值,也可以借助结构。为了简单起见,我让事件处理器返回 void。

经常有程序员会问仿函数会不会带来太大的额外开销。事实上,仿函数通常比函数更有效率。理由是它是内联的。当你在 C++ 中传递指向函数的指针时,即使你将函数定义为内联,它就是一个指针。你不能通过传值的方式来传递一个函数。但是当你传递一个对象实例到某个模板函数时,如果你象那样定义函数,编译器产生的所有东西都是内联的。对于事件来说,通过指针仍然只有一个函数调用,它发生在函数 operator 调用虚拟 OnFoo 处理器的时候。

上一页  1 2 3 4 5 6 

Tags:事件 编程

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接