一、适配器

三种类型的适配器:

容器适配器:用来扩展7种基本容器,利用基本容器扩展形成了栈、队列和优先级队列

迭代器适配器:(反向迭代器、插入迭代器、IO流迭代器)

函数适配器:函数适配器能够将仿函数和另一个仿函数(或某个值、或某个一般函数)结合起来。

针对成员函数的函数适配器

针对一般函数的函数适配器

二、函数适配器示例

 C++ Code 
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
 
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>

using namespace std;

bool is_odd(int n)
{
    return n % 2 == 1;
}

int main(void)
{
    int a[] = {1, 2, 3, 4, 5};
    vector<int> v(a, a + 5);

cout << count_if(v.begin(), v.end(), is_odd) << endl;

//计算奇数元素的个数
    // 这里的bind2nd将二元函数对象modulus转换为一元函数对象。
    //bind2nd(op, value) (param)相当于op(param, value)
    cout << count_if(v.begin(), v.end(),
                     bind2nd(modulus<int>(), 2)) << endl;

//bind1st(op, value)(param)相当于op(value, param);
    cout << count_if(v.begin(), v.end(),
                     bind1st(less<int>(), 4)) << endl;

return 0;
}

 C++ Code 
1
2
3
4
5
6
7
8
9
 
// TEMPLATE FUNCTION bind2nd
template < class _Fn2,
         class _Ty > inline
binder2nd<_Fn2> bind2nd(const _Fn2 &_Func, const _Ty &_Right)
{
    // return a binder2nd functor adapter
    typename _Fn2::second_argument_type _Val(_Right);
    return (std::binder2nd<_Fn2>(_Func, _Val));
}

将匿名对象modulus<int>() 和 2 传递进去std::binder2nd<_Fn2>(_Func, _Val);  即是一个模板类对象,看binder2nd 模板类

 C++ Code 
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
 
// TEMPLATE CLASS binder2nd
template<class _Fn2>
class binder2nd
    : public unary_function < typename _Fn2::first_argument_type,
      typename _Fn2::result_type >
{
    // functor adapter _Func(left, stored)
public:
    typedef unary_function < typename _Fn2::first_argument_type,
            typename _Fn2::result_type > _Base;
    typedef typename _Base::argument_type argument_type;
    typedef typename _Base::result_type result_type;

binder2nd(const _Fn2 &_Func,
              const typename _Fn2::second_argument_type &_Right)
        : op(_Func), value(_Right)
    {
        // construct from functor and right operand
    }

result_type operator()(const argument_type &_Left) const
    {
        // apply functor to operands
        return (op(_Left, value));
    }

result_type operator()(argument_type &_Left) const
    {
        // apply functor to operands
        return (op(_Left, value));
    }

protected:
    _Fn2 op;    // the functor to apply
    typename _Fn2::second_argument_type value;  // the right operand
};

即构造时,binder2nd 的2个成员op 和 value 分别用modulus<int>() 和 2 初始化。接着看count_if 中的主要代码:

for (; _First != _Last; ++_First)

++_Count;

*_First  就是遍历得到的容器元素了,当满足_Pred 条件时_Count++,此时可以看成是:

std::binder2nd< modulus<int> >(modulus<int>(), 2)(*_First)  也就是调用binder2nd 类的operator() 函数,返回 return (op(_Left, value));

也就是modulus<int>()(*_First, 2);  也就是调用modulus 类的operator() 函数,如下:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
 
// TEMPLATE STRUCT modulus
template<class _Ty>
struct modulus
        : public binary_function<_Ty, _Ty, _Ty>
{
    // functor for operator%
    _Ty operator()(const _Ty &_Left, const _Ty &_Right) const
    {
        // apply operator% to operands
        return (_Left % _Right);
    }
};

来说,可以理解成这样:bind2nd(op, value) (param)相当于op(param, value); 其中param 是元素值,value是需要绑定的参数,所谓

bind2nd 也即绑定第二个参数的意思,所以才说 bind2nd将二元函数对象modulus转换为一元函数对象,因为第二个参数就是2,当然

这里的第一个参数就是遍历得到的容器元素值了。

与bind2nd
类似的还有 bind1st,顾名思义是绑定第一个参数的意思,如下的表达式:

count_if(v.begin(), v.end(),  bind1st(less<int>(), 4))
; 也就是说计算容器中大于4的元素个数。这里绑定的是左操作数。

三、函数适配器应用实例

(一)、针对成员函数的函数适配器

 C++ Code 
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
 
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <string>

using namespace std;

class Person
{
public:
    Person(const string &name) : name_(name) {}
    void Print() const
    {
        cout << name_ << endl;
    }
    void PrintWithPrefix(string prefix) const
    {
        cout << prefix << name_ << endl;
    }
private:
    string name_;
};

