C++ 仿函数(functor) (转载)

转载 2011年01月19日 13:05:00

所谓的仿函数(functor),是通过重载()运算符模拟函数形为的类。
  因此,这里需要明确两点:
  1 仿函数不是函数,它是个类;

  2 仿函数重载了()运算符,使得它的对你可以像函数那样子调用(代码的形式好像是在调用函数)。

 但其实问题的本质不是在代码风格上,仿函数与回调函数各有利弊,不能一概而论。

  仿函数(functor)的优点

  我的建议是,如果可以用仿函数实现,那么你应该用仿函数,而不要用回调。原因在于:

  仿函数可以不带痕迹地传递上下文参数。而回调技术通常使用一个额外的void*参数传递。这也是多数人认为回调技术丑陋的原因。

  更好的性能。

   仿函数技术可以获得更好的性能,这点直观来讲比较难以理解。你可能说,回调函数申明为inline了,怎么会性能比仿函数差?我们这里来分析下。我们假 设某个函数func(例如上面的std::sort)调用中传递了一个回调函数(如上面的compare),那么可以分为两种情况:

  func是内联函数,并且比较简单,func调用最终被展开了,那么其中对回调函数的调用也成为一普通函数调用(而不是通过函数指针的间接调用),并且如果这个回调函数如果简单,那么也可能同时被展开。在这种情形下,回调函数与仿函数性能相同。

   func是非内联函数,或者比较复杂而无法展开(例如上面的std::sort,我们知道它是快速排序,函数因为存在递归而无法展开)。此时回调函数作 为一个函数指针传入,其代码亦无法展开。而仿函数则不同。虽然func本身复杂不能展开,但是func函数中对仿函数的调用是编译器编译期间就可以确定并 进行inline展开的。因此在这种情形下,仿函数比之于回调函数,有着更好的性能。并且,这种性能优势有时是一种无可比拟的优势(对于 std::sort就是如此,因为元素比较的次数非常巨大,是否可以进行内联展开导致了一种雪崩效应)。

  仿函数(functor)不能做的?

   话又说回来了,仿函数并不能完全取代回调函数所有的应用场合。例如,我在std::AutoFreeAlloc中使用了回调函数,而不是仿函数,这是因 为AutoFreeAlloc要容纳异质的析构函数,而不是只支持某一种类的析构。这和模板(template)不能处理在同一个容器中支持异质类型,是 一个道理。

 看下面的实例:

#include <iostream>
using namespace std;

const int CMP_LES = -1;
const int CMP_EQU = 0;
const int CMP_BIG = 1;

class Comparer
{
public:
           Comparer(int cmpType)
           {
               m_cmpType = cmpType;
           }

          bool operator () (int num1, int num2) const
           {
               bool res;

               switch(m_cmpType)
               {
               case CMP_LES:
                   res = num1 < num2;
                   break;
               case CMP_EQU:
                   res = num1 == num2;
                   break;
               case CMP_BIG:
                   res = num1 > num2;
                   break;
               default:
                   res = false;
                   break;

               }

               return res;
           }

private:
           int m_cmpType;
};

void Swap(int & num1, int & num2)
{
           int temp = num1;
           num1 = num2;
           num2 = temp;
}

void SortArray(int array[], int size, const Comparer & cmp )
{
           for (int i = 0; i < size - 1; ++i)
           {
               int indx = i;

               for (int j = i + 1; j < size; ++j)
               {
                   if (cmp(array[indx], array[j]))
                   {
                       indx = j;
                   }
               }

               if (indx != i)
               {
                   Swap(array[i], array[indx]);
               }
           }
}

void ListArray(int array[], int size)
{
           for (int i = 0; i < size; ++i)
           {
               cout << array[i] << " ";
           }
}

#define ARY_SIZE 10

int main()
{
           int array[ARY_SIZE] = {10, 12, 9, 31, 93, 34, 98, 9, 1, 20};

           cout << "The initial array is : ";
           ListArray(array, ARY_SIZE);
           cout << endl;

          SortArray(array, ARY_SIZE, Comparer(CMP_BIG));
           cout << "The ascending sorted array is :";
           ListArray(array, ARY_SIZE);
           cout << endl;

          SortArray(array, ARY_SIZE, Comparer(CMP_LES) );
           cout << "The descending sorted array is : ";
           ListArray(array, ARY_SIZE);
           cout << endl;

           return 0;
}

