C++之仿函數

最近再看STL源碼的時候看到裏面的實現用了大量的仿函數,然後上網蒐集了一些關於仿函數的知識。

仿函數(Functor)又稱爲函數對象(Function Object)是一個能行使函數功能的類。仿函數的語法幾乎和我們普通的函數調用一樣,不過作爲仿函數的類,都必須重載 operator() 運算符。因爲調用仿函數,實際上就是通過類對象調用重載後的 operator() 運算符。

如果編程者要將某種“操作”當做算法的參數,一般有兩種方法:
(1)一個辦法就是先將該“操作”設計爲一個函數,再將函數指針當做算法的一個參數。比如說我們定義一個排序函數,比較函數操作可以設計一個函數,利用函數指針作爲參數傳遞給排序函數
(2)將該“操作”設計爲一個仿函數(就語言層面而言是個 class),再以該仿函數產生一個對象,並以此對象作爲算法的一個參數,接下來我們就詳細講一下如何設計仿函數。

如下是定義了一個比較仿函數:
 

class comp
{
public:
    explicit comp(int t):a(t){}//顯式構造函數
    bool operator()(int num) const//const放前面表示這個函數的返回值是不可修改的,放後面表示這個函數不修改當前對象的成員。
    {
        return num>a;
    }
private:
    const int a;
};
int count(int *start,int *end,comp a)
{
    int *b=start;
    int c=0;
    while(b<=end)
    {
        c+=a(*b)?1:0;
        b++;
    }
    return c;
}
int main()
{
    comp a(10);
    cout<<a(11)<<endl;
    cout<<a(9)<<endl;
    int aa[4]={0,10,20,30};
    int result=count(aa,aa+3,a);
    cout<<result<<endl;
    return 0;
}

運行結果:

1
0
2
按 <RETURN> 來關閉窗口...

STL中涉及到了大量的仿函數,一般都用模板實現

1、STL中基礎仿函數

1、仿函數定義自己型別
算法內部可能需要使用仿函數返回值或者輸出值的類型參數,因此定義兩個類。

//一元函數的參數和返回值類型,通常被繼承
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;
};  

上述兩個類分別定義了一元和二元函數參數和返回值型別,對應的仿函數類僅僅需要繼承此類即可。

2、算術類仿函數
標準庫給我們定義了一些通用的仿函數,可以直接調用,生成對象,面向用戶。

//////算術類仿函數 + - * / %////////////////////////////////
//plus仿函數,生成一個對象,裏面僅僅有一個函數重載的方法。
template <class T>
struct plus : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x + y; }
};

//minus仿函數
template <class T>
struct minus : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x - y; }
};

template <class T>
struct multiplies : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x * y; }
};

template <class T>
struct divides : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x / y; }
};
template <class T>
struct modulus : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x % y; }
};

//取負值
template <class T>
struct negate : public unary_function<T, T> {
    T operator()(const T& x) const { return -x; }
};

單獨使用仿函數,通常將仿函數和算法部分單獨分開使用。

#include <iostream>     // std::cout
#include <functional>   // std::plus
#include <algorithm>    // std::transform
using namespace std;
int main(void)
{
    cout << minus<int>()(10,5) << endl;//5
    cout << multiplies<int>()(10,5) << endl;//50
    cout << divides<int>()(10,5) << endl;//2
    cout << modulus<int>()(10,5) << endl;//0
    cout << negate<int>()(10) << endl;//-10
    return 0;
}

運行結果:

5
50
2
0
-10

3、關係運算符仿函數

//關係運算符仿函數////////////////////////////////////////////
// x==y 仿函數
template <class T>
struct equal_to : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x == y; }
};

// x!=y 仿函數
template <class T>
struct not_equal_to : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x != y; }
};
// x>y 仿函數
template <class T>
struct greater : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x > y; }
};
// x<y 仿函數
template <class T>
struct less : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x < y; }
};

// x>=y 仿函數
template <class T>
struct greater_equal : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x >= y; }
};
// x<=y 仿函數
template <class T>
struct less_equal : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x <= y; }
};
// sort algorithm example
#include <iostream>     // std::cout
#include <algorithm>    // std::sort
#include <vector>       // std::vector
#include <functional>   // std::

bool myfunction (int i,int j) { return (i < j); }

int main () {
  int myints[] = {32,71,12,45,26,80,53,33};
  std::vector<int> myvector (myints, myints+8);               // 32 71 12 45 26 80 53 33

  // using default comparison (operator <):
  std::sort (myvector.begin(), myvector.begin()+4);           //(12 32 45 71)26 80 53 33

  // using function as comp
  std::sort (myvector.begin()+4, myvector.end(), myfunction); // 12 32 45 71(26 33 53 80)

  // using object as comp
  std::sort (myvector.begin(), myvector.end(), std::less<int>());     //(12 26 32 33 45 53 71 80)

  // print out content:
  std::cout << "myvector contains:";
  for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}

 運行結果:

