C++笔记-16-委托机制的实现
在C#中,有完善的委托可以存储成员函数。但是在C++中,我们需要自己利用C++特性去实现委托。
首先,我们要解决的问题就是如何存储成员函数指针,此外,对于每一个成员函数指针,他都需要一个类对象去调用它
此外,考虑到全局函数,他的存储方法又和成员函数不同,因此我们需要通过抽象类实现类型存储的多态化。
思路解读
委托抽象类
第一步,我们需要一个抽象类,作为任意委托类型的基类
class IDelegate
{
public:
virtual ~IDelegate() { }
virtual bool isType(const std::type_info& _type) = 0;
virtual void invoke() = 0;
virtual bool compare(IDelegate *_delegate) const = 0;
};
这个类存在3个接口
- 回调函数
invoke()
- 后面两个是判断输入函数地址与本类中已经存储的函数地址是否相同,这个接口的作用,会在后面说明
全局函数委托
全局函数的委托比较简单,我们先实现它
//全局函数委托类
class CStaticDelegate : public IDelegate
{
public:
typedef void (*Func)();
CStaticDelegate(Func _func) : mFunc(_func) { }
//查看类是否一致
virtual bool isType(const std::type_info& _type) { return typeid(CStaticDelegate) == _type; }
virtual void invoke() { mFunc(); }
//输入委托基类
virtual bool compare(IDelegate *_delegate) const
{
//判断和自身的类型是否相同
if (0 == _delegate || !_delegate->isType(typeid(CStaticDelegate)) ) return false;
//向下转型,判断函数地址是否相同
CStaticDelegate * cast = static_cast<CStaticDelegate*>(_delegate);
return cast->mFunc == mFunc;
}
private:
Func mFunc;
};
首先,通过typedef
统一标定函数指针类型名称:void (*Func)()
,这是一个指向 无返回值无参数的函数地址 的函数指针。原类型写法:void (*)()
现在通过Func
去代替它,作为这个类型的替代名称。
然后通过它,定义一个私有函数指针: Func mFunc;
然后就是依次重写函数,这里一一讲解
Invoke()
通过函数指针调用全局函数,比较简单
- 类型比较
isType(const std::type_info& _type)
通过typeid(类名).name()
查看输入类型与本类是否相同
compare(IDelegate *_delegate)
输入委托类指针,并调用isType
,查看与本类是否相同,解析函数体:
//判断和自身的类型是否相同
if (0 == _delegate || !_delegate->isType(typeid(CStaticDelegate)) ) return false;
//向下转型,判断函数地址是否相同
CStaticDelegate * cast = static_cast<CStaticDelegate*>(_delegate);
return cast->mFunc == mFunc;
首先判断输入类型指针是否为空(判断地址等于0和等于NULL是一样的),或者它不是本类,那么函数地址也就不可能相同了,直接返回false
其次,将该类向下转型,判断该类的函数地址与本类的函数地址是否相同,是为了后面在添加成员函数体的时候,检测是否重复添加
成员函数委托
template<class T>
class CMethodDelegate : public IDelegate
{
public:
typedef void (T::*Method)();
CMethodDelegate(T * _object, Method _method) : mObject(_object), mMethod(_method) { }
virtual bool isType( const std::type_info& _type) { return typeid(CMethodDelegate<T>) == _type; }
virtual void invoke()
{
(mObject->*mMethod)();
}
virtual bool compare(IDelegate *_delegate) const
{
if (0 == _delegate || !_delegate->isType(typeid(CMethodDelegate<T>))) return false;
CMethodDelegate<T>* cast = static_cast<CMethodDelegate<T>*>(_delegate);
return cast->mObject == mObject && cast->mMethod == mMethod;
}
private:
T * mObject;
Method mMethod;
};
对于成员函数委托,需要使用模版,因为不能对一个类单独化。
其次,通过typedef
统一标定成员函数指针:typedef void (T::*Method)();
这是一个指向 T作用域下的无返回值的无参成员函数 的函数指针
通过它,定义函数指针: Method mMethod;
再定义,调用这个函数的类对象指针,因为调用成员函数需要类对象来调用: T * mObject;
然后就是依次重写函数
Invoke()
通过类对象调用成员函数指针,调用成员函数:(mObject->*mMethod)();
isType(const std::type_info& _type)
基本一样,不多解释了
compare(IDelegate *_delegate)
基本一样,只是在比较的时候,要先比较:类对象地址是否相同,再比较:类对象下的成员函数地址是否相同。
至此,全局无参无返回值函数委托类,成员无参无返回值函数委托类已经完成。
那么,现在我们还需要一个对于委托的管理类,用于管理委托的存储,绑定与解绑定
委托管理
委托管理类,命名为
class CMultiDelegate;
存储委托,我们需要容器,这里使用list
//存储委托容器
typedef list<IDelegate*> ListDelegate;
//容器迭代器
typedef ListDelegate::iterator ListDelegateIterator;
//容器const迭代器
typedef ListDelegate::const_iterator ConstListDelegateIterator;
在分别定义其迭代器类型方便使用,这里容器存储的是委托指针,对于地址的存储,应当使用堆区地址,由我们手动控制,否则会出现非法访问的问题。
并定义私有容器
private:
ListDelegate mListDelegates;//链表容器
构造函数与析构函数
CMultiDelegate () { }
~CMultiDelegate () { clear(); }
检查是否容器是否为空的函数
//是否为空
bool empty() const
{
for (ConstListDelegateIterator iter = mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)
{
if (*iter) return false;
}
return true;
}
迭代器遍历容器,如果指针不为NULL则返回false
;
清空容器函数
//清空
void clear()
{
for (ListDelegateIterator iter=mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)
{
if (*iter)
{
delete (*iter);
(*iter) = 0;
}
}
}
迭代器遍历容器,如果指针不为NULL,则delete
指针,手动释放内存,然后将指针赋为NULL
添加绑定函数
//添加绑定
CMultiDelegate& operator+=(IDelegate* _delegate)
{
for (ListDelegateIterator iter=mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)
{
if ((*iter) && (*iter)->compare(_delegate))//判断是否已经存在,就不在添加
{
delete _delegate;
return *this;
}
}
mListDelegates.push_back(_delegate);
return *this;
}
重载+=
符号,用于添加委托绑定。
通过迭代器遍历容器,首先查看遍历的每一个指针是否不为NULL,再查看该指针指向的委托是否与当前要绑定的委托相同,并通过委托的compare
函数,比较是否相同。如果相同,那么直接释放当前要绑定的委托对象,然后返回自身引用。
如果不存在,则添加委托。
解除委托绑定
//解开绑定
CMultiDelegate& operator-=(IDelegate* _delegate)
{
for (ListDelegateIterator iter=mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)
{
if ((*iter) && (*iter)->compare(_delegate))
{
if ((*iter) != _delegate) delete (*iter);
(*iter) = 0;
break;
}
}
delete _delegate;
return *this;
}
解除委托绑定,重载-=
符号
通过迭代器遍历容器,查看每一次遍历的指针是否为空,若不为空,则查看该委托与输入的委托是否相同,如果类型相同,则再判断输入参数与当前指针的地址是否相同,如果不相同,则通过delete
释放指针内存,并将指针设置为NULL
跳出循环,再释放输入的委托对象。
这种解绑定方法,是通过输入委托对象,再通过遍历容器判断容器里是否存在指针指向的委托对象与该委托对象类型相同的委托对象,如果类型相同,还需要判断地址是否相同,因为如果地址相同,那么输入的委托对象和存储的委托对象是同一个地址,只需要释放一次即可,所以这里进行一次判断。如果地址不同,那么分别进行各自的释放即可。
仿函数调用委托
void operator()( )
{
//调用
ListDelegateIterator iter = mListDelegates.begin();
while (iter != mListDelegates.end())
{
if (0 == (*iter))
{
iter = mListDelegates.erase(iter);
}
else
{
(*iter)->invoke();
++iter;
}
}
}
重载()
,用于全体绑定委托对象的调用。
首先遍历委托对象,查看存储的指针是否为空,如果为空,则从容器中移除指针,这里需要考虑到的是,因为存储的是地址,而外部也有指针指向这个地址,而如果外部进行了释放,那么内部调用就会出现异常,因此这里调用之前需要进行一次判断;如果指针指向地址部位NULL,那么则调用指针指向委托的invoke()
函数,进行调用即可。
完整的代码
class CMultiDelegate
{
public:
//存储委托容器
typedef list<IDelegate*> ListDelegate;
//容器迭代器
typedef ListDelegate::iterator ListDelegateIterator;
//容器const迭代器
typedef ListDelegate::const_iterator ConstListDelegateIterator;
CMultiDelegate () { }
~CMultiDelegate () { clear(); }
//是否为空
bool empty() const
{
for (ConstListDelegateIterator iter = mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)
{
if (*iter) return false;
}
return true;
}
//清空
void clear()
{
for (ListDelegateIterator iter=mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)
{
if (*iter)
{
delete (*iter);
(*iter) = 0;
}
}
}
//添加绑定
CMultiDelegate& operator+=(IDelegate* _delegate)
{
for (ListDelegateIterator iter=mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)
{
if ((*iter) && (*iter)->compare(_delegate))//判断是否已经存在,就不在添加
{
delete _delegate;
return *this;
}
}
mListDelegates.push_back(_delegate);
return *this;
}
//解开绑定
CMultiDelegate& operator-=(IDelegate* _delegate)
{
for (ListDelegateIterator iter=mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)
{
if ((*iter) && (*iter)->compare(_delegate))
{
if ((*iter) != _delegate) delete (*iter);
(*iter) = 0;
break;
}
}
delete _delegate;
return *this;
}
void operator()( )
{
//调用
ListDelegateIterator iter = mListDelegates.begin();
while (iter != mListDelegates.end())
{
if (0 == (*iter))
{
iter = mListDelegates.erase(iter);
}
else
{
(*iter)->invoke();
++iter;
}
}
}
private:
ListDelegate mListDelegates;//链表容器
};
统一创建委托对象的回调函数
inline IDelegate* newDelegate( void (*_func)() )
{
return new CStaticDelegate(_func);
}
template<class T>
inline IDelegate* newDelegate( T * _object, void (T::*_method)() )
{
return new CMethodDelegate<T>(_object, _method);
}
为了能够统一的创建委托对象,我们声明并实现了创建委托对象的回调函数。
通过不同的函数重载,可以返回对应的委托
测试
那么简单的委托已经完成了,现在我们来测试一下功能
class TestClass{
public:
static void print_static(){
cout << "hello world_static" << endl;
}
void print(){
cout << "hello world_classA" << endl;
}
};
void print(){
cout << "hello world" << endl;
}
int main() {
CMultiDelegate delegate;
TestClass c;
delegate +=newDelegate(print);
delegate +=newDelegate(&TestClass::print_static);
delegate +=newDelegate(&c,&TestClass::print);
delegate();
return 0;
}
最后看到输出三个hello world
就代表成功了!!
泛用扩展
应用模版可变参数,以能够泛用化扩展
完整代码
template<typename ReturnType, typename ...ParamType>
class IDelegate
{
public:
IDelegate(){}
virtual ~IDelegate(){}
virtual bool isType(const std::type_info& _type) = 0;
virtual ReturnType invoke(ParamType ... params) = 0;
virtual bool compare(IDelegate<ReturnType, ParamType...> *_delegate) const = 0;
};
//普通函数委托
template<typename ReturnType, typename ...ParamType>
class CStaticDelegate :
public IDelegate<ReturnType, ParamType...>
{
public:
typedef ReturnType(*Func)(ParamType...);
CStaticDelegate(Func _func) : mFunc(_func) { }
virtual bool isType(const std::type_info& _type) { return typeid(CStaticDelegate<ReturnType, ParamType...>) == _type; }
virtual ReturnType invoke(ParamType ... params) { return mFunc(params...); }
virtual bool compare(IDelegate<ReturnType, ParamType ...> *_delegate)const
{
if (0 == _delegate || !_delegate->isType(typeid(CStaticDelegate<ReturnType, ParamType ...>))) return false;
CStaticDelegate<ReturnType, ParamType ...> * cast = static_cast<CStaticDelegate<ReturnType, ParamType ...>*>(_delegate);
return cast->mFunc == mFunc;
}
virtual ~CStaticDelegate(){}
private:
Func mFunc;
};
//成员函数委托
template<typename T, typename ReturnType, typename ...ParamType>
class CMethodDelegate :
public IDelegate<ReturnType, ParamType...>
{
public:
typedef ReturnType(T::*Method)(ParamType...);
CMethodDelegate(T * _object, Method _method) : mObject(_object), mMethod(_method) { }
virtual bool isType(const std::type_info& _type) { return typeid(CMethodDelegate<T, ReturnType, ParamType...>) == _type; }
virtual ReturnType invoke(ParamType...params)
{
(mObject->*mMethod)(params...);
}
virtual bool compare(IDelegate<ReturnType, ParamType...> *_delegate) const
{
if (0 == _delegate || !_delegate->isType(typeid(CMethodDelegate<T,ReturnType, ParamType...>))) return false;
CMethodDelegate<T,ReturnType, ParamType...>* cast = static_cast<CMethodDelegate<T,ReturnType, ParamType...>*>(_delegate);
return cast->mObject == mObject && cast->mMethod == mMethod;
}
CMethodDelegate(){}
virtual ~CMethodDelegate(){}
private:
T * mObject;
Method mMethod;
};
//多播委托
template<typename ReturnType, typename ...ParamType>
class CMultiDelegate
{
public:
typedef std::list<IDelegate<ReturnType, ParamType...>*> ListDelegate;
typedef typename ListDelegate::iterator ListDelegateIterator;
typedef typename ListDelegate::const_iterator ConstListDelegateIterator;
CMultiDelegate() { }
~CMultiDelegate() { clear(); }
bool empty() const
{
for (ConstListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
{
if (*iter) return false;
}
return true;
}
void clear()
{
for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
{
if (*iter)
{
delete (*iter);
(*iter) = nullptr;
}
}
}
CMultiDelegate<ReturnType, ParamType...>& operator+=(IDelegate<ReturnType, ParamType...>* _delegate)
{
for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
{
if ((*iter) && (*iter)->compare(_delegate))
{
delete _delegate;
return *this;
}
}
mListDelegates.push_back(_delegate);
return *this;
}
CMultiDelegate<ReturnType, ParamType...>& operator-=(IDelegate<ReturnType, ParamType...>* _delegate)
{
for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
{
if ((*iter) && (*iter)->compare(_delegate))
{
if ((*iter) != _delegate) delete (*iter); //避免同一个地址被delete两次
(*iter) = 0;
break;
}
}
delete _delegate;
return *this;
}
std::vector<ReturnType> operator()(ParamType... params)
{
ListDelegateIterator iter = mListDelegates.begin();
std::vector<ReturnType> _Results;
while (iter != mListDelegates.end())
{
if (0 == (*iter))
{
iter = mListDelegates.erase(iter);
}
else
{
_Results.push_back((*iter)->invoke(params...));
++iter;
}
}
return _Results;
}
private:
CMultiDelegate<ReturnType, ParamType...>(const CMultiDelegate& _event);
CMultiDelegate<ReturnType, ParamType...>& operator=(const CMultiDelegate& _event);
private:
ListDelegate mListDelegates;
};
这样还不够,由于我们使用了容器进行返回值的整体返回,而void
不存在返回值,因此,要对void
特殊处理。
这里我们对多播委托进行一个特化即可,其实只需要更改一下仿函数即可
template< typename ...ParamType>
class CMultiDelegate<void, ParamType...>
{
public:
typedef std::list<IDelegate<void, ParamType...>*> ListDelegate;
typedef typename ListDelegate::iterator ListDelegateIterator;
typedef typename ListDelegate::const_iterator ConstListDelegateIterator;
CMultiDelegate() { }
~CMultiDelegate() { clear(); }
bool empty() const
{
for (ConstListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
{
if (*iter) return false;
}
return true;
}
void clear()
{
for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
{
if (*iter)
{
delete (*iter);
(*iter) = nullptr;
}
}
}
CMultiDelegate<void, ParamType...>& operator+=(IDelegate<void, ParamType...>* _delegate)
{
for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
{
if ((*iter) && (*iter)->compare(_delegate))
{
delete _delegate;
return *this;
}
}
mListDelegates.push_back(_delegate);
return *this;
}
CMultiDelegate<void, ParamType...>& operator-=(IDelegate<void, ParamType...>* _delegate)
{
for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
{
if ((*iter) && (*iter)->compare(_delegate))
{
if ((*iter) != _delegate) delete (*iter); //避免同一个地址被delete两次
(*iter) = 0;
break;
}
}
delete _delegate;
return *this;
}
void operator()(ParamType... params)
{
ListDelegateIterator iter = mListDelegates.begin();
while (iter != mListDelegates.end())
{
if (0 == (*iter))
{
iter = mListDelegates.erase(iter);
}
else
{
(*iter)->invoke(params...);
++iter;
}
}
}
private:
CMultiDelegate<void, ParamType...>(const CMultiDelegate& _event);
CMultiDelegate<void, ParamType...>& operator=(const CMultiDelegate& _event);
private:
ListDelegate mListDelegates;
};
最后在写好回调函数即可
template<class ReturnType,class ...ParamType>
inline IDelegate<ReturnType,ParamType...>* newDelegate( ReturnType (*_func)() )
{
return new CStaticDelegate<ReturnType,ParamType...>(_func);
}
template<class T,class ReturnType,class ...ParamType>
inline IDelegate<ReturnType,ParamType...>* newDelegate(T* _object, ReturnType (T::*_method)(ParamType...))
{
return new CMethodDelegate<T,ReturnType,ParamType...>(_object, _method);
}
测试
class TestClass_B{
public:
static void print_static(int i){
cout << "hello world_static " << i << endl;
}
void print(int i){
cout << "hello world_classA " << i << endl;
}
};
int main() {
CMultiDelegate<void,int> delegate;
TestClass_B b;
delegate+=newDelegate<TestClass_B,void,int>(&b, &TestClass_B::print);
delegate(1);
return 0;
}
但是这样写你会发现你在newDelegate
里面对于传来的函数指针进行new CStaticDelegate
或new CMethodDelegate
的时候需要制定函数返回值、参数的个数和类型,这显然不满足动态类型演化。这时候我们想,能不能给定一个函数指针,让代码自动去识别这个函数的返回值和参数呢?答案是可以的,我们只需要对上面的CStaticDelegate
和CMethodDelegate
特化一个版本,并增加回调函数的重载,以简化调用并满足动态类型演化。
这里使用了一个小技巧:通过函数指针去得到函数返回值、参数个数类型。
特化
//普通函数的委托特化版本
template<typename ReturnType, typename ...ParamType>
class CStaticDelegate<ReturnType(*)(ParamType ...)> :
public IDelegate<ReturnType, ParamType ...>
{
public:
//定义 Func 为 void (void) 函数类型指针。
typedef ReturnType(*Func)(ParamType...);
CStaticDelegate(Func _func) : mFunc(_func) { }
virtual bool isType(const std::type_info& _type) { return typeid(CStaticDelegate<ReturnType(*)(ParamType ...)>) == _type; }
virtual ReturnType invoke(ParamType ... params) { return mFunc(params...); }
virtual bool compare(IDelegate<ReturnType, ParamType ...> *_delegate)const
{
if (0 == _delegate || !_delegate->isType(typeid(CStaticDelegate<ReturnType(*)(ParamType ...)>))) return false;
CStaticDelegate<ReturnType(*)(ParamType ...)> * cast = static_cast<CStaticDelegate<ReturnType(*)(ParamType ...)>*>(_delegate);
return cast->mFunc == mFunc;
}
virtual ~CStaticDelegate(){}
private:
Func mFunc;
};
//成员函数委托特化
template<typename T, typename ReturnType, typename ...ParamType>
class CMethodDelegate<T,ReturnType (T:: *)(ParamType...)> :
public IDelegate<ReturnType, ParamType...>
{
public:
typedef ReturnType(T::*Method)(ParamType...);
CMethodDelegate(T * _object, Method _method) : mObject(_object), mMethod(_method) {
// cout <<"??";
}
virtual bool isType(const std::type_info& _type) { return typeid(CMethodDelegate<T,ReturnType(T:: *)(ParamType...)>) == _type; }
virtual ReturnType invoke(ParamType...params)
{
return (mObject->*mMethod)(params...);
}
virtual bool compare(IDelegate<ReturnType, ParamType...> *_delegate) const
{
if (0 == _delegate || !_delegate->isType(typeid(CMethodDelegate<T, ReturnType(T:: *)(ParamType...)>))) return false;
CMethodDelegate<T, ReturnType(T:: *)(ParamType...)>* cast = static_cast<CMethodDelegate<T, ReturnType(T:: *)(ParamType...)>*>(_delegate);
return cast->mObject == mObject && cast->mMethod == mMethod;
}
CMethodDelegate(){}
virtual ~CMethodDelegate(){}
private:
T * mObject;
Method mMethod;
};
这里使用了类特化的使用
我们以其中一个为例
template<typename T, typename ReturnType, typename ...ParamType>
class CMethodDelegate<T,ReturnType (T:: *)(ParamType...)> :
public IDelegate<ReturnType, ParamType...>
这里其实是特化为另外一个模版类了。
模版更改为:
-
T
-
ReturnType (T:: *)(ParamType...)
后者看起来很复杂,其实只是一个成员函数指针。
也就是说现在只需要输入两个参数:类对象,类成员函数指针
因此我们增加回调函数的重载
template<typename T>
CStaticDelegate<T>* newDelegate(T func)
{
return new CStaticDelegate<T>(func);
}
template< typename T,typename F>
CMethodDelegate<T,F>* newDelegate(T * _object, F func)
{
return new CMethodDelegate<T, F>(_object, func);
}
这样就能简化回调函数,甚至可以用宏来简化
测试
class TestClass_B{
public:
static void print_static(int i){
cout << "hello world_static " << i << endl;
}
void print(int i){
cout << "hello world_classA " << i << endl;
}
};
int main() {
CMultiDelegate<void,int> delegate;
TestClass_B b;
delegate+=newDelegate(&b, &TestClass_B::print);
delegate(1);
return 0;
}
能够看到输出hello world_classA
,就成功了。