在
Java
、
C
#等语言或者说其类库中,都实现了事件模型。而
c++
语言本身并没有定义事件机制,并且在目前众多优秀的
c++
类库,包括
STL
、
Boost
等都没有实现类似的事件机制。当我们被
MFC
的消息搞得头昏眼花之时,是否有冲动自己去实现一个简单的事件模型呢。我想,有着相同想法的人肯定很多,而真正动手来写可能会碰到各种各样的困难。下面就让我们一步步来编写一个简单的事件模型。
一、
了解事件模型的机制
在开始之前,我们有必要简单的了解一下事件模型的机制。事实上,事件模型的机制不止一种,
Java
和
c#
的事件机制就不太一样,不过事件模型的基础都是一样,那就是一般都使用
Observer
设计模式。关于
Observer
设计模式,希望详细了解的朋友可以参考《设计模式》一书,在这里我们就不详细的介绍,只是参照
c#
的事件机制来实现。
在
c#
中,我们可以以
event
关键字声明一个事件,然后我们可以将一个函数委托挂接在这个事件上,当事件被调用时,则所有挂接的函数也会被调用。这里有一个比较难以理解的词大概是委托,其实也不难理解。委托其实就是一种函数包装类,这种类可以把任何函数的指针保存起来,等到需要调用的时候再调用,并且这种类的实例必须能够挂接到事件之中。
看了上面这段话,是不是觉得其实事件模型也并不复杂。还想对事件模型了解得更多?我这里就不继续了,感兴趣的朋友可以查找一下相关的资料。下面我们就开始在
c++
中编写一个简单的事件模型。
二、
设计一个简单的
c++
事件模型
因为一个事件模型其实就是一个典型的
Observer
设计模式,因此最重要的就是
Subject(
目标类
)
和
Observer(
观察者类
)
的设计。
首先,我们需要一个事件类,它其实是一个抽象的
Subject
。它是一个基类,所有的其他事件都从它继承,并且用户只需要从这个基类继承就可以自定义事件。我们不仿将这个类定义为
CEvent
。
CEvent
的声明如下:
class
CEvent
{
public
:
typedef
list
<
CEventHandler
>
data_type
;
CEvent
();
virtual
~
CEvent
();
void
operator
()()
{
data_type
::
iterator
it
;
for
(
it
=
m_observer
.
begin
();
it
!=
m_observer
.
end
(); ++
it
)
{
(*
it
)(*
this
);
}
}
CEvent
&
operator
+= (
const
CEventHandler
&
handler
)
{
Register
(
handler
);
return
*
this
;
}
CEvent
&
operator
-= (
const
CEventHandler
&
handler
)
{
UnRegister
(
handler
);
return
*
this
;
}
void
Register
(
const
CEventHandler
&
handler
)
{
m_observer
.
push_back
(
handler
);
}
void
UnRegister
(
const
CEventHandler
&
handler
)
{
m_observer
.
remove
(
handler
);
}
protected
:
//
将侦听的所有函数或者仿函数的集合起来
data_type
m_observer
;
};
其次,这个事件可以挂接任意的函数,也就是它可以保存所有的挂接函数。在
c++
中,我们大致有三种类型的函数,全局或者静态函数、成员函数、仿函数。要在
CEvent
中实现一个或者多个方法来执行挂接的任务不太现实。我们参考
c#
的实现方法,可以先实现一个委托类,将各种函数保存起来。然后再使用
CEvent
对这个委托类进行挂接。我们不仿称这个类为
CEventHandler
。
然后,我们的这个委托类需要能够保存所有的函数。在
c++
里,我们能够很容易的得到函数的指针,只需要在函数名前加
&
,但是各种函数,特别是成员函数它是没有类型的,这就要有一种机制,将所有的函数转换为同一种类型。因为函数是没有类型,也相当于每一个函数都是一种类型,解决这个问题的方法其实就是使用模板类。因此,我们先定义一个虚基类
CFunImpl
。对三种不同的函数,我们分别从
CfunImpl
继承三种不同的子类,
CstaticFun
、
CmemFun,CFunctor
。
CstaticFun
表示全局的或者静态的函数,不过这必须是一个模板类,对于每一个静态或者全局函数,都是模板类的一个特化。类声明的代码如下:
template
<
typename
Fun
>
class
CStaticFun
:
public
CFunImpl
{
public
:
CStaticFun
(
const
Fun
&
fun
) :
m_fun
(
fun
){};
virtual
~
CStaticFun
()
{
}
protected
:
Fun
m_fun
;
};
对于
CmemFun
类,我们如法炮制,不过我们这时需要保存两个成员变量,一个是成员函数的指针,另一个则是该成员函数所属的对象的指针。类声明的代码如下:
template
<
typename
PointerToObj
,
typename
PointerToMemFun
>
class
CMemFun
:
public
CFunImpl
{
public
:
CMemFun
(
const
PointerToObj
&
pObj
,
PointerToMemFun
pMemFn
)
:
m_pObj
(
pObj
),
m_pMemFun
(
pMemFn
)
{
}
virtual
~
CMemFun
()
{
}
protected
:
PointerToObj
m_pObj
;
PointerToMemFun
m_pMemFun
;
};
对于仿函数,我们这里先不讲,因为仿函数使用得很少,并且这还涉及到仿函数的概念,有兴趣的朋友可以在阅读完本文之后自己来实现。
好,到现在为止,我们可以将所有的函数使用
CfunImpl
来表示了。因此,我们可以在委托类
CEventHandler
中保存
CfunImpl*
来达到我们的目的。
CEventHandler
的声明如下:
class
CEventHandler
{
public
:
virtual
~
CEventHandler
()
{
Clear
();
}
template
<
class
Fun
>
CEventHandler
(
const
Fun
&
fun
)
:
m_pImpl
(
new
CStaticFun
<
Fun
>(
fun
))
{}
CEventHandler
(
const
CEventHandler
&
fun
)
:
m_pImpl
(
NULL
)
{
*
this
=
fun
;
}
void
Clear
()
{
if
(
m_pImpl
)
{
delete
m_pImpl
;
m_pImpl
=
NULL
;
}
}
CEventHandler
&
operator
= (
const
CEventHandler
&
fun
)
{
Clear
();
if
(
fun
.
m_pImpl
)
{
m_pImpl
=
fun
.
m_pImpl
->
Clone
();
}
return
*
this
;
}
//
成员函数
template
<
typename
PointerToObj
,
typename
PointerToMemFun
>
CEventHandler
(
const
PointerToObj
&
pObj
,
const
PointerToMemFun
&
pMemFun
)
:
m_pImpl
(
new
CMemFun
<
PointerToObj
,
PointerToMemFun
>(
pObj
,
pMemFun
))
{}
void
operator
()(
CEvent
&
e
)
{
if
(
m_pImpl
)
{
(*
m_pImpl
)(
e
);
}
}
bool
operator
== (
const
CEventHandler
&
handler
)
{
if
(
m_pImpl
==
NULL
||
handler
.
m_pImpl
==
NULL
)
{
return
true
;
}
if
(
typeid
(
m_pImpl
) ==
typeid
(
handler
.
m_pImpl
))
{
return
(*
m_pImpl
) == (*(
handler
.
m_pImpl
));
}
return
false
;
}
protected
:
CFunImpl
*
m_pImpl
;
};
不过要实现事件机制,我们还需要
CfunImpl*
的子类必须实现几个方法。
第一个方法就是调用函数的方法,我们这里要求重载
void operator()(CEvent& e);
来达到目的。
第二个方法就是
operator==
,为什么需要这个方法呢。因为对于
CEvent
来说,调用
UnRegister()
时,我们必须找到我们使用
Register()