myvector contains:12 26 32 33 45 53 71 80

4、邏輯運算符仿函數

template <class T>
struct logical_and : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x && y; }
};

template <class T>
struct logical_or : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x || y; }
};

template <class T>
struct logical_not : public unary_function<T, bool> {
    bool operator()(const T& x) const { return !x; }
};

5、證同、選擇、投射仿函數

///////////////////////////////////////////////////////////////////////////////////////////
//證同仿函數,主要用於RB或者hashmap裏面 key = value情況
template <class T>
struct identity : public unary_function<T, T> {
  const T& operator()(const T& x) const { return x; }
};

//選擇仿函數,主要用與RB和hashmap裏面key 不爲value情況,從pair種取出key
template <class Pair>
struct select1st : public unary_function<Pair, typename Pair::first_type> {
  const typename Pair::first_type& operator()(const Pair& x) const
  {
    return x.first;
  }
};

//選擇仿函數,主要用與RB和hashmap裏面key 不爲value情況,從pair種取出value
template <class Pair>
struct select2nd : public unary_function<Pair, typename Pair::second_type> {
  const typename Pair::second_type& operator()(const Pair& x) const
  {
    return x.second;
  }
};

//投射函數,輸入x和y返回x
template <class Arg1, class Arg2>
struct project1st : public binary_function<Arg1, Arg2, Arg1> {
  Arg1 operator()(const Arg1& x, const Arg2&) const { return x; }
};
//投射函數,輸入x和y返回y
template <class Arg1, class Arg2>
struct project2nd : public binary_function<Arg1, Arg2, Arg2> {
  Arg2 operator()(const Arg1&, const Arg2& y) const { return y; }
};
/////////////////////////////////////////////////////////////////////

3、STL中仿函數適配器

仿函數適配器是通過將上述仿函數重新配置成含有新功能的模板函數。

1、對仿函數返回值進行否定適配器
傳入仿函數對象即可,和以前一樣使用,僅僅包裝了一下子而已。

//否定一元返回值
//模板參數傳入仿函數類
template <class Predicate>
class unary_negate
  : public unary_function<typename Predicate::argument_type, bool> {
protected:
  Predicate pred;//對象
public:
  explicit unary_negate(const Predicate& x) : pred(x) {}
  bool operator()(const typename Predicate::argument_type& x) const {
    return !pred(x);//這裏是調用的關鍵
  }
};
//輔助函數,使得我們方便使用unary_negate<Pred>
//傳入對象,並返回臨時對象。
template <class Predicate>
inline unary_negate<Predicate> not1(const Predicate& pred) {
  return unary_negate<Predicate>(pred);//返回臨時對象
}
//輔助函數,識別傳入對象,通過模板萃取其模板型別,然後聲明模板聲明臨時對象並用傳入對象初始化。
/////////////////////////////////////////////////////////////////////////
//否定二元返回值
template <class Predicate> 
class binary_negate 
  : public binary_function<typename Predicate::first_argument_type,
                           typename Predicate::second_argument_type,
                           bool> {
protected:
  Predicate pred;
public:
  explicit binary_negate(const Predicate& x) : pred(x) {}
  bool operator()(const typename Predicate::first_argument_type& x, 
                  const typename Predicate::second_argument_type& y) const {
    return !pred(x, y); 
  }
};
template <class Predicate>
inline binary_negate<Predicate> not2(const Predicate& pred) {
  return binary_negate<Predicate>(pred);
}
//not1 example
#include <iostream>     // std::cout
#include <functional>   // std::not1
#include <algorithm>    // std::count_if

struct IsOdd {
  bool operator() (const int& x) const {return x%2 == 1;}
  typedef int argument_type;
};//類

int main () {
  int values[] = {1,2,3,4,5};
  int cx = std::count_if( values, values+5, std::not1(IsOdd()) );//找出不是奇數的個數
  //IsOdd()產生臨時對象a,not1返回臨時對象並用a初始化。
  std::cout << "There are " << cx << " elements with even values.\n";
  return 0;
}

輸出:
There are 2 elements with even values.

// not2 example
#include <iostream>     // std::cout
#include <functional>   // std::not2, std::equal_to
#include <algorithm>    // std::mismatch
#include <utility>      // std::pair

int main () {
  int foo[] = {10,20,30,40,50};
  int bar[] = {0,15,30,45,60};

  std::pair<int*,int*> firstmatch,firstmismatch;

  firstmismatch = std::mismatch (foo, foo+5, bar, std::equal_to<int>());//返回第一個不匹配數值

  firstmatch = std::mismatch (foo, foo+5, bar, std::not2(std::equal_to<int>()));//返回第一個匹配的數值

  std::cout << "First mismatch in bar is " << *firstmismatch.second << '\n';
  std::cout << "First match in bar is " << *firstmatch.second << '\n';
  return 0;
}