运行结果:

The initial array is : 10 12 9 31 93 34 98 9 1 20
The ascending sorted array is :1 9 9 10 12 20 31 34 93 98
The descending sorted array is : 98 93 34 31 20 12 10 9 9 1

  程序中定义了一个仿函数Comparer,它重重载了()运算符:
  Comparer::bool operator () (int num1, int num2) const ;
  这里温习一下运算符重载的方式:
  ret_type operator opt (array_list);
  其中,ret_type为运算符重载后返回值的类型,operator为c++运算符重载专用关健字,opt为所要重载的运算符,如+, -, *, /, [], ()...
  于是我们可以解读Comparer::bool operator ()(int num1, int num2) const的意义:
  bool限定了()的返回值为布尔类型,(int num1, int num2)指定了运算符()的参数形式,const使得应该运算符可被它的const对象调用。()运算符中根据m_cmpType值返回不同方式下两整数的比较值。

  函数void SortArray (int array[], int size, const Comparer & cmp) 用于给数组排序。其中,array[]指定所要排序的数组对象,size限定数组元素个数,cmp为Comparer对象的引用,用作对元素的比较使用, 前面使用const修饰是向函数调用都声明,在函数内不会有修改该对象任何数据的形为。注意SortArray中的代码:
           if (cmp(array[indx], array[j]))
           {
               indx = j;
           }
   其中,cmp为Comparer类的一个对象,但这里的用法好像它是某个函数的样子。这就是仿函数的真谛。

  别外,void Swap(int &num1, int &num2)完成交换num1与num2值的功能。int &num1表示函数参数使用的引用,用久了c的朋友也许更习惯了void Swap(int *num1, int *num2),但在c++中这个习惯要改了,引用和指针一样高效,但引用要比指针更直观。下面是指针版的Swap函数:
           void Swap(int *num1, int *num2)
           {
               int temp = *num1;
               *num1 = *num2;
               *num2 = temp;
           }
  实现的功能与程序中使用的一模一样,替换掉程序照样正常工作。仔细比较引用版与指针版的Swap()函数,我相信大多数人会爱上C++的引用版。

相关文章推荐

C++ 仿函数(functor)

所谓的仿函数(functor),是通过重载()运算符模拟函数形为的类。   因此,这里需要明确两点:   1 仿函数不是函数,它是个类;   2 仿函数重载了()运算符,使得它的对你可以像函数那...
  • guomei
  • guomei
  • 2011年07月21日 22:03
  • 878

c++仿函数 functor

内容整理自国外C++教材   先考虑一个简单的例子:假设有一个vector,你的任务是统计长度小于5的string的个数,如果使用count_if函数的话,你的代码可能长成这样: 1 bool...
  • pql925
  • pql925
  • 2017年05月10日 14:35
  • 62

C++回调函数(callback)与仿函数(functor)的异同

转自 http://blog.csdn.net/xushiweizh/archive/2007/03/03/1519828.aspx回调函数(callback)与仿函数(functor)很多时候从用途...

[C/C++标准库]_[初级]_[函数对象functor的使用]

Functor 场景: 1. functor其实就是

STL仿函数functor

1.仿函数的妙处(P127) 示例: //一般函数 void fo(){ statements } //仿函数 class FunctionObjectType{ public: void...

深入理解仿函数(functor或function object)

仿函数(functor),就是使一个类的使用看上去象一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。   有些功能的的代码,会在不同的成员函数中...

在STL中的functor(仿函数)的另一用处:当模板函数必须要进行偏特化时

funtor的一个用途:弥补模板函数无法偏特化的不足

c++的Functor修正版

  • 2013年12月15日 18:49
  • 929KB
  • 下载

C++ Review: Function pointer & Function object(functor)

Function Pointer must pointer to function with definition not just declaration. Consider the followi...
  • wyc1230
  • wyc1230
  • 2012年04月27日 01:26
  • 1202

【函数式】Monads模式初探——Functor

函子与范畴函子(functor)是从一个范畴到另一个范畴的转换,并且其亦可转换/保持态射(morphism)。 一个态射是从一个范畴里的一个值到同一个范畴里的另一个值的变换。在猫的范畴的例子里,一个...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++ 仿函数(functor) (转载)
举报原因:
原因补充:

(最多只允许输入30个字)