void foo(const vector<Person> &v)
{
    for_each(v.begin(), v.end(), mem_fun_ref(&Person::Print));
    for_each(v.begin(), v.end(), bind2nd(mem_fun_ref(&Person::PrintWithPrefix), "person: "));
}

void foo2(const vector<Person *> &v)
{
    for_each(v.begin(), v.end(), mem_fun(&Person::Print));
    for_each(v.begin(), v.end(), bind2nd(mem_fun(&Person::PrintWithPrefix), "person: "));
}

int main(void)
{
    vector<Person> v;
    v.push_back(Person("tom"));
    v.push_back(Person("jerry"));
    foo(v);

vector<Person *> v2;
    v2.push_back(new Person("tom"));
    v2.push_back(new Person("jerry"));
    foo2(v2);
    return 0;
}

在foo 函数中,第一行的mem_fun_ref 将空元函数转换为一元函数对象,具体流程大家可以自己跟踪代码,实际上跟上面bind2nd 是类似的,

需要稍微说一下的是传递函数指针的情况:

 C++ Code 
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
 
template < class _Result,
         class _Ty > inline
const_mem_fun_ref_t<_Result, _Ty>
mem_fun_ref(_Result (_Ty::*_Pm)() const)
{
    // return a const_mem_fun_ref_t functor adapter
    return (std::const_mem_fun_ref_t<_Result, _Ty>(_Pm));
}

// TEMPLATE CLASS const_mem_fun_ref_t
template < class _Result,
         class _Ty >
class const_mem_fun_ref_t
    : public unary_function<_Ty, _Result>
{
    // functor adapter (*left.*pfunc)(), const *pfunc
public:
    explicit const_mem_fun_ref_t(_Result (_Ty::*_Pm)() const)
        : _Pmemfun(_Pm)
    {
        // construct from pointer
    }

_Result operator()(const _Ty &_Left) const
    {
        // call function
        return ((_Left.*_Pmemfun)());
    }

private:
    _Result (_Ty::*_Pmemfun)() const;   // the member function pointer
};

传入的参数是一个函数指针,也就是void (Person::*_Pm) () const , 传递后 _Pm = &Print,在operator() 函数中

return ((_Left.*_Pmemfun)());   _Left 也就是遍历到的Person 类对象,先找到类的函数,然后进行调用。

 C++ Code 
1
2
3
4
5
 
_Result operator()(_Ty &_Left, _Arg _Right) const
{
    // call function with operand
    return ((_Left.*_Pmemfun)(_Right));
}

也就是将第二个参数当作参数传递给PrintWithPrefix,所以打印出来的带有前缀person:

而mem_fun 就类似了,只不过此次for_each 遍历得到的是对象指针,所以进行函数调用时需要用-> 操作符,如下所示:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
 
_Result operator()(const _Ty *_Pleft) const
{
    // call function
    return ((_Pleft->*_Pmemfun)());
}

_Result operator()(const _Ty *_Pleft, _Arg _Right) const
{
    // call function with operand
    return ((_Pleft->*_Pmemfun)(_Right));
}

(二)、针对一般函数的函数适配器

例程1:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <string>

using namespace std;

int main(void)
{
    char *a[] = {"", "BBB", "CCC"};
    vector<char *> v(a, a + 2);
    vector<char *>::iterator it;
    it = find_if(v.begin(), v.end(), bind2nd(ptr_fun(strcmp), ""));
    if (it != v.end())
        cout << *it << endl;

return 0;
}

ptr_fun 将strcmp 二元函数转换为二元函数对象,bind2nd 再将其转化为一元函数对象,即绑定了第二个参数,因为strcmp 是在比较

不相等的情况返回为真,故find_if 查找的是第一个不等于空串的串位置。

例程2:

 C++ Code 
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
 
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <string>

using namespace std;

bool check(int elem)
{
    return elem < 3;
}

int main(void)
{
    int a[] = {1, 2, 3, 4, 5};
    vector<int> v(a, a + 5);

vector<int>::iterator it;
    it = find_if(v.begin(), v.end(), not1(ptr_fun(check)));
    if (it != v.end())
        cout << *it << endl;
    return 0;
}

ptr_fun 做了一次转换,not1 再转换一次,故find_if 查找的是第一个大于等于3的元素位置。

这些代码的跟踪就留给大家自己完成了,篇幅所限,不能将所有调用过程都显现出来,学习STL还是得靠大家跟踪源码,才能有更深的体会。

参考:

C++ primer 第四版
Effective C++ 3rd
C++编程规范

