事件编程(二)

在本文的第一部分(事件编程一),我回答了一个关于用 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 event
list<IPrimeEvents*>::iterator it;
for (it=m_clients.begin(); it!=m_clients.end(); it++) {
 (*it)->OnFoo(/*args*/);
}

时间: 2024-09-09 04:51:43

事件编程(二)的相关文章

Framework 类库的事件编程

编程 本页内容 EventHandler 委托 自定义的事件参数 参数化自定义事件 小结 本月的内容是专门介绍事件编程的系列专栏(共三期)的最后一期.在前两期专栏中,我已经介绍了如何定义和引发事件(请参见 Basic Instincts:Programming with Events Using .NET 和 Basic Instincts:Static Event Binding Using WithEvents).我还解释了如何使用动态和静态事件绑定来绑定事件处理程序.本月,我将通过一些在

事件编程(一)

在微软 .NET 框架中可以定义托管类事件并用委托和 += 操作符处理这些事件.这种机制似乎很有用,那么在本机 C++ 中有没有办法做同样的事情? 确实如此!Visual C++ .NET 具备所谓统一事件模型(Unified Event Model),它可以像托管类一样实现本机事件(用 __event 关键字),但是由于本机事件存在一些不明显的技术问题,而微软的老大不打算解决这些问题,所以他们要我正式奉劝你不要使用它们.那么这是不是就是说 C++ 程序员与事件无缘了呢?当然不是!可以通过别的方

gpu c语言-cuda gpu 编程 二维线程块

问题描述 cuda gpu 编程 二维线程块 这个程序语言用到二维线程块么 什么时候需要? 解决方案 GPU-cuda编程葵花宝典CUDA下的GPU编程--线程和变量CUDA下的GPU编程--线程和变量

JAVA的网络功能与编程 二

编程|网络     五.显示网络上其他HTML文档     利用Java提供的getAppletContext().showDocument(URL)可以 显示其他结点的HTML文档,同前面的显示网络上其他结点的图象, 有两种格式,下面各举一例:                       ●程序8   格式一 import java.applet.*; import java.awt.*; import java.net.*; public class showdoc extends Appl

Windows Socket网络编程(二) 套接字编程原理

一.客户机/服务器模式 在TCP/IP网络中两个进程间的相互作用的主机模式是客户机/服务器模式(Client/Server model).该模式的建立基于以下两点:1.非对等作用:2.通信完全是异步的.客户机/服务器模式在操作过程中采取的是主动请示方式: 首先服务器方要先启动,并根据请示提供相应服务:(过程如下) 1.打开一通信通道并告知本地主机,它愿意在某一个公认地址上接收客户请求. 2.等待客户请求到达该端口. 3.接收到重复服务请求,处理该请求并发送应答信号. 4.返回第二步,等待另一客户

JavaScript FAQ(十五)——鼠标事件(二)

 十二.鼠标事件   2. 左键 vs.右键(Left vs. Right Button) Q:我如何检查用户点击的是右键还是左键? A:click事件只在左键发生,因此onClick事件处理器不用进行左右键测试. 另一方面,mousedown和mouseup事件可能发生在鼠标的任何键上.要确定用户点击的是左键还是右键,可以使用下列事件属性: Netscape Navigator中 event.which Internet Explorer中 event.button 若这些属性的值是1,事件就

转贴:MSDN访谈录之C#编程二

编程 ROBERT HESS:C#被认为是微软的私有语言吗? ANDERS HEJLSBERG:其实并非如此.我们与产业伙伴特别是HP和Intel合作,今年年初,我们向一个叫做ECMA(欧洲计算机制造商协会)的欧洲标准化组织提交了建议,以便标准化C#和CLI. CLI代表通用语言基层体系(Common Language Infrastructure). ROBERT HESS:而这是不是有点类似于C Runtime 和VB Runtime? ANDERS HEJLSBERG:嗯,实际上CLI是.

vb的属性、方法和事件(二)

单击按钮执行操作 使用户与应用程序交互的最简便的方法是给用户提供一个按钮.可以用Visual Basic 提供的 CommandButton 控件,或利用包含图形的 Image 控件,如一个图标,来创建自己的"按钮". 使用 CommandButton大多数 Visual Basic 应用程序都有 CommandButton,使用户通过简单的敲击按钮来执行操作.当用户选中按钮时,不仅会执行相应操作,还会使该按钮看上去象被按下并释放一样.无论何时,只要用户单击按钮,就会调用 Click

GDI+编程(二)使用画笔

画笔常用于绘制图形的轮廓.GDI+的画笔除了具有常见的色彩和宽度属性外,还具有对齐方式,线帽,变换方式等属性.GDI+中通过Pen类来定义画笔对象. (一).构造与使用画笔 Pen(brush, width); //用颜色与线宽构造一个画笔 Pen(color, width); //用画刷与宽度构造一个画笔 例子: Pen pen(Color(255, 0, 0, 0),1);//用第一个构造函数.构造宽度为1的黑色画刷 graphics.DrawLine(&pen, 20, 10, 300,