輸出:
First mismatch in bar is 0
First match in bar is 30

2、將仿函數某個參數綁定爲固定值的適配器

//////////////////////////////////////綁定參數,將二元函數某個參數綁定爲恆定值///////////////////////////////////////

//Operation前面講解的仿函數類
template <class Operation> 
class binder2nd
  : public unary_function<typename Operation::first_argument_type,
                          typename Operation::result_type> {
protected:
  Operation op;//仿函數對象
  typename Operation::second_argument_type value;//第二個參數類型
public:
  binder2nd(const Operation& x,
            const typename Operation::second_argument_type& y) 
      : op(x), value(y) {}//傳入x是對象的引用,第二參數的引用
  typename Operation::result_type
  operator()(const typename Operation::first_argument_type& x) const {//傳入x,底層調用op
    return op(x, value);//將value綁定爲op的第二個參數 
  }
};

//輔助函數,輔助產生綁定好的對象 bind2nd
template <class Operation, class T>
inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) {
  typedef typename Operation::second_argument_type arg2_type;
  return binder2nd<Operation>(op, arg2_type(x));//僅僅產生臨時對象,可以傳給模板函數
}



//第一個參數綁定起來
template <class Operation> 
class binder1st
  : public unary_function<typename Operation::second_argument_type,
                          typename Operation::result_type> {
protected:
  Operation op;//操作
  typename Operation::first_argument_type value;//第一個參數類型
public:
  binder1st(const Operation& x,
            const typename Operation::first_argument_type& y)
      : op(x), value(y) {}//構造
  typename Operation::result_type
  operator()(const typename Operation::second_argument_type& x) const {
    return op(value, x); 
  }
};

//輔助函數調用進行
template <class Operation, class T>
inline binder1st<Operation> bind1st(const Operation& op, const T& x) {
  typedef typename Operation::first_argument_type arg1_type;
  return binder1st<Operation>(op, arg1_type(x));
}
//////////////////////////////////////////////////////////////////////////////
// binder2nd example
#include <iostream>
#include <functional>
#include <algorithm>
using namespace std;

int main () {
  int numbers[] = {10,-20,-30,40,-50};
  int cx;
  int cx1;
  binder2nd< less<int> > IsNegative (less<int>(),0);//將less<int>重新包裝產生新的對象binder2nd
  cx = count_if (numbers,numbers+5 , IsNegative);//二者用法一樣
  cx1 = count_if (numbers,numbers+5,bind2nd(less<int>() , 0));
  cout << "There are " << cx <<"  "<< cx1 << " negative elements.\n";
  return 0;
}

輸出結果:

There are 3 3 negative elements.

3、將兩個仿函數合併成一個仿函數的適配器

///////////////////////////////用於函數合成/////////////////////////////////////////////////
//一元仿函數合成操作
//h(x) = f( g(x) )
template <class Operation1, class Operation2>
class unary_compose : public unary_function<typename Operation2::argument_type,
                                            typename Operation1::result_type> {
protected:
  Operation1 op1;
  Operation2 op2;
public:
  unary_compose(const Operation1& x, const Operation2& y) : op1(x), op2(y) {}
  typename Operation1::result_type
  operator()(const typename Operation2::argument_type& x) const {
    return op1(op2(x));//類似f(g(x))
  }
};

template <class Operation1, class Operation2>
inline unary_compose<Operation1, Operation2> compose1(const Operation1& op1, 
                                                      const Operation2& op2) {
  return unary_compose<Operation1, Operation2>(op1, op2);//返回臨時對象
}

//二元仿函數合成操作
//h(x) = f( g1(x) , g2(x) )
template <class Operation1, class Operation2, class Operation3>
class binary_compose
  : public unary_function<typename Operation2::argument_type,
                          typename Operation1::result_type> {
protected:
  Operation1 op1;
  Operation2 op2;
  Operation3 op3;
public:
  binary_compose(const Operation1& x, const Operation2& y, 
                 const Operation3& z) : op1(x), op2(y), op3(z) { }
  typename Operation1::result_type
  operator()(const typename Operation2::argument_type& x) const {
    return op1(op2(x), op3(x));//返回臨時對象
  }
};

template <class Operation1, class Operation2, class Operation3>
inline binary_compose<Operation1, Operation2, Operation3> 
compose2(const Operation1& op1, const Operation2& op2, const Operation3& op3) {
  return binary_compose<Operation1, Operation2, Operation3>(op1, op2, op3);
}

