阿牧路泽

哪有那么多坚强,无非是死扛罢了
  博客园  :: 首页  :: 新随笔  :: 联系 :: 管理

6、【C++ STL】仿函数(函数对象)

Posted on 2018-10-22 15:05  阿牧路泽  阅读(562)  评论(0编辑  收藏  举报

一、仿函数(函数对象)

  仿函数又称函数对象,函数对象首先是一个对象,即某个类的实例。其次,函数对象的行为和函数一致,即是说可以像调用函数一样来使用函数对象,如参数传递、返回值等。这种行为是通过重载类的()操作符来实现的。

【示例】

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

  其实我们早就开始使用函数对象了,当你写下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 template <class T>
2 class less
3 {
4 public:
5     bool operator()(const T&l, const T&r)const
6     {
7         return l < r;
8     }
9 };

1、函数对象的分类

  根据用途和参数特征,STL中的函数对象通常分为以下几类:Predicates, Arithmetic Function Objects, Binders, Negaters, Member Function Adapters, Pointer to Function Adapters。下面逐一介绍一下,之前得先介绍两个基类:

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

  Predicate是一种函数对象,返回值(应该是operator()的返回值)为布尔型,接受一个或者两个参数。通常用来判断对象的有效性(一个参数时)或者对两个对象进行比较(如less)。你可以根据自己的需要定义自己的Predicate,但STL已经定义了一些Predicate,你可以直接使用。

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 使用||逻辑或
(2)算术运算函数对象

  进行简单的算术运算,这类函数对象我用的很少,通常是自己定义。

函数对象类型描述
negate() Unary 使用-求负
plus() Binary 使用+加法
minus() Binary 使用-减法
multiplies() Binary 使用*乘法
divides() Binary 使用/除法
modulus() Binary 使用%求余
(3)绑定Binders

  有两种绑定bind1st和bind2nd,它们可以将一个二元函数对象的其中一个参数绑定为某个已知的对象,从而得到一个一元函数对象。例如要在vector<int> v中查找等于372的值的位置,我可以将372绑定到equal_to<int>()的第一个或者第二个参数:

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

  其实,这里的bind1st和bind2nd并不是函数对象,只是模板函数而已。这两个函数分别返回类型为binder1st和binder2nd的函数对象。下面的代码,聪明的你肯定一看就懂:

 1 // bind1st
 2 template<class Op> 
 3 class binder1st : public unary_function
 4                   <typename Op::second_argument_type,
 5                    typename Op::result_type>
 6 {
 7    Op op_;
 8    typename Op::first_argument_type first_arg_;
 9  
10    public:
11       binder1st(const Op& op,
12                 const typename Op::first_argument_type&
13                 first_arg) : op_(op),
14                first_arg_(first_arg) {}
15  
16    typename Op::result_type operator()
17       (const typename Op::second_argument_type& arg) const
18    {
19       return op_(first_arg_, arg);
20    }
21 };
22  
23 template<class Op, class Arg>
24 inline binder1st<Op> bind1st(const Op& op,
25                              const Arg& arg)
26 {
27    return binder1st<Op>(op, arg);
28 }
29  
30 // bind2nd
31 template<class Op>
32 class binder2nd : public unary_function
33    <typename Op::first_argument_type,
34     typename Op::result_type>
35 {
36    Op op_;
37    typename Op::second_argument_type second_arg_;
38  
39    public:
40       binder2nd(const Op& op,
41                 const typename Op::second_argument_type&
42                                    second_arg) : op_(op),
43                                    second_arg_(second_arg) {}
44  
45    typename Op::result_type operator()(const typename
46       Op::argument_type& arg) const
47    {
48       return op_(arg, second_arg_);
49    }
50 };
51  
52 template<class Op, class Arg>
53 inline binder2nd<Op> bind2nd(const Op& op,
54                              const Arg& arg)
55 {
56    return binder2nd<Op>(op, arg);
57 }
(4)Negaters

  Negater是针对Predicate设计的,它简单的将Predicate的返回值取反。有两个Negater,not1和not2,分别对一元和二元Predicate取反。

(5)Member Function Adapters

  有时候,你可能想让算法调用容器元素的成员函数,而不是外部函数。因为外部无法改变对象内的状态,且内部函数通常具有更高的效率。例如swap(string, string)总是没有string.swap(string)快速。又比如sort无法对list进行排序,这时候只能使用list.sort()来给链表排序。这时候就需要使用一定的方法将对象内部的函数“变成”函数对象,这样的函数对象叫做成员函数适配器,其实前面的binder也是一种适配器。看下面的例子:

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

  上面的例子中,遍历vector<list<int> >并对链表进行排序。其中使用的是成员函数适配器mem_fun_ref,它返回的函数对象会以list<int>对象的引用为参数。另外一个mem_fun则是以指向list<int>对象的指针为参数。