boost------signals2的使用2(Boost程序庫徹底開發指南)讀書筆記

一、應用於觀察者模式

本小節將使用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.hc#

#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

 

 

二、與C#的區別

 

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;
}


 

對前幾張blog的總結

 

首先討論了result_of庫。它很小但功能很強大,使用了模板元編程技術,能夠幫助肯定一個調用表達式的返回類型,相似typeof庫,主要用於泛型編程。


ref也是一個很小的庫。它最初是tuple庫的一部分,後來因爲其重要性二被移出,成爲了單獨的庫,並且也被收入了TR1標準草案。它可以包裝對象的引用,變成一個能夠被拷貝、賦值的普通對象,所以減小了昂貴的複製代價,標準庫算法、tuple、bind、function等許多庫均可以從ref庫受益。但ref庫實現有個較大的缺陷,不支持operator()重載(函數調用),經過更改源文件,作出了一個示範性質的實現,它能夠配合標準庫算法和其餘庫組件正常工做。


bind是一個功能強大的函數綁按期。它能夠綁定任何可調用對象,搭配標準算法能夠得到靈活操做容器內元素的強大功能。但bind過於強大也是個弱點。程序員學會bind的用法後每每會傾向於老是用bind解法,而忘記代碼的清晰易讀纔是最重要的。


function庫是函數指針的泛化,能夠存儲任意可調用的對象,所以function庫常常配合bind使用,它能夠存儲bind表達式的結果,以備以後調用。


最後是signals2庫,它綜合運用了前四個組件,使用了信號/插槽機制,是觀察者設計模式的一個具體應用,也是一個功能強大的回調框架。使用signals2庫能夠簡化對象間的通訊關係,下降它們的耦合性,只須要在程序開始時把它們鏈接起來,以後的一切都會自動處理。

相關文章
相關標籤/搜索