4、將函數指針合併成仿函數的適配器

///////////////////////////////用於函數指針/////////////////////////////////////////////////

//將一元函數指針包裝成仿函數
template <class Arg, class Result>
class pointer_to_unary_function : public unary_function<Arg, Result> {
protected:
  Result (*ptr)(Arg);//函數指針變量
public:
  pointer_to_unary_function() {}
  explicit pointer_to_unary_function(Result (*x)(Arg)) : ptr(x) {}
  Result operator()(Arg x) const { return ptr(x); }
};

template <class Arg, class Result>
inline pointer_to_unary_function<Arg, Result> ptr_fun(Result (*x)(Arg)) {
  return pointer_to_unary_function<Arg, Result>(x);//傳入函數指針、一元參數類型、返回值類型,返回一個仿函數對象
}

/////////////////////////////////////////////////////////

//將二元函數指針包裝成仿函數
template <class Arg1, class Arg2, class Result>
class pointer_to_binary_function : public binary_function<Arg1, Arg2, Result> {
protected:
    Result (*ptr)(Arg1, Arg2);
public:
    pointer_to_binary_function() {}
    explicit pointer_to_binary_function(Result (*x)(Arg1, Arg2)) : ptr(x) {}
    Result operator()(Arg1 x, Arg2 y) const { return ptr(x, y); }
};

template <class Arg1, class Arg2, class Result>
inline pointer_to_binary_function<Arg1, Arg2, Result> 
ptr_fun(Result (*x)(Arg1, Arg2)) {
  return pointer_to_binary_function<Arg1, Arg2, Result>(x);//返回對象即可
}
// ptr_fun example
#include <iostream>
#include <functional>
#include <algorithm>
#include <cstdlib>
#include <numeric>
using namespace std;

int main () {
  char* foo[] = {"10","20","30","40","50"};
  int bar[5];
  int sum;
  transform (foo, foo+5, bar, ptr_fun(atoi) );//將函數指針轉換成仿函數對象,這裏輸入函數指針效果一樣
  transform (foo, foo+5, bar, atoi );
  sum = accumulate (bar, bar+5, 0);
  cout << "sum = " << sum << endl;
  return 0;
}

輸出:
sum = 150

5、將成員函數指針提取出來包裝成仿函數適配器

//函數指針類別:返回S 無輸入  通過指針調用
template <class S, class T>
class mem_fun_t : public unary_function<T*, S> {
public:
  explicit mem_fun_t(S (T::*pf)()) : f(pf) {}//初始化
  S operator()(T* p) const { return (p->*f)(); }//調用,p裏面對應的函數
private:
  S (T::*f)();//這是一個變量,這個函數指針變量
};
//輔助函數,直接通過模板萃取相應的型別,然後聲明相應的對象
template <class S, class T>
inline mem_fun_t<S,T> mem_fun( S (T::*f)() ) { 
  return mem_fun_t<S,T>(f);//返回仿函數臨時對象,真的很牛逼哦抽象出來了
}
//小例子
size_type length() const 
{ return _M_string_length; }
mem_fun(&string::length);//傳入函數指針,::優先級大於&
//s 是 size_type  T是string  f是length,通過模板萃取出這些型別,即可產生相應的對象。


//有一個參數,通過指針調用
template <class S, class T, class A>
class mem_fun1_t : public binary_function<T*, A, S> {
public:
  explicit mem_fun1_t(S (T::*pf)(A)) : f(pf) {}
  S operator()(T* p, A x) const { return (p->*f)(x); }
private:
  S (T::*f)(A);
};

template <class S, class T, class A>
class const_mem_fun1_t : public binary_function<const T*, A, S> {
public:
  explicit const_mem_fun1_t(S (T::*pf)(A) const) : f(pf) {}
  S operator()(const T* p, A x) const { return (p->*f)(x); }
private:
  S (T::*f)(A) const;
};
// mem_fun example
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;

int main () {
  vector <string*> numbers;

  // populate vector of pointers:
  numbers.push_back ( new string ("one") );
  numbers.push_back ( new string ("two") );
  numbers.push_back ( new string ("three") );
  numbers.push_back ( new string ("four") );
  numbers.push_back ( new string ("five") );

  vector<int> lengths(numbers.size());//預先分配內存空間

  transform (numbers.begin(), numbers.end(), lengths.begin(), mem_fun(&string::length));

  for (int i=0; i<5; i++) {
    cout << *numbers[i] << " has " << lengths[i] << " letters.\n";
  }

  // deallocate strings:
  for (vector<string*>::iterator it = numbers.begin(); it!=numbers.end(); ++it)
    delete *it;

  return 0;
}

輸出:
one has 3 letters.
two has 3 letters.
three has 5 letters.
four has 4 letters.
five has 4 letters.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章