接着(三)的内容说,设计模式中有一种适配器模式,它的意思是,当两个接口不匹配时,创建一个适配器在中间进行转接,使原本不能在一起工作的两个接口可以在一起工作。这种类型不匹配的情况在普通代码层也会经常出现,比如函数的参数类型,模版的参数等。那么模版也可以起到适配器的作用。很好的例子是traits,stl中可以发到大量的使用。下面例子Responst类是一个模版类,接受一个参数T,要求T类型有一个func函数,以及A_type的类型定义,否则编不过。
template<class T>
class Responst
{
public:
void show(T& t)
{
t->func();
}
typename T::A_type a;
};
class test
{
public:
typedef double A_type;
void func()
{
}
};
int main()
{
Responst<test> r;
//Responst<double> r1;//这行取消注释后编不过
}
那么如何让没有func函数和A_type类型定义的一个类型通过Responst模版?
可以用一个模版适配器。
定义一个模版类adapter来做为适配器,
template<class P>
class adapter
{
public:
typedef typename P::A_type A_type;
void func(P& t)
{
t->func();
}
};
template<>
class adapter<double>
{
public:
typedef int A_type;
void func(int& t)
{
}
};
这样就可以使用Responst<double> r了。
XX的封装
XX代表函数,函数又分为全局函数或成员函数。
为什么要对函数进行封装呢?可以说有2个原因,而应用,则主要是在回调函数中。
一般的C类型回调函数会是这样的:
//定义回调函数类型
typedef void (*p)(int*);
//注册回调到A
A.register(p,(int*)para);//para表示p需要的参数
//使用完后反注册
A.unregister(p,(int*)para)
这样做有什么问题?
从功能上说,没问题。但是从安全的角度说,它是不安全的,注册时一起传入了指针参数para,那么它需要在A中使用,那么如果我在调用了A.register且还未调用A.unregister时,由于一些其他操作,破坏了指针para,那么A中回调p时因为使用了para指针,就有可能造成异常。
原因二,接口不友好,C类型的接口本身就是不友好的,并且站在面向对象的角度,我们应该使用对象进行注册,而不是将回调函数和其参数分开。
有了这样的需求,那么就可以通过函数的封装来实现,也就用到了模版。
可以设计一个模版类,用于将函数指针和其参数封装,在以后的某一个时间点通过这个模版类的对象去调用被封装的函数,该函数可以是全局函数,也可以是成员函数。
如下:
template<class T>
class function;
template<class R,class Pa,class C>
class function<R(C::*)(Pa)>//定义模版类,用于封装
{
public:
typedef R(C::*fun)(Pa);
function(fun ptr,C*p)
{
m_ptr = ptr;
m_this = p;
}
R operator()(Pa a)
{
return (m_this->*m_ptr)(a);
}
private:
fun m_ptr;
C* m_this;
};
class B
{
public:
int a(int b)
{
printf("%d\n",b);
return 0;
}
};
int main()
{
B b;
function<int(B::*)(int)> f(&B::a,&b);//封装
f(100);//封装后可以任意键调用
}
关于回调,其实重点还不是在模版上,回调的安全性也不是靠上面的封装函数可以解决的,boost中有一套可以借鉴的方法来处理模版,而回调和事件,本身就是一个很大内容,以后有时间再研究。