事件编程(一)
2007-03-15 21:53:34 来源:WEB开发网// in CPrimeCalculator::FindPrimes
for (UINT p=2; p<max; p++) {
// figure out if p is prime
if (/* every now and then */)
NotifyProgress(GetNumberOfPrimes());
...
}
NotifyDone();
CPrimeCalculator 调用内部辅助函数 NotifyProgress 和 NotifyDone 来触发事件。这些函数遍历客户机对象列表,为每个客户机调用相应的事件处理器。代码如下:
void CPrimeCalculator::NotifyProgress(UINT nFound)
{
list<IPrimeEvents*>::iterator it;
for (it=m_clients.begin(); it!=m_clients.end(); it++) {
(*it)->OnProgress(nFound);
}
}
如果你对 STL 不熟悉,去看看有关迭代器反引用操作符的内容,它返回当前指向的对象,上面代码段中,for 循环里的代码等同于:
IPrimeEvents* obj = *it;
obj->OnProgress(nFound);
触发 Done 事件的 NotifyDone 函数做法类似,它没有参数,如 Figure 3 所示。你也许觉得 Done 事件是多余的,因为当 FindPrimes 返回控制时,客户机已经知道 CPrimeCalculator 完成了工作。没错——但有一种情况除外,那就是多个客户机注册接收的事件,并且调用 CPrimeCalculator::FindPrimes 的对象可能不是同一个。Figure 4 是我的测试程序 PrimeCalc。该程序为素数事件实现了两个不同的事件处理器。第一个处理器是主对话框本身,CMyDlg,它利用多继承实现 IPrimeEvents。该对话框处理 OnProgress 和 OnDone,并在对话窗口显示进度,完成后发出蜂鸣声。其它的事件处理器,如 CTracePrimeEvents 也实现了 IPrimeEvents,这个实现显示诊断(TRACE)流中的信息。如 Figure 6 所示,在我的 TraceWin 程序(参见 2004 年三月的专栏)中显示的范例输出。我写的 CTracePrimeEvents 展示了多个客户机如何注册相同的事件。
Figure 5 运行中的 PrimeCalc
从使用 CPrimeCalculator 来编写应用的程序员角度看,处理事件简单而直白。从 IPrimeEvents 派生,实现处理器函数,然后调用 Register。从编写触发事件的类的程序员看来,这个过程有些冗长乏味。首先你得定义事件接口。这并没有什么不好。但接着你得编写 Register 和 Unregister 函数,每个 Foo 事件都得有一个相应的 NotifyFoo 函数。如果有 15 个事件的话,那就十分令人不爽了,尤其是每个 NotifyFoo 函数的模式都相同:
void CMyClass::NotifyFoo(/* args */)
{
list<IPrimeEvents*>::iterator it;
for (it=m_clients.begin(); it!=m_clients.end(); it++) {
(*it)->OnFoo(/* args */);
}
}
Figure 6 PrimeCalc 在 TraceWin 中的输出
NotifyFoo 迭代客户机列表,为每个注册的客户机调用相应的 OnFoo 处理器,并传递任何需要的参数。有没有什么方法实现这个一般过程,比如用宏或者模板来封装这种繁琐而固定的样板代码,将自己从重复性劳动中解放出来呢?实际上是有的。下个月的专栏文章我们将讨论这个问题。记住在同一时间,同一频道,咱们再见——顺祝编程愉快!
更多精彩
赞助商链接