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 会需要一个实现 operator()(T) 的仿函数(functor)。它调用该集合中
T 对象的这个操作符。所以这里函数 operator 有一个 IPrimeEvents 指针参数并返回 void —— 因为客户机列表是一个 IPrimeEvents
指针列表。为了传递附加参数,构造函数将它们保存在数据成员里。NotifyProgress(nFound) 调用构造函数以创建一个用 m_nFound=nFound
初始化的堆栈实例。所以,任何触发 Foo 事件的 Foo 仿函数的一般模式是这样的:
class NotifyFoo {
protected:
ARG1 m_arg1; // whatever, as many as needed
public:
NotifyProgress(ARG1 a1, ...) : m_arg1(a1) { }
void operator()(IMyEvents* obj)
{
obj->OnFoo(m_arg1, ...);
}
};
构造函数将事件参数作为数据成员来保存,函数 operator 将它们传递到对象事件处理函数。对于所有仿函数来说,最终结果是——将函数 OnFoo 转换为类 NotifyFoo。这样做为什么会有用呢?因为我能编写一个模板。在我开始做之前,有一件事我必须得提一下。那就是你必须从一个叫 unary_function
的 STL 类派生你的仿函数类:
class NotifyProgress :
public unary_function
{
.
. // as before
.
};
也就是说,NotifyProgress 是一个一元函数,其函数 operator 带一个参数,IPrimeEvents 指针并返回
void。该一元函数使你的仿函数类“可适配”,使你能将它与 STL 适配器,如:not1、bind2nd
等等进行结合。但是即使你从来都没有打算使用适配器,就像我的事件处理例程,unary_function
仍然不失为一个好主意,因为它向这个世界宣告:“这是一个函数类。”它是一种将代码文档化的方式。有关适配器的详细讨论,参见 Effective STL:50
Specific Ways to Improve Your Use of the Standard Template Library
(Addison-Wesley, 2001) by Scott Meyers
STL 的高手们也许会问:为什么我不使用 mem_fun 适配器直接将 IPrimeEvents::OnProgress
转换为函数对象。因为 OnProgress 是虚拟函数,我不能适配一个虚拟函数。如果这样做,要触及到基类。如果你使用 Boost
库,可以用其捆绑适配器直接将 OnFoo
这样的虚拟事件处理器转换为仿函数,不用编写仿函数。如果你不明白我所讲的这些内容,不用害怕,不看这些内容好了。
当然,我还需要一个 Done 事件的 NotifyDone。由于 Done 没有参数,构造函数也没有:
class NotifyDone : public unary_function
{
public:
NotifyDone() { }
void operator()(IPrimeEvents* obj)
{
obj->OnDone();
}
};
现在我有了自己的仿函数类,我可以用 for_each
代替手工迭代客户机列表。可我把它们放在哪呢?仿函数属于与事件说明有关的范畴,所以我把它们放在 IPrimeEvents
接口中,用嵌套类的形式。代码如 Figure 2 所示。细心的读者会注意到我在两个地方还做了细小的恶修改。仿函数的命名没有用
NotifyProgress,而是叫做
Progress。稍后你会明白这样做使代码更易读;还有就是我没有把事件处理器都声明为纯虚拟函数,而是将它们定义为空实现。IPrimeEvents
只有两个事件,但对于一般的事件机制来说,如果程序员感兴趣的的处理并不多,但要让他们实现每一个事件处理器似乎不是很友好。所以这里每个处理器默认实现什么也不做。为了使基类抽象化,我声明了一个纯虚拟析构函数。当你想抽象化一个没有任何纯虚函数的基类时,这是一个标准的C 技巧。唯一的要做的是你必须定义一个析构函数。纯虚拟函数没有定义——除非它是析构函数。既然每一个派生类的析构都调用其基类的析构函数,那么基类需要一个实现,即便它是纯虚拟的: 本文章更多内容:<<上一页 - 1 - 2 - 3 - 4 - 下一页>> |