事件编程(二)

画风子

画风子

2016-01-29 12:28

事件编程(二),事件编程(二)

C++ At Work 专栏...
事件编程(二)

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/cyuyanjiaocheng/)

原著:Paul DiLascia
翻译:NorthTibet

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/cyuyanjiaocheng/)

下载源代码:CAtWork0603.exe (3,178KB)
原文出处:Event Programming, Part 2


  在本文的第一部分(事件编程一),我回答了一个关于用 C++ 实现本机事件的问题。讨论了一般意义上的事件并示范了如何用接口为你的类定义事件处理器,事件的处理必须在客户机实现。我的实现有一些缺陷,我承诺过最终要解决掉,本文就来完成这件事情。
  在开始之前,先简单回顾一下前面写的那个程序,PrimeCalc。如 Figure 1 所示:


Figure 1 计算素数

  程序中使用了一个计算素数的类 CPrimeCalculator,这个类发起两个事件:Progress 和 Done。当搜索到素数时,该类触发 Progress 事件以报告目前发现了多少素数。完成处理后触发 Done 事件。这两个事件都是由接口 IPrimeEvents 定义的:

class IPrimeEvents {public:  virtual void OnProgress(UINT nPrimes) = 0;  virtual void OnDone() = 0;}; 
  客户机要想处理事件必须得从 IPrimeEvents 派生,实现事件处理函数,并调用 CPrimeCalculator::Register 来注册其接口。CPrimeCalculator::Register 会将客户机对象/接口添加到其内部列表。当触发了一个 Progress 事件时,CPrimeCalculator 便调用辅助函数 NotifyProgress:
void CPrimeCalculator::NotifyProgress(UINT nFound){  list::iterator it;  for (it=m_clients.begin(); it!=m_clients.end(); it++) {    (*it)->OnProgress(nFound);  }} 
  NotifyProgress 遍历客户机列表,调用每个客户机的 OnProgress 处理函数。当某个程序员使用 CPrimeCalculator 时,编写事件处理代码很容易——只要从 IPrimeEvents 派生并实现处理器即可。但是在实现这种触发事件的 CPrimeCalculator 类机制时冗长乏味。你必须得为每个事件(如 Foo)实现诸如 NotifyFoo 这样的函数,即使处理模式一模一样。事件触发代码被划分在两个类中,事件接口 IPrimeEvents 和 事件源 CPrimeCalculator。如果你想将同样的事件接口用于不同的事件源那该怎么办?IPrimeEvents 是很通用,我可能将它改名为 IProgressEvents 并将它用于任何以整数形式报告处理进度的类并在完成处理时用 Done。但每个触发 Progess 事件的类必须重新实现触发事件的通知函数。理想情况下,所有事件代码都应该放在单个类中。
  既然通知函数在本文中是一种实验模型,那么自然会问这样的问题:它们有没有某种通用的实现方法?我能将整个事件机制封装到单个的类、模板或宏,或者任何事件源能使用的其它什么类型中吗?答案是肯定中的肯定。我将示范如何创建一个使用宏和模板的事件系统,以便将事件处理的代码量降至最低限度。我们的旅程需要借助一些高境界的 C++ 操作,比如嵌套模板以及仿函数类(functor class)。
  我将分几个步骤实现这个系统。目的是编写一个实现通知函数 NotifyProgress 以及 NotifyDone 的模板。每个函数都具备相似而又不完全一样的模型:
// NotifyFoo — raise Foo eventlist<IPrimeEvents*::iterator it;for (it=m_clients.begin(); it!=m_clients.end(); it++) {  (*it)-OnFoo(/*args*/);} 
  也就是说迭代客户机列表,并针对每个客户机调用 OnFoo,传递事件参数。如何把它写成一个模板呢?可以将接口 IPrimeEvents 参数化为一个类型 T,但如何参数化事件处理函数 OnFoo,程序员可能选择的任何名字和签名。
  任何时候你参数化某个函数时,都应该考虑:仿函数,也叫做 functor。仿函数是 C++ 语言中将函数转换为类的一种机制,它代替了给回调函数传递指针的做法,而是传递仿函数类的实例。在标准模板库 STL 中包含有丰富的 Functor,并实现了一些使用 functor 的算法,尤其是 for_each 算法,在本文中很有用:
for_each(m_clients.begin(), m_clients.end(),NotifyProgress(nFound));

  for_each 算法从头到尾迭代容器元素,并对每个元素调用函数对象 NotifyProgress。这里说的“函数对象”到底是指的什么呢?不是一个函数,它是一个对象。这个类看起来像下面这个样子:

class NotifyProgress {protected:  UINT m_nFound;public:  NotifyProgress(UINT n) : nFound(n) { }  void operator()(IPrimeEvents* obj)  {    obj->OnProgress(nFound);  }};
  NotifyProgress 实现函数 operator()(IPrimeEvents*),它是 for_each 算法需要的东西。一般来讲,如果你具备一个类型为 T 对象集合,for_each 会需要一个
展开更多 50%)
分享

猜你喜欢

事件编程(二)

C语言教程 C语言函数
事件编程(二)

事件编程(一)

C语言教程 C语言函数
事件编程(一)

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

SQL编程(二)

Delphi
SQL编程(二)

自己编程模拟 MouseEnter,MouseExit 事件

编程语言 网络编程
自己编程模拟 MouseEnter,MouseExit 事件

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

JScript面向事件驱动的编程

Web开发
JScript面向事件驱动的编程

Visual C#事件与接口编程实例

编程语言 网络编程
Visual C#事件与接口编程实例

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

《闪之轨迹2》一周目通关心得及各主角评价

《闪之轨迹2》一周目通关心得及各主角评价

《四大萌捕》晋级的土豪攻略

《四大萌捕》晋级的土豪攻略
下拉加载更多内容 ↓