C++没有提供委托模型,为了达到目的,需要继承一个类并重写virtual方法,这种做法需要很多代码,效率比较低下。然而,在C++面前,没有什么不可能,已经有很多人实现了各种委托。其中比较著名的有FastDelegate,这个模型在《Member Function Pointers and the Fastest Possible C++ Delegates》中,这个模型就是快,因此要依赖编译器的具体实现。下面看实例:
成员函数指针的操作
class A{
public:
void Func(int){...}
};
要取得Func函数指针,void (A::*pFunc)(int)=&A::Func;
::*是一个特殊操作符,表示pFunc是一个指针,指向A的成员。获取成员函数的地址不能通过类对象来获取,必须通过类名获取,而且要加上取地址操作。
那么如果通过指针来调用该函数,成员函数都隐含了一个this参数,表示函数要操作的对象,我们只获取了函数的指针,还缺少一个对象作为this参数,为了这个目的,我们先创建一个对象,然后通过该对象来调用成员函数指针:
A a; ( a.*pFunc)(10);
A* pa=&a;
(pa->*pFunc)(11);
第一种方式通过对象本身调用,第二种方式通过对象指针调用,效果一样。
使用类模板:
要调用一个成员函数,仅仅有成员函数指针是不够的,还需要一个对象指针,所以要用一个类将两者绑到一起。由于对象的类型是无穷多的,所以这里必须使用类模板:
template
class DelegateHandler
{
public:
DelegateHandler(T* pT,void(T::*pFunc)(int)):m_pT(pT),m_pFunc(pFunc)
{}
void Invoke(int value)
{
(m_pT->*m_pFunc)(value);
}
private:
T* m_pT;
void (T::*m_pFunc)(int);
};
可以这样使用该模板:
A a; DelegateHandler ah(&a,&A::Func);
ah.Invoke(3);
B b; DelegateHandler bh(&b,&B::Method);
bh.Invoke(4);
如果希望调用的目标是非成员函数,那么需要使用模板偏特化技术:
template<>
class DelegateHandler
{
public:
DelegateHandler(void(*pFunc)(int)):m_pFunc(pFunc)
{}
void Invoke(int value)
{
(*m_pFunc)(value);
}
private:
void (*m_pFunc)(int);
}
使用方式:
DelegateHandler
h.Invoke(5);
也许有人怀疑,非成员函数不需要将函数指针和函数对象指针绑到一块,为何还用一个类来包装函数指针呢?
使用多态:
对于单目标的委托来说,使用上面的代码就已经足够。我们实现多目标的委托。多目标委托起始就是一个容器,在这个容器里可以存放多个对象,当调用委托的时候一次调用每个对象。容器里的对象应该都是相同的类型,这样才能够放到强类型的容器中;而且委托调用方不应该知道具体的调用目标是什么,所以这些对象也应该要隐藏具体的细节。不过上面的类模板不具备这些能力,DelegateHandler和DelegateHandler是不同的类型,不能放到一个容器中。
解决方法是使用多态,令所有目标类窦继承一个公共接口,调用方只通过这个接口来进行调用
class IDelegateHandler
{
public:
virtual ~IDelegateHandler(){}
virtual void Invoke(int)=0;
};
令DelegateHandler继承该接口
template
class DelegateHandle:public IDelegateHandler
{
}
template<>
class DelegateHandler<>:public IDelegateHandler
{
}
现在可以将各种类型的DelegateHandler放到同一容器中,并使用同一方法调用了:
A a; B b; DelegateHandler ah(&a,&A::Func);
DelegateHandler bh(&b,&B::Func);
DelegateHandler<> h(NonmemberFunc);
使用宏:
函数参数的声明可以只有类型而没有名字,的女士为了在函数内使用参数,该参数必须有名字。
void Invoke(int){}//不能使用参数
void Invoke(int value){}
另外调用的时候只能使用名字,不能带有类型: int value=10;
Invoke(value);
DELEGATE(void,DelegateHandler,int,int);
从第三个参数开始应该使用可变参数,像这样(只截取了定义的一部分)
#define DELEGATE(retType,name,...)
retType Invoke(__VA_ARGS__)
{
return (*m_pFunc)(__VA_ARGS__);
}
...