在游戏编程中,新的策划需求总是在迭代不停。。。。。。,对于游戏程序员肯定深有感触吧,遇到这种情况咱只能小小的抱怨下,活还得干。尤其是遇到耦合到很多类的时候,要是直接实现不加抽象的话,那咱的代码就要被拆的七零八落,并且在代码维护性和程序健壮性上问题很大。前面说到的问题其实就是常听到的代码耦合,说白了就是在原有的代码上插上一段代码。而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的函数都别调用了。这不就是传说中的“杀人”于无形中,这样很大程度上封装了函数,简化了表现层的复杂度。