[STL]函数对象/仿函数

news/2024/7/3 14:52:22 标签: function, iterator, class, less, 算法, containers
class="baidu_pl">
class="article_content clearfix">
class="htmledit_views">

class="post">
class="post-2209 post type-post hentry category-language category-program tag-cplusplus tag-stl">
class="postText">

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

什么是函数对象

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

class="wp_codebox">
class="line_numbers">
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class="code">
class="language-cpp">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>的一个典型实现可能是这样的:

class="wp_codebox">
class="line_numbers">
1
2
3
4
5
6
7
8
9
class="code">
class="language-cpp">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。下面逐一介绍一下,之前得先介绍两个基类:

class="wp_codebox">
class="line_numbers">
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class="code">
class="language-cpp">template<class Arg, class Res>
struct unary_class="tags" href="/tags/FUNCTION.html" title=function>function //~ 一元函数对象基类
{
   typedef Arg argument_type;
   typedef Res result_type;
};
 
template<class Arg1, class Arg2, class Res>
struct binary_class="tags" href="/tags/FUNCTION.html" title=function>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>()的第一个或者第二个参数:

class="wp_codebox">
class="line_numbers">
1
2
3
4
5
6
7
8
9
10
11
12
13
class="code">
class="language-cpp">int
main(int argc, char **argv)
{
    vector<int> v;
    for(int i = 0; i < 1000; ++i)
    {
        v.push_back(i);
    }
    vector<int>::class="tags" href="/tags/ITERATOR.html" title=iterator>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的函数对象。下面的代码,聪明的你肯定一看就懂:

class="wp_codebox">
class="line_numbers">
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
class="code">
class="language-cpp">// bind1st
template<class Op> 
class binder1st : public unary_class="tags" href="/tags/FUNCTION.html" title=function>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_class="tags" href="/tags/FUNCTION.html" title=function>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也是一种适配器。看下面的例子:

class="wp_codebox">
class="line_numbers">
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class="code">
class="language-cpp">int
main(int argc, char **argv)
{
    vector<list<int> > v;
    v.push_back(list<int>());
    vector<list<int> >::class="tags" href="/tags/ITERATOR.html" title=iterator>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>::class="tags" href="/tags/ITERATOR.html" title=iterator>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>对象的指针为参数。

class="tags-in-post">


http://www.niftyadmin.cn/n/1710861.html

相关文章

Flask——前后端分离知识点

1.小知识 werkzeug 实现 web server Flask 底层运行 from werkzeug.wrappers import Request, Response from werkzeug.serving import run_simpleclass Short(object):def __call__(self, environ, start_response):request Request(environ)text hello, %s %(request.args…

listbox的全选,反选和全不选

http://zwkufo.blog.163.com/blog/static/25882512009102334056128/ //全选方法一private void SelectAll(ListBox ListBox){ for (int i 0; i < ListBox.Items.Count; i) { ListBox.SelectedIndex i; } }//全选方法一和全不选private void Se…

剑指offer——5

1.输入一个数组&#xff0c;返回其最大连续子数组的和&#xff0c;数组中正负整数都有 class Solution:def findsum(self, array):maxnum Nonetempnum 0for i in array:if maxnum None:maxnum iif tempnum i < i:tempnum ielse:tempnum iif maxnum < tempnum:max…

控件通知消息

控件通知消息 2008年04月06日 星期日 00:09控件通知消息有很多种&#xff0c;但是有一种是很常用&#xff0c;但是又不是很容易掌握的&#xff0c;那就是WM_NOTIFY&#xff0c;我试着对此做一下比较全面的论述&#xff0c;有不对的地方&#xff0c;还希望各路大虾批评指正。 控…

web前端期末大作业《中华传统文化题材网页之丝绸之路》 html+css+javascript网页设计实例

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Python实现快排

1.快排核心思想 从待排序的数组中找出一个数作为基准数 (取第一个数即可&#xff09;然后将原来的数组划分为两部分&#xff1a;小于基准数的左子数组和大于基准数的右子数组。然后对这两个子数组再递归重复上述过程&#xff0c;直到两个子数组的所有数都分别有序。最后返回 ‘…

深度解析VC中的消息传递机制

摘要&#xff1a;Windows编程和Dos编程&#xff0c;一个很大的区别就是&#xff0c;Windows编程是事件驱动&#xff0c;消息传递的。所以&#xff0c;要学好Windows编程&#xff0c;必须 对消息机制有一个清楚的认识&#xff0c;本文希望能够对消息的传递做一个全面的分析。 一…

Xpath抽取技术

1.xpath常用规则 表达式 描述nodename 选取此节点的所有子节点/ 从当前节点选取直接子…