inline IPrimeEvents::~IPrimeEvents() { }
有了我的仿函数定义,CPrimeCalculator 是这样触发 Progress 事件的:
// in CPrimeCalculator:
void NotifyProgress(UINT nFound)
{
for_each(m_clients.begin(), m_clients.end(),
IPrimeEvents::Progress(nFound));
}
到这里,我已经介绍了仿函数类 Progress 和 Done,同时,NotifyProgress 和 NotifyDone 都能用 STL
的 for_each 算法。下一步该做什么?记住,我的目的是完全摆脱 NotifyFoo
函数——或者说得更具体一点,就是把它们实现为模板,以便程序员在创建事件时不必为他们定义的每个事件编写千篇一律的函数。将 for 循环转化为
for_each 算法只是万里长征的第一步。 逆风编程精品
通过将虚拟成员函数 OnFoo 转换为 Foo 仿函数类型,从而为模板化创造条件。(仿函数在这里有点像 .NET
中的委托。)现在我的通知函数根据类型的不同而不同,替代了函数名,我可以将它们参数化。这样一来,我便可以将整个事件实现移出 CPrimeCalculator
,把它们放入新的模板类 CEventMgr 中,这是一个完全通用的类。如 Figure 3 所示。CEventMgr<I> 保存 I*
指针列表。它具备 Register 和 Unregister 方法以便添加元素和从其列表中删除元素,此外它还有一个模板成员函数
Raise 用于触发事件:
template
class CEventMgr
{
...
template
void Raise(F fn)
{
for_each(m_clients.begin(), m_clients.end(), fn);
}
};
很难相信,平时几乎碰不到的模板套模板的情况?在此处派上用场了。现在触发事件我们可以这样做:void NotifyProgress(UINT nFound)
{
m_eventmgr.Raise(IPrimeEvents::Progress(nFound));
}
没有 for 循环,甚至都没有 for_each,所有细节都被封装在 CEventMgr
之中,事件的触发使用一行代码。我甚至可以完全省略掉 NotifyProgress,每当想要触发事件时仅仅调用 CEventMgr::Raise
即可——然而,好的编码规范促使我宁愿将 Raise 封装在某个函数中,以防万一我要修改 CEventMgr 或将事件触发函数暴露给客户机。既然 NotifyProgress
是内联函数,就不会有幸能丢失。
如果模板使你伤脑筋,我就再讲清楚一些吧。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
事件仿函数。如果你需要更多的参数,可以定义一个结构,用一个事件参数来传递此结构的指针: 本文章更多内容:<<上一页 - 1 - 2 - 3 - 4 - 下一页>> |