转贴【http://blog.csdn.net/glock18/archive/2004/07/14/41566.aspx】
面向对象发展到今天,人们已经不满足于对一个事物的描述仅停留在属性和方法上了。事件也作为对象的基本组成部分,被新兴的面向对象语言所采纳。 所谓事件就是一个对象在某个特定的条件下触发通知对象的创建者,由创建者来进行相应的处理。我们可以看到事件的真实处理过程并不被包装在对象里。在java,c#,dephi等语言中都提供了对事件的支持,或者以关键字的形式,或者以类库的形式。而c++中见不到任何关于事件的关键字和类库,难道c++就无法拥有事件这个特性吗?只要动动脑筋就没有难事。现在我们就让c++也来支持事件。
这里仅从语言上考虑,而不使用任何平台相关的特性(消息,事件对象...)。因此我拿出两种实现方法,一种是采用函数指针,另一种采用虚函数。
1. 采用函数指针方式,代码如下:
//定义事件函数的类型
typedef void (*EOnBeforeInvoke)();
typedef void (*EOnAfterInvoke)();
//一个拥有事件的类
class CSomeObject1
{
public:
//定义事件
EOnBeforeInvoke OnBeforeInvoke; //调用前事件
EOnAfterInvoke OnAfterInvoke; //调用后事件
public:
CSomeObject1()
{
OnBeforeInvoke = NULL;
OnAfterInvoke = NULL;
}
void Invoke()
{
//触发OnBeforeInvoke事件
if (OnBeforeInvoke != NULL)
OnBeforeInvoke();
cout << "CSomeObject1::Invoke()" <//触发OnAfterInvoke事件
if (OnAfterInvoke != NULL)
OnAfterInvoke();
}
};//CSomeObject1对象的创建者类
class CEventTest1
{
CSomeObject1 m_objSome;static void OnBeforeInvoke()
{
cout<< "调用前..." < }
static void OnAfterInvoke()
{
cout<< "调用后..." < }
//安装事件
void InstallEvents()
{
m_objSome.OnBeforeInvoke = OnBeforeInvoke;
m_objSome.OnAfterInvoke = OnAfterInvoke;
}
//卸载事件
void UninstallEvents()
{
m_objSome.OnBeforeInvoke = NULL;
m_objSome.OnAfterInvoke = NULL;
}public:
CEventTest1()
{
InstallEvents();
}
~CEventTest1()
{
UninstallEvents();
}void Invoke()
{
m_objSome.Invoke();
}
};
int main(int argc, char* argv[])
{
CEventTest1 test1;
test1.Invoke();
}
记得当年在TurboC下编程时,虽然没有c++,但依然可以写出面向对象的代码,就是利用struct里添加函数指针域的方式,来实现现在c++中的class。如今函数指针依然有用武之地。2. 采用虚拟函数的形式,代码如下:
//事件接口
class __declspec(novtable) ISomeObject2Events
{
friend class CSomeObject2;
protected:
virtual void OnBeforeInvoke();
virtual void OnAfterInvoke();
};//一个拥有事件的类
class CSomeObject2
{
public:
ISomeObject2Events * m_pEvents;
void Invoke()
{
//触发OnBeforeInvoke事件
if (m_pEvents != NULL)
m_pEvents->OnBeforeInvoke();
cout<<"CSomeObject2::Invoke()"<
//触发OnAfterInvoke事件
if (m_pEvents != NULL)
m_pEvents->OnAfterInvoke();
}
};//CSomeObject2对象的创建者类
class CEventTest2 : public ISomeObject2Events
{CSomeObject2 m_objSome;
//调用前事件处理
virtual void OnBeforeInvoke()
{
cout<< "调用前..." < };
//调用后事件处理
virtual void OnAfterInvoke()
{
cout << "调用后..." < };//安装事件
void InstallEvents()
{
m_objSome.m_pEvents = this;
}
//卸载事件
void UninstallEvents()
{
m_objSome.m_pEvents = NULL;
}public:
CEventTest2()
{
InstallEvents();
}
~CEventTest2()
{
UninstallEvents();
}void Invoke()
{
m_objSome.Invoke();
}};
int main(int argc, char* argv[])
{
CEventTest2 test2;
test2.Invoke();
}
这两种方式各有利弊,使用函数指针的好处是触发事件效率高。而采用虚函数的形式的好处是代码更令活更清晰一些。大家可以凭喜好来选择。如果大家看过之后有什么指教或补充,请千万别吝惜你的思想和文字。
本质上,Delphi,C#都是采用函数指针的机制来处理事件的.即将函数指针作为类的一个成员,在某个事件发生时,来调用相应的函数.但在Delphi,C#这样的语言中,事件的处理函数是匿名的,即,一个对象并不知道预订了自己某个事件的函数属于什么类,等等.只是在事件触发时,调用这些方法而已.我也曾经尝试在C++中实现类似的效果,但因为C++本身的语言特性所决定,在C++在要实现这样的机制是很困难的.就像上例中,"事件"只能指向全局函数.成员指针可以指向成员函数,可是成员指针要求你在赋值之前,就要告诉编译器,要指向一个什么类的方法. 这从本质上就限制了在C++中使用"事件"特性.BCB中实现了这样的特性,但这是在编译层做了大量的工作,才实现的.C++语言本身并没有包含这样的特性.其实我很欣赏Delphi中的这种机制,优点很多,比MFC中的什么消息映射不知要好多少倍.