STL之适配器(adapter)


一、适配器

定义

适配器是标准库中的一个通用概念。容器、迭代器和函数都有适配器。本质上,一个适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样。

改变函数对象接口的称为 function adapter,改变容器接口的称为 container adapter,改变迭代器接口的称为 iterator adapter。

二、容器适配器

STL定义了3个顺序容器适配器:stack、queue和priority_queue。有关容器适配器的内容请参看STL之顺序容器

三、函数适配器

3.1、bind()

bind()接受一个可调用对象,生成一个新的可调用对象来"适应"原对象的参数列表。

调用bind的一般形式为:auto newCallable = bind(callable,arg_list);
其中,newCallable 本身是一个可调用对象,arg_list 是一个逗号分隔的参数列表,对应给定的 callable 的参数。即,当我们调用 newCallable 时,newCallable 会调用 callable,并传递给它 arg_list 中的参数。

arg_list 中的参数可能包含形如 _n 的名字,其中 n 是一个整数。这些参数是“占位符”,表示 newCallable 的参数,它们占据了传递给 newCallable 的参数的“位置”。数值 n 表示生成的可调用对象中参数的位置:_1 为 newCallable 的第一个参数,_2 为第二个参数,依此类推。

3.1.1、示例

例如: auto f2 = bind(f1,a,b,_2,_1);
这个bind调用会将 f2(_1,_2) 映射为 f1(a,b,_2,_1)。即对 f2 的调用会调用 f1,用f2的参数代替占位符,再加上绑定的参数a、b。

#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders;

void fun1(string &str, int &i)
{
  cout << str << ' ' << i << endl;
}

auto fun2 = bind(fun1, _1, _2);

int main()
{
  string str("hello");
  int i = 0;
  fun2(str, i); //输出为:hello 0

  return 0;
}

3.1.2、重载函数

为了绑定重载函数的参数,必须显式说明绑定的是哪个版本的函数。
在这里插入图片描述

3.1.3、绑定引用参数

默认情况下,bind 的那些不是占位符的参数被拷贝到 bind 返回的可调用对象中。如果希望传递给 bind 一个对象而又不拷贝它,例如当参数为一个ostream,就必须使用标准库的 ref 函数。函数 ref 返回一个对象,包含给定的引用,此对象是可以拷贝的。标准库中还有一个 cref 函数,生成一个保存const引用的类。

使用方式: auto f2 = bind(f1,ref(os),_1);

3.2、mem_fn()

3.2.1、类的成员函数指针

对于类的成员函数指针,若想通过一个指向成员函数的指针进行函数调用,必须首先利用.* 运算符或 ->*运算符将该指针绑定到特定的对象上。因此与普通的函数指针不同,成员指针不是一个可调用对象,这样的指针不支持函数调用运算符

因为成员指针不是可调用对象,所以我们不能直接将一个指向成员函数的指针传递给算法。例如,如果我们想在一个 string 的 vector 中找到第一个空 string,不能使用下面的语句:

auto  fp = &string::empty;//fp指向string的empty函数
find_if(svec.begin(), svec.end(), fp);//错误,必须使用.*或->*调用成员指针

find_if 算法需要一个可调用对象,但我们提供给它的是一个指向成员函数的指针fp。因此在 find_if 的内部将执行如下形式的代码,从而导致无法通过编译:

if(fp(*it))    //错误:要想通过成员指针调用函数,必须使用->*运算符

显然该语句试图调用的是传入的对象,而非函数。

3.2.2、使用mem_fn生成一个可调用对象

mem_fn(mf) 生成一个函数对象,可以作为非成员函数调用。mem_fn()的主要用途是服务与需要非成员函数的算法。通过使用 mem_fn 可以从成员指针生成一个可调用对象,mem_fn 可以根据成员指针的类型推断可调用对象的类型,而无须用户显式地指定:

find_if(svec.begin(), svec.end(), mem_fn(&string::empty));

3.2.3、示例

#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
  vector<string> vec{"hello", "", "world"};
  auto it = find_if(vec.begin(), vec.end(), mem_fn(&string::empty));
  *it = "my";

  for (auto &str : vec)
    cout << str << ' ';//输出为:hello my world

  return 0;
}

