C++事件机制(上篇)-- Observer模式
简介:
本文通过上中下三篇文章来分析c++事件机制的各个方面,并实作出一个c++的事件库。
用过C#的朋友,一定对于C#的事件机制感觉很爽,来我们看一看一个C#的例子。
class Test
{
public delegate void ClickHandle(int X,int Y);
public event ClickHandle OnClickEvent;
public void OnClick()
{
OnClickEvent(3,4);
}
}
在使用时用
Test test = new Test();
test.OnClickEvent+=new Test.ClickHandle(test_OnClickEvent);
就完成了事件的注册,非常之方便简洁。C#的事件基础是委托,并在此之上形成了多播委托。
那么C++能否做到呢?C++社团的理念是首先C++一定能做到,如果C++没有提供,那么动手做一个,最后是很重要的一条尽量的节省成本或者表述为"不为不需要的东西付出成本"。
我想看这篇文章的很多朋友已是C++的老手,相信对于C#的这种用法是羡慕良久,在工作上可能也使用过网上的解决方案,比如在CodeProject上就有不少该类实现,比较有名的我想是以下几篇
Don Clugston的大作,见 http://www.codeproject.com/KB/cpp/FastDelegate.aspx;
JaeWook Choi的大作,见 http://www.codeproject.com/KB/cpp/fd.aspx;
Sergey Ryazanov的大作,见 http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx;
他们各自采用了不同的方法,有的利用“hack方式”,有的利用了标准c++中不起眼的角落的规则,这些不过都还没达到我心中完美的c++事件实现,或者说是各有千秋但均不完善。他们的方法我会在下篇文章中进行介绍。当然,你可以等待C++1X的标准中包含delegate和event关键字,不过可能性不是很大,因为C++老大很反感增加关键字,因为他说必需保证要与上千万行已存在的代码兼容。
C++从来不会让我们失望。不过溯本正源,我们首先来看一看C++中传统的“事件”方式是如何实现的,这能让我们了解事件的本质,以及事件机制的成本,也可以把它作为我们事件方式成本的考量。GOF中提出的23种设计模式中的Observer模式就是来实现“事件模型",看看这种模式是干什么的定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。这就是事件机制的精髓,举个简单例子,张三结婚了,他会给每个同事发一个请柬告诉大家“XXXX年XX月XX日,我结婚,有红包的赶快拿来!”,于是各位同仁不得不从微薄的薪水中挤出几张红票子给张三。这就是事件。其实从某种意思上讲windows的消息机制也是一种事件机制,不过它是系统角度实现的,而非语言层面的。费话少说有,我们首先来看一看Observer的图例,老手自然不用我多说,自然早已山水了于胸了,新手么,我建议你看看GOF的书。
下面是一个subject的具体实现
class Subject {
public:
virtual ~Subject();
virtual void Attach(Observer*);
virtual void Detach(Observer*);
virtual void Notify();
protected:
Subject();
private:
list<Observer*> *_observers;
};
当我们使用时可以用
ConcreteSubjectObj.Attach(&ConcreteObserverObj1);
ConcreteSubjectObj.Attach(&ConcreteObserverObj2);
这样就把ConcreteObserverObj1,ConcreteObserverObj2置于我们的事件通知链中,当ConcreteSubjectObj调用Notify()时,便会使用 list中的每个对象进行相应的操作。于是就完成了我们的事件模型。这种方式没有用到模板之类的东东,因此无论是初涉C++的朋友,还是C++的老手应该都容易了解,只是它的问题是过于繁琐,写一个类的事件尚易,写很多个类的事件则非常的麻烦,而且使用了C++中比较恶心的多继承,,语法上也是非常地不直观不KISS。
但是它也并非一无是处,首先它让我们了解到多播事件必需有一个“链表”之类的东东来保存信息,其次它在所有的C++编译器中都能执行。
嗯,我要的不是这个,我要的是类似于下面的东东
typedef delegate<void(int,int)> ClickHandle; //声明委托
event<ClickHandler> OnClick; //声明事件
test.OnClick+= ClickHandle(obj,ObjOnClick); //注册事件
它应该有以下几个优点:
1、 类似于C#的语法,或者是非常便于书写
2、 易于使用,而且支持多播委托,甚至是异步多播委托
3、 不为不需的功能付出成本
4/、尽可能地靠近C++标准,(完全符合C++标准的编译器好象还没有吧?)
下一篇预告,将带领大家领略前文中提到过三大高手的不同实现。哈哈,看高手比武是很有趣的。