C++实现.NET风格的委托2009-01-25 本站整理 引言在.NET中,委托被用来实现事件处理。它允许一个类(方法)先注册一个事件,然后当此事件被引发时此注册的方法就会被调用。在非.Net环境的C++中,这并不是一件容易的事,尤其是类的非静态成员函数,要做为回调函数就更困难了。本文的目标就是给出一种解决方案, 使类的静态成员函数,非静态成员函数,还有类非成员函数都能像回调函数一样使用。这个实现非常重视类型安全,为了保持类型安全我们省去了某些特性的实现。什么是委托?.NET框架中对委托的定义如下:"委托是一个可以保持对某个方法引用的类。不同于其它类,委托类有自己的签名(返回值,参数类型,个数),并且只能引用与其签名匹配的方法。委托其实可以看成一个类型安全的函数指针或回调函数。一个提供了委托的类允许其它函数或类在此委托上注册事件处理函数。然后当这个类的委托被执行时,就会遍历其处理函数列表,逐个调用,并传入传给委托的信息。而提供委托的那个类不需要知道委托注册了多少处理函数,委托自己会处理这一切。正文函数对象(functor)概述我们用函数对象(functor, function object)来实现C++中的委托。这允许一个非静态成员函数能在特定对象的环境中被调用。我们用模板技术来保证任何类类型都能在其上使用。一个基本的函数对象(functor)定义如下:template<class T> class Functor { public: // Constructor takes the values and stores them Functor(T *pObj, int (T::*pFunc)(int)) { m_pObject = pObj; m_pFunction = pFunc; } // Invokes the stored function intoperator ()(int p) { return (m_pObject->*m_pFunction)(p); } private: T *m_pObject; // Pointer to the object int (T::*m_pFunction)(int); // Pointer to the function }; 这个函数对象(functor)使用的函数格式为:返回类型为int,带一个类型为int的参数。操作符operator ()是函数对象的关键。它使一个函数对象(functor)使用起来和函数调用一样。它的工作就是每次执行时调用保存在类内部的函数指针。以下代码展示了如何使用这个函数对象(functor): class MyClass { public: int Square(int p) { return p * p; }; }; void some_function() { // Create a class to call in the context of MyClass theClass; // Create and initialise the functor object Functor<MyClass> myFunc(&theClass, MyClass::Square); // Call the functor using the overloaded () operator int result = myFunc(5); // result will hold the value 25 } 由于重载了operator ()运算符,调用函数对象(functor)几乎就和调用该函数本身一样方便。这里说“几乎”是因为指向实际对象的指针并没有被显示使用-它被存放在函数对象(functor)内部使用。的确,这很不错,但是我们为什么要使用函数对象(functor),而不是函数本身呢?很好的问题,当你知道你要调用的函数的签名(返回值和参数)而不关心其是否是类的成员函数,是哪个类的成员函数时,函数对象就非常的有用(译注:将这一信息局部化在对象内部,从而以统一的方式来调用所有具有相同签名的函数)看以下代码,我将它们划分成几项以便理解:首先,是一个用纯虚基类来表示的一个以一个int为参数,返回值为int的函数对象。它只有一个函数,虚拟的operator()操作符,这样,我们就可以在不知道某函数对象实例的实际对象类型的情况下调用函数对象(functor)了.// Abstract base class class Functor { public: // Invoke the functor (no implementation here as it must be overridden) virtualintoperator()(int) = 0; }; 下面就是一个可以被实例化为任何类类型的模板类,假设它也有一个以一个int为参数,返回为int的函数。它是从Functor派生来的,所以一个指向特定函数对象的指针可以传给任何一个需要其基类对象(Functor)指针的地方,所以此函数对象可以不管其真正的对象类型而被调用。除了基类和类名,这个类与之前给出的类是完全一样的: // Template functor template<class T> class TemplateFunctor : public Functor { public: // Constructor takes the values and stores them TemplateFunctor(T *pObj, int (T::*pFunc)(int)) { m_pObject = pObj; m_pFunction = pFunc; } // Invokes the stored function (overrides Functor::operator ()) intoperator ()(int p) { return (m_pObject->*m_pFunction)(p); } private: T *m_pObject; // Pointer to the object int (T::*m_pFunction)(int); // Pointer to the function }; 下面是一个以函数对象指针和该函数的参数为参数的简单函数,用来调用该函数对象。注意这里以基类Functor指针而不是派生模板类指针为参数。这是必需的, 因为每一个不同的模板参数产生的模板类都是不同的类型,直接用此模板类为参数就不能支持多种类型了。int OperateOnFunctor(int i, Functor *pFunc) { if(pFunc) return (*pFunc)(i); else return0; } 这是一个简单的类,它包含了一个符合函数对象要求的函数-以一个int为参数并返回一个int。注意此函数还用到了一个该类的数据成员,这说明这个回调函数实际应该是在实例对象的环境下被执行的, 所以引用同一类不同对象的函数对象会产生不同的结果:class ClassA { public: ClassA(int i) { m_Value = i; } int FuncA(int i) { return (m_Value - i); } int m_Value; };