3.3、not1()和not2()

not1()和not2()用于对返回值进行逻辑否定,别用于一元和二元否定。

3.3.1、not1()

#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;

struct IntGreaterThanThree
    : public std::unary_function<int, bool>
{
  bool operator()(int x) const { return x > 3; }
};

int main()
{
  vector<int> vec{1, 2, 3, 4, 5};
  cout << count_if(vec.begin(), vec.end(), IntGreaterThanThree()) << endl;//2
  cout << count_if(vec.begin(), vec.end(), not1(IntGreaterThanThree()));//3

  return 0;
}

3.3.2、not2()

#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;

struct mysort
    : public std::binary_function<int, int, bool>
{
  bool operator()(int x, int y) const { return x > y; }
};

int main()
{
  vector<int> vec{1, 5, 3, 7, 2, 6};
  sort(vec.begin(), vec.end(), mysort());
  for (auto &i : vec)
    cout << i << ' ';//输出为:7 6 5 3 2 1

  cout << endl;

  sort(vec.begin(), vec.end(), not2(mysort()));
  for (auto &i : vec)
    cout << i << ' ';//输出为:1 2 3 5 6 7

  return 0;
}

3.4、ptr_fun()

将函数指针转换为函数对象。相比于函数指针,函数对象的优势在于,标准库中的对象声明了嵌套的 typedef,它们以统一的名称描述其参数和结果类型。有时候这些typedef是必需的,而不是可选的。

#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;

int inc(int &i)
{
  i++;
  return 0;
}

int main()
{
  vector<int> vecint{1, 2, 3, 4, 5};
  for_each(vecint.begin(), vecint.end(), ptr_fun(inc));
  for (auto &i : vecint)
    cout << i << ' ';//输出:2 3 4 5 6

  return 0;
}

四、迭代器适配器

4.1、插入迭代器

插入迭代器接受一个容器作为参数,生成一个迭代器,能实现向给定容器添加元素。当通过一个插入迭代器进行赋值时,该迭代器调用容器操作来向给定容器的指定位置插入一个元素。

共有3种插入迭代器:

  • back_inserter创建一个使用push_back的迭代器
  • front_inserter创建一个使用push_front的迭代器
  • inserter创建一个使用insert的迭代器。此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器。元素将被插入到给定迭代器所表示的元素之前。
#include <iostream>
#include <functional>
#include <list>
#include <algorithm>
using namespace std;

int main()
{
  list<int> listint{1, 2, 3};
  list<int> listdes;
  copy(listint.begin(), listint.end(), back_inserter(listdes));
  for (auto &i : listdes)
    cout << i << ' '; //输出:1 2 3

  auto it = front_inserter(listdes);
  *it = 4;
  for (auto &i : listdes)
    cout << i << ' '; //输出:4 1 2 3

  auto it1 = inserter(listdes, listdes.end());
  *it1 = 5;
  for (auto &i : listdes)
    cout << i << ' '; //输出:4 1 2 3 5

  return 0;
}

4.2、反向迭代器

反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器。对于反向迭代器,递增(以及递减)操作的含义会颠倒过来。递增一个反向迭代器(++it)会移动到前一个元素;递减一个迭代器(–it)会移动到下一个元素。

#include <iostream>
#include <functional>
#include <list>
#include <algorithm>
using namespace std;

int main()
{
  list<int> listint{1, 2, 3};
  for (auto it = listint.rbegin(); it != listint.rend(); it++)
    cout << *it << ' '; //输出:3 2 1

  return 0;
}

4.2.1、反向迭代器和其他迭代器之间的关系

对于序列[b:e),反向迭代器从序列尾部向序列起始位置进行遍历。为了获得一个半开区间,必须将b - 1视为序列的尾后位置,将e - 1视为起始位置,从而得到半开区间
[e - 1, b - 1)。因此,一个反向迭代器与其底层迭代器之间的根本关系是:
&*(reverse_iterator§)==&*(p - 1)
。例如,如果vec为一个vector,则vec.rbegin()指向其尾元素vec[vec.size() - 1]。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值