| 一片枫叶's profile追梦的人BlogLists | Help |
|
March 22 类似boost库function的免delete委托实现困难不因你害怕而不出现,只因你勇敢面对迎刃而解
------ 鼓励已成大龄程序员的自己 近来试验着重写一个C++的委托功能,就是.net里的delegate关键字描述的东东,似乎C++里喜欢说是成员函数closure,记不太清楚了。 原来的实现跟loki库里的思路差不多,就是利用虚函数描述委托函数的原型,这个原型与具体实现类无关,子类实现函数的功能,实现 里可以记住具体实现函数的类实例,现在看来没有什么很巧妙的东东了: 1、接口层,纯虚接口,与实现对象无关。
class IEvent { public: virtual ~IEvent(){} public: virtual bool operator () (const EventArg& arg)=0; }; 2、实现层,利用模板参数化实现对象类型。
template<typename Obj> class Event : public IEvent { typedef bool (Obj::*Fptr)(const EventArg&); public: Event(Obj* pObj,Fptr fptr) { mObj=pObj; mFunc=fptr; } bool operator () (const EventArg& arg) { return (mObj->*mFunc)(arg); } private: Obj* mObj; Fptr mFunc; }; 3、创建函数,利用模板推导自动提供对象类型。 template<typename Obj> IEvent* create(Obj* pObj,bool (Obj::*fptr)(const EventArg&) { return new Event<Obj>(pObj,fptr); } boost库里的function不使用虚函数机制,而是使用安全的强制转换实现了这种委托功能,它的核心技术在于对成员函数closure
的对象封装(对象指针可以与普通指针互相强制转型)以及模板类的静态成员函数指针(函数原型可以不依赖模板类的模板参数 ,而函数实现可以使用模板类的模板参数,从而实现虚函数接口与实现动态绑定的类似效果)。 委托的实质其实是用一个普通函数原型(实现类无关的)关联一个函数closure对象(绑定到具体实现类的),一个是接口层的,
一个是实现层的。 不管是虚函数的实现还是基于强制转型的实现,closure对象的数据部分主要包含一个对象指针与一个函数指针,这两个指针在委
托的生命周期内是必须有效的,委托无需关心它们指向对象的释放,委托需要关心的是closure对象实例的释放,因为closure对象 有实现类的信息,不能直接作为委托的数据成员(否则委托方就被绑定到一个具体实现类了,而委托的涵义是可以有任意类型的实 现类),所以基于虚函数的实现与boost库的实现均使用了堆内存分配closure对象,从而强加给委托方一个释放的职责。 closure对象因为只是两个指针,占用的空间很小,如果不倒致类型依赖的话,作为委托方的数据成员是完全合适的,由此我们可 以看到一个底层的实现,假设委托方类为Delegation,closure对象类为Closure: class Delegation { public: ... 接口层的东西 ... private: char space[sizeof(Closure)]; }; Delegation d;
Closure c; *(Closure*)&(d.space)=c;//直接浅拷贝 经过这样的处理,Delegation的成员space的空间里就会保存一个Closure实例的数据,在实现代码里再将此数据强制转成Closure
对象也就没问题了(boost的实现里的模板类静态成员函数invoke里正适合做此工作),这里还有一个问题,就是sizeof(Closure) ,其中出现了Closure,会倒致Delegation对Closure的依赖,不过我们只是想知道Closure实例的大小,这个依赖似乎是可以消除的 ,前面说过,Closure里主要是保存一个对象指针与一个成员函数指针,我们只需要伪造一个类似的虚拟类,用这个虚拟类来获取 实例大小即可(也就是Delegation依赖一个虚拟类获取大小,这个大小却适用于任何Closure对象),因为涉及成员函数指针,C++对 有多重继承的类与无多重继承的类的成员函数指针大小是不一样的,我们这里需要取尺寸的最大值,也就是使用一个多重继承的虚 拟类。 上面的方法是利用对象实例浅拷贝来存储实例数据的,与此类似,还有一个办法是像内存池的实现一样重载new操作,采用带placement
参数的new重载,明确的将空间指定,下面我们给出基于重载new的实现,对我而言,用重载new感觉上比对象实例浅拷贝更优雅一些。 //提供Closure存储空间的虚拟类
struct ObjectPlaceHolder { //用来模拟实际情况类的虚拟类,成员函数指针在有继承与无继承下大小不一样, //ObjectPlaceHolder需要提供足够的空间供MemFuncInvoker...实例存储,所以需 //要按多继承情形模拟成员函数指针。 class DummyBaseA {}; class DummyBaseB {}; class DummyClass : public DummyBaseA, public DummyBaseB {}; public: ObjectPlaceHolder() { clear(); } ObjectPlaceHolder(const ObjectPlaceHolder& other) { obj=other.obj; fptr=other.fptr; } inline bool isNull(void) const { return obj==NULL || fptr==NULL; } inline void clear(void) { obj=NULL; fptr=NULL; } private: DummyClass* obj; void (DummyClass::*fptr)(void); }; //Closure对象实现,同时实现了静态成员函数接口invoke,这是实现虚函数相似功能的关键
//重载的new方法使用指定的空间作为对象实例化的空间,因为我们实际上是用的委托方的空 //间,并不需要释放,所以delete方法什么也不做 template<typename Delegation,typename T,typename R> struct MemFuncInvoker0 { private: friend typename Delegation; typedef MemFuncInvoker0<Delegation,T,R> ThisType; typedef R (T::*Fptr)(void); MemFuncInvoker0(T* obj,Fptr func) { mObj=obj; mFunc=func; } static inline R invoke(const ObjectPlaceHolder* address) { const ThisType* pThis=reinterpret_cast<const ThisType*>(address); T* pObj=pThis->mObj; Fptr pFunc=pThis->mFunc; return (pObj->*pFunc)(); } static inline void* operator new (size_t size,const ObjectPlaceHolder* address,size_t space) { _ASSERT(size<=space); return (void*)address; } static inline void operator delete(void*,const ObjectPlaceHolder* address,size_t space) {} private: T* mObj; Fptr mFunc; }; //委托的实现
template<typename R> struct Delegation0 { public: typedef Delegation0<R> ThisType; typedef R (*InvokerType)(const ObjectPlaceHolder*); public: Delegation0() { detach(); } Delegation0(const Delegation0& other) { mObj=other.mObj; invoker=other.invoker; } template<typename T> Delegation0(T* obj,R (T::*fptr)(void)) { attach(obj,fptr); } template<typename T> inline void attach(T* obj,R (T::*fptr)(void)) { invoker=MemFuncInvoker0<ThisType,T,R>::invoke; new(&mObj,sizeof(ObjectPlaceHolder)) MemFuncInvoker0<ThisType,T,R>(obj,fptr); } inline void detach(void) { mObj.clear(); invoker=NULL; } inline bool isNull(void) const { return mObj.isNull() || invoker==NULL; } inline R operator () (void) { return invoker(&mObj); } private: ObjectPlaceHolder mObj;//用来保存MemFuncInvoker实例的空间 InvokerType invoker; }; 上面的实现是对有一个返回类型(含void)无参数的函数样式的委托,一个典型用法如下:
class A { public: char* run(void) { if(!onRun.isNull()) return onRun(); } public: Delegation0<char*> onRun; }; class B
{ public: char* test(void) { return "B::test called !"; } }; int main(void) { A a; B b; a.onRun.attach(&b,&B::test); std::cout<<a.run()<<std::endl; ::_getch(); return 0; } 可以看到动态的将A::run委托到B::test的实现了。 |
|
|