zoukankan      html  css  js  c++  java
  • Functor仿函数

    转载声明:本文转自网络,稍加整理以备学习和參考之用。

    函数对象/仿函数

    提到C++ STL。首先被人想到的是它的三大组件:Containers, Iterators, Algorithms,即容器,迭代器和算法。容器为用户提供了经常使用的数据结构,算法大多是独立于容器的经常使用的基本算法,迭代器是由容器提供的一种接口。算法通过迭代器来操控容器。接下来要介绍的是另外的一种组件。函数对象(Function Object,JJHou译作Functor仿函数)。

    什么是函数对象

      顾名思义,函数对象首先是一个对象,即某个类的实例

    其次,函数对象的行为和函数一致,即是说能够像调用函数一样来使用函数对象,如參数传递、返回值等。

    这样的行为是通过重载类的()操作符来实现的

    仿函数:用途和适用的场合
      之所以要开发仿函数(functors),是由于函数不能容纳不论什么有意义的状态。

    比如。使用函数,你不能为某个元素加一个随意值。再将其应用于一个范围。可是,使用仿函数可轻易做到这一点。

    举个栗子。。。

    #include <iostream>
    #include <algorithm>
    using namespace std;
    //回调函数
    void call_back(char elem)
    {
     cout << elem << endl;
    }
    //仿函数
    struct Functor
    {
     void operator() (char elem) 
     {
      cout << elem << endl;
     
    };
    int main()
    {
     string strA = "hello";
     string strB = "world";
      
     for_each(strA.begin(),strA.end(),Functor());
     cout<<"===========GAP==============="<<endl;
     for_each(strB.begin(),strB.end(),call_back);
     getchar();
     return 0;
    }

    h
    e
    l
    l
    o
    ===========GAP===============
    w
    o
    r
    l
    d

    可能会有疑问两者有什么差别?

    假如我要for_each遍历的不是字符串而是int类型的vector呢?

    是不是又要重写一个int类型作为參数的回调函数,那假设有N种类型的容器遍历岂不是要写N个回调函数或N个仿函数类?

    非也!!!

    C++有类模板 也有 函数模板 相同能够用于回调

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    #include <iostream>
    #include <algorithm>
    #include <vector>
    using namespace std;
    //模板函数
    template<typename T>
    void call_back(T elem)
     cout<< elem <<endl;
    }
    //仿函数
    template<typename T>
    class Functor
    {
    public:
     Functor()
      :m_val(0)
     {
      cout<< "Functor()" <<endl;
     }
     ~Functor()
     {
      cout<<"~Functor()"<<endl;
     }
     void operator() (T elem) 
     {
      Do(elem);
     }
      
     //举个栗子
     void Do(T elem)
     {
      m_val+=elem;
      cout<<elem<<"/"<<m_val<<endl;
     }
    private:
     T m_val;
    };
      
    int main()
    {
     vector<int> vec;
     vec.push_back(1);
     vec.push_back(2);
     vec.push_back(3);
     vec.push_back(4);
     vec.push_back(5);
     for_each(vec.begin(),vec.end(),call_back<int>);
     cout<<"===========GAP==============="<<endl; 
     for_each(vec.begin(),vec.end(),Functor<int>());
     return 0;
    }

    1
    2
    3
    4
    5
    ===========GAP===============
    Functor()
    1/1
    2/3
    3/6
    4/10
    5/15
    ~Functor()
    ~Functor()
    ~Functor()

     

    三次析构的原因:

    先附上for_each的源代码(VC2008)

    ?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template<class _InIt,class _Fn1>
    inline _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
    // perform function for each element
     _DEBUG_RANGE(_First, _Last);
     _DEBUG_POINTER(_Func);
     _CHECKED_BASE_TYPE(_InIt) _ChkFirst(_CHECKED_BASE(_First));
     _CHECKED_BASE_TYPE(_InIt) _ChkLast(_CHECKED_BASE(_Last));
     for (; _ChkFirst != _ChkLast; ++_ChkFirst)
      _Func(*_ChkFirst);
     return (_Func);
    }
    ?
    1
    2
    3
    4
    5
    Functor<int>() 产生暂时对象传參(值) 构造一次,析构一次 
    for_each參数值传递,拷贝构造一次,析构一次(函数内部)
    for_each返回仿函数的对象(值),拷贝构造一次,析构一次
    由于没有重载拷贝构造函数 所以打印出第一次创建暂时对象时的普通构造函数
    实际上在这个过程中一共产生过三个仿函数对象

     

    假设把代码改变下:

    ?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    #include <iostream>
    #include <algorithm>
    #include <vector>
    using namespace std;
     
    template<typename T>
    class Functor
    {
    public:
     Functor()
      :m_val(0)
     {
      cout<< "Functor()"<<this<<endl;
     }
     Functor(Functor& that)
     {
      this->m_val = that.m_val;
      cout<< "Copy Functor()" <<this<<endl;
     }
     ~Functor()
     {
      cout<<"~Functor()"<<this<<endl;
     }
     void operator() (T elem) 
     {
      Do(elem);
     }
      
     //举个栗子
     void Do(T elem)
     {
      m_val+=elem;
      cout<<elem<<"/"<<m_val<<endl;
     }
     T getVal()
     {
      return m_val;
     }
    private:
     T m_val;
    };
      
    int main()
    {
     vector<int> vec;
     vec.push_back(1);
     vec.push_back(2);
     vec.push_back(3);
     vec.push_back(4);
     vec.push_back(5);
     
     Functor<int> func;
     Functor<int>& ref = for_each(vec.begin(),vec.end(),func);
     cout<<ref.getVal()<<endl;
      
     return 0;
    }

    执行结果

    Functor()0032F800           //main函数中的实參仿函数对象
    Copy Functor()0032F68C  //值传递 对【实參对象】拷贝构造了形參对象

    1/1
    2/3
    3/6
    4/10
    5/15
    Copy Functor()0032F7E8 //返回对象的值类型  对【形參对象】拷贝构造

    ~Functor()0032F68C       //析构形參对象
    15
    ~Functor()0032F7E8       //析构返回值对象
    ~Functor()0032F800      //析构实參对象

    如今一目了然了吧!

     

    使用回调函数高效 由上面的样例能够看出 构造1次 拷贝构造2次 析构3次  是有代价的

     

    最后回到仿函数和回调函数

    差别在于:

    1. 使用仿函数能够声明在业务相关的类内部 缩小作用域

    2. 使用仿函数能够使用类的成员属性和成员函数

    3. 仿函数是一个 能够使用面向对象的各种机制(封装 继承 多态)

    4. 若使用回调函数 那么仅仅能声明为某个类的静态成员函数或全局函数。使用类内部的资源须要用一些手段传參,没有直接使用成员函数便捷

  • 相关阅读:
    VIM中去除Windows文本中的^M符号,多种方法
    LAMP=LINUX+Apache+MySQL+PHP
    个人常用的VIM命令列表
    vim与windows/linux之间的复制粘贴小结
    eclipse中安装vim插件
    关闭VIM/VI自动备份
    linux一些个非官方网站
    程序员的编辑器
    linux和windows双系统互拷文件乱码问题
    Linux ubuntu lamp安装配置环境phpmyadmin
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/5085125.html
Copyright © 2011-2022 走看看