程序开发基础学习四(boost::signal2 函数学习)

时间:2022-09-09 07:27:57

        在游戏编程中,新的策划需求总是在迭代不停。。。。。。,对于游戏程序员肯定深有感触吧,遇到这种情况咱只能小小的抱怨下,活还得干。尤其是遇到耦合到很多类的时候,要是直接实现不加抽象的话,那咱的代码就要被拆的七零八落,并且在代码维护性和程序健壮性上问题很大。前面说到的问题其实就是常听到的代码耦合,说白了就是在原有的代码上插上一段代码。而signal就能很好的解决这种问题。这样不仅可以让咱的代码优美,而且修改的时候也很方便。好的,按照惯例先模拟一下signal的实现方法。看清它的真面目,也就不用害怕了。

1、signal模拟程序

#include "stdafx.h"
#include <iostream>
#include "boost/bind.hpp"
#include "boost/function.hpp"
#include "boost/signals2.hpp"
using namespace std;
using namespace boost;
class Buttion
{
public:
	void connect(void (*f)(int, int));
	void OnBtnClick();
private:
	void (*fuc_)(int, int);
};

void Buttion::connect(void (*f)(int, int))
{
	fuc_ = f;
}
void Buttion::OnBtnClick()
{
	fuc_(10, 20);
}
void PrintCodeline(int x, int y)
{
	cout<<"x:"<<x<<",y:"<<y<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
	Buttion btn;
	btn.connect(&PrintCodeline);
	btn.OnBtnClick();
	getchar();
	return 0;
}

         看见了吗?其实就是传递个函数指针,不过signal用的是template而已,并且里面还有一些保护机制。在下面的程序会提到。

2、signal函数实现

#include "stdafx.h"
#include <iostream>
#include "boost/bind.hpp"
#include "boost/function.hpp"
#include "boost/signals2.hpp"
using namespace std;
using namespace boost;
class Buttion
{
	typedef signals2::signal<void (int ,int)> OnClick;
public:
	typedef OnClick::slot_type OnSlottype;
	signals2::connection connect(const OnSlottype& type);
	void OnBtnClick();
private:
	OnClick onclick_;
	signals2::connection connect_;
};

signals2::connection Buttion::connect(const OnSlottype& type)
{
	return connect_ = onclick_.connect(type);
}
void Buttion::OnBtnClick()
{
	onclick_(10, 20);
}
void PrintCodeline(int x, int y)
{
	cout<<"x:"<<x<<",y:"<<y<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
	Buttion btn;
	btn.connect(&PrintCodeline);
	btn.OnBtnClick();
	getchar();
	return 0;
}
        重点有两个方面,一个是OnSlottype,其实就相当于上一个列子中的函数指针,在一个要问为什么要返回成员变量connect_,一个原因是通过它的返回值可以知道是否connect成功,另一个在用完之后调用disconnect方法,释放connect_变量。

3、类之间相互调用

// mercurial.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include "boost/bind.hpp"
#include "boost/function.hpp"
#include "boost/ref.hpp"
#include "boost/shared_ptr.hpp"
#include "boost/signals2.hpp"
using namespace std;
using namespace boost;
class Document
{
	typedef signals2::signal<void (void)> signal_t;
public:
	Document(){}
	signals2::connection connect(const signal_t::slot_type &subscriper)
	{
		return sig_.connect(subscriper);
	}
	void Append(const char* text)
	{
		text_ += text;
		sig_();
	}
	string GetText()
	{
		return text_;
	}
private:
	signal_t sig_;
	string text_;
};
class TextView
{
public:
	TextView(Document* doc):doc_(doc)
	{
		connect_ = doc_->connect(bind(&TextView::Refresh, this));
	}
	~TextView()
	{
		connect_.disconnect();
	}
	void Refresh()
	{
		cout<<doc_->GetText()<<endl;
	}
private:
	signals2::connection connect_;
	Document* doc_;
};

class HexView
{
public:
	HexView(Document* doc):doc_(doc)
	{
		connect_ = doc_->connect(bind(&HexView::Refresh, this));
	}
	~HexView()
	{
		connect_.disconnect();
	}
	void Refresh()
	{
		string str = doc_->GetText();
		for (size_t i = 0; i < str.size(); ++i)
		{
			cout<<(int)str[i];
		}
		cout<<endl;
	}
private:
	Document* doc_;
	signals2::connection connect_;
};
int _tmain(int argc, char* argv[])
{
	Document *doc = new Document();
	TextView text(doc);
	HexView hex(doc);
	doc->Append(argc == 2 ? argv[1] : "Hello,Word.");
	delete doc;
	getchar();
	return 0;
}

       这个程序模仿boost给出的示例写的,要注意的是在TextView和HexView两个构造函数传递的是Document的指针,之前我自己写的时候传递的是Document的复制,但是编译不过。signal自己有不允许复制的机制。这就很强大了,本来是粗心造成的一个问题(复制的话,就不能改变传递的参数了),signal在编译的时候就能检测出问题了。有点只能的感觉了。再一个在main函数里我只申明TextView和HexView两个对象。调用下Document的一个函数,就将TextView和HexView的函数都别调用了。这不就是传说中的“杀人”于无形中,这样很大程度上封装了函数,简化了表现层的复杂度。