• STL学习总结之<仿函数>


    1 仿函数的概念

    仿函数,又名函数对象,是一个定义了operator ()的对象。仿函数的主要功能代码在仿函数类的operator ()体内完成。仿函数的妙处:

    (1) 仿函数比一般函数更灵巧,可以用有状态,对于仿函数可以同时拥有两个状态的不同实体。

    (2) 每个仿函数都有其型别,通过传递不同型别的仿函数当作template参数给容器,可以构造出型别不同的容器。

    (3) 执行速度上,仿函数通常比函数指针快。

    很多stl算法有一个函数参数,例如remove_if,for_each等,这个函数可以是普通的全局函数,仿函数,类的成员函数(非static,static可以作为全局函数使用),类的成员函数比较特殊,需要使用适配器函数mem_fun/mem_fun_ref包装才可以。 

    2 仿函数的状态

    仿函数可以拥有内部状态,但算法并不会改变随参数而来的仿函数的状态。仿函数是以passed by value(传值),不是passed by reference(传址)。这样的好处是可以传递常量和暂时表达式,缺点是无法存取仿函数的最终状态,因为算法内部改变的是仿函数的副本的状态。得到仿函数最终状态的两种方法:

    (1) 以by reference的方式传递函数(eg1)。

    (2) 运用for_each()算法的返回值(eg2)。如果for_each中的第三个参数是仿函数则for_each返回该仿函数的副本;如果for_each中的第三个参数是一个全局函数则返回一个函数指针。

    eg1:

    View Code
     1 #include<iostream>
     2 #include<vector>
     3 #include<algorithm>
     4 using namespace std;
     5 class INIT
     6 {
     7 private:
     8     int value;
     9 public:
    10     INIT(int m):value(m){}
    11     int operator() ()
    12     {
    13         return value++;
    14     }
    15 };
    16 class PRINT
    17 {
    18 public:
    19     void operator() (int value)
    20     {
    21         cout<<value<<" ";
    22     }
    23 };
    24 
    25 int main()
    26 {
    27     vector<int>vec;
    28     INIT init(1);
    29     generate_n<back_insert_iterator<vector<int> >,int,INIT&>(back_inserter(vec),5,init);
    30     for_each(vec.begin(),vec.end(),PRINT());
    31     cout<<endl;
    32     generate_n(back_inserter(vec),5,INIT(42));
    33     for_each(vec.begin(),vec.end(),PRINT());
    34     cout<<endl;
    35     generate_n(back_inserter(vec),5,init);
    36     for_each(vec.begin(),vec.end(),PRINT());
    37     cout<<endl;
    38     return 0;
    39 }
    40 输出:
    41 1 2 3 4 5
    42 1 2 3 4 5 42 43 44 45 46
    43 1 2 3 4 5 42 43 44 45 46 6 7 8 9 10

    eg2:

    View Code
     1 #include<iostream>
     2 
     3 #include<vector>
     4 
     5 #include<algorithm>
     6 
     7 #include<iterator>
     8 
     9 using namespace std;
    10 
    11 class INIT
    12 
    13 {
    14 
    15 private:
    16 
    17          int value;
    18 
    19 public:
    20 
    21          INIT(int m):value(m){}
    22 
    23          int operator() ()
    24 
    25          {
    26 
    27                    return value++;
    28 
    29          }
    30 
    31 };
    32 
    33  
    34 
    35 class MeanValue
    36 
    37 {
    38 
    39 private:
    40 
    41          long num;
    42 
    43          long sum;
    44 
    45 public:
    46 
    47          MeanValue():num(0),sum(0){}
    48 
    49          void operator() (int value)
    50 
    51          {
    52 
    53                    num++;
    54 
    55                    sum+=value;
    56 
    57          }
    58 
    59          operator double()//类型转换函数
    60 
    61          {
    62 
    63                    return static_cast<double>(sum)/static_cast<double>(num);
    64 
    65          }
    66 
    67 };
    68 
    69  
    70 
    71 int main()
    72 
    73 {
    74 
    75          vector<int> coll;
    76 
    77          generate_n(back_inserter(coll),10,INIT(1));
    78 
    79          double mv = for_each(coll.begin(),coll.end(),MeanValue());//for_each返回仿函数类型
    80 
    81          //MeanValue mv = for_each(coll.begin(),coll.end(),MeanValue());//这样写也可以,输出时会                                                                                                                           自动将mv转换成double
    82 
    83          cout<<"mean value: "<<mv<<endl;
    84 
    85          return 0;
    86 
    87 }

    3 判断式

    所谓判断式就是一个返回布尔值的一个函数或仿函数。注:最好总是将判断式的operator()声明为const成员函数,应保证判断式不应该因为调用而改变自身状态,判断式的副本应该和其正本有着相同的状态。

    4 预定义的仿函数

    要使用这些函数必须包含<functional>

    仿函数

    效果

    negate<type>()

    - param

    plus<type>()

    param1 + param2

    minus<type>()

    param1 - param2

    multiplies<type>()

    param1 * param2

    divides<type>()

    param1 / param2

    modulus<type>()

    param1 % param2

    equal_to<type>()

    param1 == param2

    not_equal_to<type>(0

    param1 != param2

    less<type>()

    param1 < param2

    greater<type>()

    param1 > param2

    less_equal<type>()

    param1 <= param2

    greater<type>()

    param1 > param2

    less_equal<type>()

    param1 <= param2

    greater_equal<type>()

    param1 >= param2

    logical_not<type>()

    ! param

    logical_and<type>()

    param1 && param2

    logical_or<type>()

    param1 || param2

    5 函数配接器

    所谓函数配接器是指能够将仿函数和另一个函数(或某个值,或某个一般函数)结合起来的仿函数。函数配接器也声明在<functional>。例如:

    find_if(coll.begin(),coll.end(),bind2nd(greater<int>(),42));

    其中的表达式bind2nd(greater<int>(),42)导致一个组合型仿函数,检查某个int值是否大于42,bind2nd可以将一个二元仿函数转换成一个一元仿函数,它通常将第二个参数传给由第一个参数指出的二元仿函数,作为后者的第二参数。

    预定义的函数配接器:

    表达式

    效果

    bind1st(op, value)

    op(value, param)

    bind2nd(op, value)

    op(param, value)

    not1(op)

    !op(param)

    not2(op)

    !op(param1,param2)

     

    5.1 针对成员函数而设计的函数配接器

    在算法中使用类的成员函数。

    C++标准程序库提供了一些额外的函数配接器,透过它们可以针对群集内的每个元素调用其成员函数。成员函数配接器:

    表达式

    效果

    mem_fun_ref(op)

    调用op,那是某对象的一个成员函数(const和非const)

    mem_fun(op)

    调用op,op是某对象指针的成员函数(const和非const)

    不能直接把一个对象的成员函数传给一个算法,必须用配接器。算法对传入的指针调用的是operator(),而不是调用该指针所指的成员函数,配接器mem_fun_ref和mem_fun将operator()调用动作做了适当转换。

    eg1:

    View Code
     1 #include<iostream>
     2 
     3 #include<functional>
     4 
     5 #include<vector>
     6 
     7 #include<algorithm>
     8 
     9 using namespace std;
    10 
    11 class student
    12 
    13 {
    14 
    15 private:
    16 
    17          int num;
    18 
    19 public:
    20 
    21          student(int n):num(n){}
    22 
    23          void print()
    24 
    25          {
    26 
    27                    cout<<num<<" ";
    28 
    29          }
    30 
    31 };
    32 
    33  
    34 
    35 int main()
    36 
    37 {
    38 
    39          vector<student>vec;
    40 
    41          for(int i=0; i<10; ++i)
    42 
    43          {
    44 
    45                    vec.push_back(student(i));
    46 
    47          }
    48 
    49          for_each(vec.begin(),vec.end(),mem_fun_ref(&student::print));
    50 
    51          cout<<endl;
    52 
    53          return 0;
    54 
    55 }

    注: &student::print为指向student成员函数print的成员函数指针,其类型为:

    void (student::*) ()。

    5.2 针对一般函数(非成员函数)设计的函数配接器

    函数配接器ptr_fun()允许在其他函数配接器中使用一般函数

    eg:

    bool  check(int elem); //对元素进行检查的一般全局函数

    pos = find_if(coll.begin(), coll.end(), not1(ptr_fun(check)));//这里不能直接使用not1(check),因为要想让not1直接使用check,需要check提供一些特性型别。
    5.3 让自定义仿函数直接使用函数配接器

    自定义的仿函数要与配接器搭配使用,必须满足某些条件,必须提供一些型别成员来反映其参数和返回值的型别,C++标准程序库提供了一些结构如下:

    template<class Arg, class Result>

    struct unary_function{

             typedef Arg argument_type;

             typedef Result result_type;

    };

    template <class Arg1, class Arg2, class Result>

    struct binary_function{

             typedef Arg1 first_argument_type;

             typedef Arg2 second_argument_type;

             typedef Result result_type;

    };

    自定义仿函数只要继承上面的一个就可以与配接器搭配使用。

  • 相关阅读:
    你所不知道的mfc…mfc项目索引 &mfc调优指南 &mfc vc添加添加子功能指南
    Cu 大彻大悟内存管理 mm (update 0410)
    [转]Linux iostat监测IO状态
    linux virtual memory layout by moniskiller upload [读书笔记]
    河畔找到的 面经笔经
    【转】Linux本地磁盘(硬盘)介绍
    读写UTF8、Unicode文件
    codesmith执行时提示“调用的目标发生了异常”的处理过程经验。
    DB2表信息以及字段信息的表
    iBatis.NET获取resultMap相关数据
  • 原文地址:https://www.cnblogs.com/landy126/p/2951227.html
Copyright © 2020-2023  润新知