本小节将使用signals2开发一个完整的观察者模式示例程序,用来演示信号/插槽的用法。这个程序将模拟一个平常生活场景:客人按门铃,门铃响,护士开门,婴儿哭闹。ios
Ring.h:
程序员
#ifndef __RING_H__ #define __RING_H__ #include "iostream" using namespace std; #include "boost/signals2.hpp" class Ring { public: typedef boost::signals2::signal<void()> signal_t; typedef signal_t::slot_type slot_t; boost::signals2::connection connect(const slot_t& s) { return alarm.connect(s); } void Press() { cout << "Ring alarm..." << endl; alarm(); } private: signal_t alarm; }; #endif // !__RING_H__
Nurse.h:算法
#ifndef __NURSE_H__ #define __NURSE_H__ #include "boost/random.hpp" extern char const nurse1[] = "Mary"; extern char const nurse2[] = "Kate"; typedef boost::variate_generator<boost::rand48, boost::uniform_smallint<> > bool_rand; bool_rand g_rand(boost::rand48(time(0)), boost::uniform_smallint<>(0, 100)); template<char const* name> class Nurse { public: Nurse() : rand_(g_rand) { } void Action() { cout << name; if (rand_() > 30) { cout << " wake up and open door." << endl; } else { cout << " is sleeping..." << endl; } } private: bool_rand& rand_; }; #endif // !__NURSE_H__
Baby.h:编程
#ifndef __BABY_H__ #define __BABY_H__ extern char const baby1[] = "Tom"; extern char const baby2[] = "Jerry"; template<char const* name> class Baby { public: Baby() : rand(g_rand) { } void Action() { cout << "Baby " << name; if (rand() > 50) { cout << " wake up and crying loudly..." << endl; } else { cout << " is sleeping sweetly..." << endl; } } private: bool_rand& rand; }; #endif // !__BABY_H__
Guest.h:c#
#ifndef __GUEST_H__ #define __GUEST_H__ #include "Ring.h" class Guest { public: void Press(Ring& r) { cout << "A guest press the ring." << endl; r.Press(); } }; #endif // !__GUEST_H__
main:设计模式
#include "stdafx.h" #include "boost/utility/result_of.hpp" #include "boost/typeof/typeof.hpp" #include "boost/assign.hpp" #include "boost/ref.hpp" #include "boost/bind.hpp" #include "boost/function.hpp" #include "boost/signals2.hpp" #include "numeric" #include "iostream" using namespace std; #include "Ring.h" #include "nurse.h" #include "Baby.h" #include "Guest.h" int _tmain(int argc, _TCHAR* argv[]) { // 声明门铃、护士、婴儿、客人等类的实例 Ring r; Nurse<nurse1> n1; Nurse<nurse2> n2; Baby<baby1> b1; Baby<baby2> b2; Guest g; // 把护士、婴儿、门铃链接起来 r.connect(boost::bind(&Nurse<nurse1>::Action, n1)); r.connect(boost::bind(&Nurse<nurse2>::Action, n2)); r.connect(boost::bind(&Baby<baby1>::Action, b1)); r.connect(boost::bind(&Baby<baby2>::Action, b2)); // 客人按动门铃,触发一系列的事件 g.Press(r); return 0; }
在程序中采用随机数来让护士和婴儿的行为具备不肯定性。随机数的产生使用random库,为了方便使用把随机数发生器定义为全局变量:框架
typedef boost::variate_generator<boost::rand48, boost::uniform_smallint<> > bool_rand;
bool_rand g_rand(boost::rand48(time(0)), boost::uniform_smallint<>(0, 100));
而后咱们实现护士类nurse,他有一个action()函数,根据随机数决定是惊醒开门仍是继续睡觉。注意:他的模板参数,使用了charconst*做为护士的名字,所以实例化时字符串必须声明成extern(要否则别的地方找不到这个串)。dom
signals2中的信号/插槽机制原理上相似于c#语言的event/deletegate机制。函数
但c#的deletegate的功能要比signals2弱,它要求精确的类型匹配,也没有合并器的概念,只能返回一个结果。spa
deletegate使用operator+=来连接event与deletegate,signals2则使用connect()函数。这是由于signals2在设计时认为operator+=并无带来太多的好处,反而会致使连续使用+=连接、-=等其余语义问题。
不过咱们能够稍微重载一下+=号来实现这种方式:
#include "stdafx.h" #include "boost/utility/result_of.hpp" #include "boost/typeof/typeof.hpp" #include "boost/assign.hpp" #include "boost/ref.hpp" #include "boost/bind.hpp" #include "boost/function.hpp" #include "boost/signals2.hpp" #include "numeric" #include "iostream" using namespace std; template<int N> struct Slot { void operator()(int x) { cout << "Slot current N is : " << N << endl; } }; template<int N> bool operator== (const Slot<N>& a, const Slot<N>& b) { return true; } template<typename Signature> class SigEx { public: typedef boost::signals2::signal<Signature> signal_type; typedef typename signal_type::slot_type slot_type; boost::signals2::connection connect(const slot_type& s) { return sig.connect(s); } boost::signals2::connection operator+=(const slot_type& s) { return connect(s); } typename signal_type::result_type operator()(typename signal_type::template arg<0>::type a0) { return sig(a0); } private: signal_type sig; }; int _tmain(int argc, _TCHAR* argv[]) { SigEx<void(int)> sig; sig += Slot<10>(); sig += Slot<10>(); sig(2); return 0; }
首先讨论了result_of库。它很小但功能很强大,使用了模板元编程技术,能够帮助肯定一个调用表达式的返回类型,相似typeof库,主要用于泛型编程。
ref也是一个很小的库。它最初是tuple库的一部分,后来因为其重要性二被移出,成为了单独的库,并且也被收入了TR1标准草案。它可以包装对象的引用,变成一个能够被拷贝、赋值的普通对象,所以减小了昂贵的复制代价,标准库算法、tuple、bind、function等许多库均可以从ref库受益。但ref库实现有个较大的缺陷,不支持operator()重载(函数调用),经过更改源文件,作出了一个示范性质的实现,它能够配合标准库算法和其余库组件正常工做。
bind是一个功能强大的函数绑按期。它能够绑定任何可调用对象,搭配标准算法能够得到灵活操做容器内元素的强大功能。但bind过于强大也是个弱点。程序员学会bind的用法后每每会倾向于老是用bind解法,而忘记代码的清晰易读才是最重要的。
function库是函数指针的泛化,能够存储任意可调用的对象,所以function库常常配合bind使用,它能够存储bind表达式的结果,以备以后调用。
最后是signals2库,它综合运用了前四个组件,使用了信号/插槽机制,是观察者设计模式的一个具体应用,也是一个功能强大的回调框架。使用signals2库能够简化对象间的通讯关系,下降它们的耦合性,只须要在程序开始时把它们链接起来,以后的一切都会自动处理。