函数适配器bind2nd 、mem_fun_ref 源码分析、函数适配器应用举例的更多相关文章

  1. Flask系列10-- Flask请求上下文源码分析

    总览 一.基础准备. 1. local类 对于一个类,实例化得到它的对象后,如果开启多个线程对它的属性进行操作,会发现数据时不安全的 import time from threading import ...

  2. jQuery-1.9.1源码分析系列完毕目录整理

    jQuery 1.9.1源码分析已经完毕.目录如下 jQuery-1.9.1源码分析系列(一)整体架构 jQuery-1.9.1源码分析系列(一)整体架构续 jQuery-1.9.1源码分析系列(二) ...

  3. angular源码分析:injector.js文件分析——angular中的依赖注入式如何实现的(续)

    昨天晚上写完angular源码分析:angular中jqLite的实现--你可以丢掉jQuery了,给今天定了一个题angular源码分析:injector.js文件,以及angular的加载流程,但 ...

  4. Java-Integer源码分析

    除了两种浮点型,剩下的几种基本数据类型的包装类几乎都实现了常量池,有好处用数据的时候直接去拿,没有再去创建,坏处是在程序编译的时候就存入大量数据不管用不用到.下面是一篇很好的文章,很详细,转自:htt ...

  5. 【spring源码分析】IOC容器初始化(总结)

    前言:在经过前面十二篇文章的分析,对bean的加载流程大致梳理清楚了.因为内容过多,因此需要进行一个小总结. 经过前面十二篇文章的漫长分析,终于将xml配置文件中的bean,转换成我们实际所需要的真正 ...

  6. 【spring源码分析】IOC容器初始化(四)

    前言:在[spring源码分析]IOC容器初始化(三)中已经分析了BeanDefinition注册之前的一些准备工作,下面将进入BeanDefinition注册的核心流程. //DefaultBean ...

  7. 【spring源码分析】IOC容器初始化(十)

    前言:前文[spring源码分析]IOC容器初始化(九)中分析了AbstractAutowireCapableBeanFactory#createBeanInstance方法中通过工厂方法创建bean ...

  8. Java源码分析:关于 HashMap 1.8 的重大更新(转载)

    http://blog.csdn.net/carson_ho/article/details/79373134 前言 HashMap 在 Java 和 Android 开发中非常常见 而HashMap ...

  9. linux内存源码分析 - 伙伴系统(初始化和申请页框)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 之前的文章已经介绍了伙伴系统,这篇我们主要看看源码中是如何初始化伙伴系统.从伙伴系统中分配页框,返回页框于伙伴系 ...

随机推荐

  1. SQL SERVER SQLOS的任务调度--微软亚太区数据库技术支持组 官方博客

    https://blogs.msdn.microsoft.com/apgcdsd/2011/11/23/sql-server-sqlos/

  2. HDU 1159 &amp;&amp; POJ 1458

    最长公共子序列.状态转移方程见代码. #include <iostream> #include <cstdio> #include <cstring> using ...

  3. 根据url下载图片和页面

    需要将&tp=webp&wxfrom=5去掉,既可以在任何地方显示,也可以下载了 http://mmbiz.qpic.cn/mmbiz_jpg/bf8pC39RBhGFOH1ib9Ac ...

  4. Introducing ASP.NET Core: The New ASP.NET in Town!

    The new version of ASP.NET is called ASP.NET Core (a.k.a ASP.NET 5) and it has the most significant ...

  5. CSS学习要点

    目标 掌握CSS基本语法,了解如何应用CSS到Html元素上并能熟练使用CSS进行元素布局. 要点 CSS基本概念.存在的意义 CSS 指层叠样式表 (Cascading Style Sheets), ...

  6. dedecms织梦 标签远程文件写入漏洞

    测试方法: @Sebug.net   dis本站提供程序(方法)可能带有攻击性,仅供安全研究与教学之用,风险自负! 前题条件,必须准备好自己的dede数据库,然后插入数据: insert into d ...

  7. MSSQL 触发器 暂停 和 启动

    开启关闭触发器 禁用: ALTER TABLE member DISABLE TRIGGER trig1 GO 恢复: ALTER TABLE member ENABLE TRIGGER trig1 ...

  8. microsoft visual c++与microsoft visual net 版本对应关系

    7.1 -> 2003 8.0 -> 2005 9.0 -> 2008 10.0 -> 2010 11.0 -> 2012 12.0 -> 2013 14.0 -& ...

  9. 给出a的定义 -- 指针 和 数组

  10. Android -- 经验分享(三)

    目录 获取系统版本号 获取系统信息 获取安装路径和已安装程序列表 获取图片.应用名.包名 解决listview上Item上有按钮时item本身不能点击的问题 不让文本框输入中文 获取屏幕宽高 将Tab ...