zoukankan      html  css  js  c++  java
  • c++谓词函数和仿函数

      什么谓词,其实就是一个判断式,说白了就是一个返回bool值的函数或者仿函数。(这里说明了谓词可以有2种形式)几元就是函数有几个参数,至于定义和使用,函数定义和一般的函数定义一样仿函数就是写个类,然后重载operator()使用就是在那些以这种需要返回bool值的函数作参数的函数里用了。
     

    一元谓词函数举例如下:

    1,判断给出的string对象的长度是否小于6
    bool GT6(const string &s)
    {
     return s.size() >= 6;
    }
    2,判断给出的int是否在3到8之间
    bool Compare( int i )   
    {   
         return ( i >= 3 && i <= 8 );   
    }
     
    二元谓词举例如下:
    1,比较两个string对象,返回一个bool值,指出第一个string是否比第二个短
    bool isShorter(const string &s1, const string &s2)
    {
         return s1.size() < s2.size();
    }
     
      stl 中_if 后缀的函数都是需要一个谓词的, 但是stl 提供给我们的 可以自己写的谓词函数有一个限制,那就是比较的对象必须是个常量, 且必须在函数里面出现, 这样就带了一个问题, 比如说,我们需要找出保存在容器中的string 对象长度大于6的个数,按照stl 给我们提供的简单的谓词形式,我们可以定义一个谓词,比如:bool Great6(int i){return i >6;},但是如果说在你项目中我们传入的界限是个变量, 比如说找出长度大于n的个数, 这种方法就行不通了。下面有几种方法:
     
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <functional>
    using namespace std;
    
    /* 方法一
     *对应内置对象,函数里面处理的是常数
     * 比如:找出容器里面大于6的数的个数
     **********************************/
    bool Great6(int i)
    {
            return i >6;
    }
    
    bool Great10(int i)
    {
            return i > 10;
    }
    
    /*方法二
     *利用函数对象,封装类,有特定名字叫仿函数,重载operator()
     * 找出容器里面大于给定数的个数,比如说找出大于8的个数
     *****************************************************/
    class GreatInt
    {
    
    public:
             GreatInt(int i): m_i(i){}
             bool operator() (int i)
             {
                     return i >m_i;
             }
    private:
            int m_i;
    };
    
    /*方法三
     *利用函数对象的函数适配器
     *参数n表示比较的数
     ***************************/
    int GreatN(vector<int>::iterator beg, vector<int>::iterator end, int n)
    {
       return count_if(beg, end, bind2nd(greater<int>(), n));
    }
    
    int main()
    {
    
            vector<int> ivec;
        
            for(int i =0 ; i != 10; ++i)
        ivec.push_back(i);
    
            int iFind = 11;
            cout<<"第一种方法输出结果:"<<endl;
            int iCount1 = count_if(ivec.begin(), ivec.end(), Great6);
            cout<<iCount1<<endl;
            iCount1 = count_if(ivec.begin(), ivec.end(), Great10);
            cout<<iCount1<<endl;
        int iCount2;
            cout<<"第二种方法输出结果:"<<endl;
            iCount2 = count_if(ivec.begin(), ivec.end(), GreatInt(6));
            cout<<iCount2<<endl;
            iCount2 = count_if(ivec.begin(), ivec.end(),GreatInt(10));
            cout<<iCount2<<endl;
            cout<<"第三种方法输出结果:"<<endl;
            cout<<GreatN(ivec.begin(), ivec.end(), 6)<<endl;
            cout<<GreatN(ivec.begin(), ivec.end(), 10)<<endl;
            
    
            return 0;
    
    }

    所谓的仿函数(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

    总结,谓词函数有3中写法:

    class TestIndex{  
    public:  
        int index;  
        TestIndex(){  
        }  
        TestIndex(int _index):index(_index){  
        }  
        bool operator()(const TestIndex* t1,const TestIndex* t2){  
            printf("Operator():%d,%d/n",t1->index,t2->index);  
            return t1->index < t2->index;  
        }  
        bool operator < (const TestIndex& ti) const {  
            printf("Operator<:%d/n",ti.index);  
            return index < ti.index;  
        }  
    };  
    bool compare_index(const TestIndex* t1,const TestIndex* t2){  
        printf("CompareIndex:%d,%d/n",t1->index,t2->index);  
        return t1->index < t2->index;  
    }  

    注意,重载operator()要带()。如sort(a,a+len,TestIndex());

     程序中定义了一个仿函数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++的引用版。

     


     
  • 相关阅读:
    Mysql 服务无法启动 服务没有报告任何错误
    mysql国内镜像下载网址
    windows上自动设置java环境变量的脚本
    史上最详细的新浪广告系统技术架构优化历程
    十分钟理解广告系统
    Nginx基础配置指令
    nginx配置详情(总结)
    利用tcpdump抓包工具监控TCP连接的三次握手和断开连接的四次挥手
    Windows7配置QT-Android开发环境!
    一位计算机专业硕士毕业生的求职经历和感想[转载]
  • 原文地址:https://www.cnblogs.com/youxin/p/2582530.html
Copyright © 2011-2022 走看看