函数对象

[STL]函数对象/仿函数

  提到C++ STL,首先被人想到的是它的三大组件:Containers, Iterators, Algorithms,即容器,迭代器和算法。容器为用户提供了常用的数据结构,算法大多是独立于容器的常用的基本算法,迭代器是由容器提供的一种接口,算法通过迭代器来操控容器。接下来要介绍的是另外的一种组件,函数对象(Function ObjectJJHou译作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())(假定vvector<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

  有两种绑定bind1stbind2nd,它们可以将一个二元函数对象的其中一个参数绑定为某个已知的对象,从而得到一个一元函数对象。例如要在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;

}

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

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的返回值取反。有两个Negaternot1not2,分别对一元和二元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>对象的指针为参数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值