[STL]函數對象/仿函數

提到C++ STL,首先被人想到的是它的三大組件:Containers, Iterators, Algorithms,即容器,迭代器和算法。容器爲用戶提供了常用的數據結構,算法大多是獨立於容器的常用的基本算法,迭代器是由容器提供的一種接口,算法通過迭代器來操控容器。接下來要介紹的是另外的一種組件,函數對象(Function Object,JJHou譯作Functor仿函數)。

什麼是函數對象

  顧名思義,函數對象首先是一個對象,即某個類的實例。其次,函數對象的行爲和函數一致,即是說可以像調用函數一樣來使用函數對象,如參數傳遞、返回值等。這種行爲是通過重載類的()操作符來實現的,舉例說明之,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Print
{
public:
    void operator()(int n)
    {
        std::cout<<n<<std::endl;
        return ;
    }
};
int
main(int argc, char **argv)
{
    Print print;
    print(372);
    print.operator()(372); //~ 顯式調用
    return 0;
}

  其實我們早就開始使用函數對象了,當你寫下sort(v.begin(), v.end())時(假定v是vector<int>),其實調用的是sort(v.begin(), v.end(), less<int>()),這樣sort就會將v從小至大排序。若要逆向排序,你就需要顯式地爲sort指定一個排序規則,即函數對象greater<int>(). less<T>和greater<T>是STL中的兩個模板類,它們使用類型T的<和>操作符。less<T>的一個典型實現可能是這樣的:

1
2
3
4
5
6
7
8
9
template <class T>
class less
{
public:
    bool operator()(const T&l, const T&r)const
    {
        return l < r;
    }
};

函數對象的分類

  根據用途和參數特徵,STL中的函數對象通常分爲以下幾類:Predicates, Arithmetic Function Objects, Binders, Negaters, Member Function Adapters, Pointer to Function Adapters。下面逐一介紹一下,之前得先介紹兩個基類:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<class Arg, class Res>
struct unary_function //~ 一元函數對象基類
{
   typedef Arg argument_type;
   typedef Res result_type;
};
 
template<class Arg1, class Arg2, class Res>
struct binary_function //~ 二元函數對象基類
{
   typedef Arg1 first_argument_type;
   typedef Arg2 second_argument_type;
   typedef Res  result_type;
};

使用這兩個基類,首先需要包含頭文件。

Predicates

  Predicate是一種函數對象,返回值(應該是operator()的返回值)爲布爾型,接受一個或者兩個參數。通常用來判斷對象的有效性(一個參數時)或者對兩個對象進行比較(如less)。你可以根據自己的需要定義自己的Predicate,但STL已經定義了一些Predicate,你可以直接使用。

Predicates
Predicate類型描述
equal_to() Binary 使用==判等
not_equal_to() Binary 使用!=判等
less() Binary 使用<
greater() Binary 使用>
less_equal() Binary 使用<=
greater_equal() Binary 使用>=
logical_not() Unary 使用!邏輯取反
logical_and() Binary 使用&&邏輯與
logical_or() Binary 使用||邏輯或
算術運算函數對象

  進行簡單的算術運算,這類函數對象我用的很少,通常是自己定義。

算術運算函數對象
函數對象類型描述
negate() Unary 使用-求負
plus() Binary 使用+加法
minus() Binary 使用-減法
multiplies() Binary 使用*乘法
divides() Binary 使用/除法
modulus() Binary 使用%求餘
綁定Binders

  有兩種綁定bind1st和bind2nd,它們可以將一個二元函數對象的其中一個參數綁定爲某個已知的對象,從而得到一個一元函數對象。例如要在vector<int> v中查找等於372的值的位置,我可以將372綁定到equal_to<int>()的第一個或者第二個參數:

1
2
3
4
5
6
7
8
9
10
11
12
13
int
main(int argc, char **argv)
{
    vector<int> v;
    for(int i = 0; i < 1000; ++i)
    {
        v.push_back(i);
    }
    vector<int>::iterator it;
    it = find_if(v.begin(), v.end(), bind1st(equal_to<int>(), 372));
    std::cout<<*it<<std::endl;
    return 0;
}

  其實,這裏的bind1st和bind2nd並不是函數對象,只是模板函數而已。這兩個函數分別返回類型爲binder1st和binder2nd的函數對象。下面的代碼,聰明的你肯定一看就懂:

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
// bind1st
template<class Op> 
class binder1st : public unary_function
                  <typename Op::second_argument_type,
                   typename Op::result_type>
{
   Op op_;
   typename Op::first_argument_type first_arg_;
 
   public:
      binder1st(const Op& op,
                const typename Op::first_argument_type&
                first_arg) : op_(op),
               first_arg_(first_arg) {}
 
   typename Op::result_type operator()
      (const typename Op::second_argument_type& arg) const
   {
      return op_(first_arg_, arg);
   }
};
 
template<class Op, class Arg>
inline binder1st<Op> bind1st(const Op& op,
                             const Arg& arg)
{
   return binder1st<Op>(op, arg);
}
 
// bind2nd
template<class Op>
class binder2nd : public unary_function
   <typename Op::first_argument_type,
    typename Op::result_type>
{
   Op op_;
   typename Op::second_argument_type second_arg_;
 
   public:
      binder2nd(const Op& op,
                const typename Op::second_argument_type&
                                   second_arg) : op_(op),
                                   second_arg_(second_arg) {}
 
   typename Op::result_type operator()(const typename
      Op::argument_type& arg) const
   {
      return op_(arg, second_arg_);
   }
};
 
template<class Op, class Arg>
inline binder2nd<Op> bind2nd(const Op& op,
                             const Arg& arg)
{
   return binder2nd<Op>(op, arg);
}
Negaters

  Negater是針對Predicate設計的,它簡單的將Predicate的返回值取反。有兩個Negater,not1和not2,分別對一元和二元Predicate取反。

Member Function Adapters

  有時候,你可能想讓算法調用容器元素的成員函數,而不是外部函數。因爲外部無法改變對象內的狀態,且內部函數通常具有更高的效率。例如swap(string, string)總是沒有string.swap(string)快速。又比如sort無法對list進行排序,這時候只能使用list.sort()來給鏈表排序。這時候就需要使用一定的方法將對象內部的函數“變成”函數對象,這樣的函數對象叫做成員函數適配器,其實前面的binder也是一種適配器。看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int
main(int argc, char **argv)
{
    vector<list<int> > v;
    v.push_back(list<int>());
    vector<list<int> >::iterator it;
    for(it = v.begin(); it != v.end(); ++it)
    {
        for(int i = 0; i < 20; ++i)
        {
            (*it).insert((*it).begin(), i);
        }
    }
    for_each(v.begin(), v.end(), std::mem_fun_ref(&list<int>::sort));
    for(it = v.begin(); it != v.end(); ++it)
    {
        for(list<int>::iterator lt; lt != (*it).end(); ++lt)
        {
            std::cout<<*lt<<std::endl;
        }
    }
    return 0;
}

上面的例子中,遍歷vector<list<int> >並對鏈表進行排序。其中使用的是成員函數適配器mem_fun_ref,它返回的函數對象會以list<int>對象的引用爲參數。另外一個mem_fun則是以指向list<int>對象的指針爲參數。

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