C/C++编程:STL 配接器

1059 篇文章 278 订阅

概述

adapter在STL组件的灵活组合运用上,扮演者转换器的角色。adapter来源于一种适配器模式,其功能是:将一个class接口转换为另一个class的接口,使得原本因接口不兼容而不能合作的classes,可以一起运作

STL配接器可以分为三种:

  • 改变仿函数functions接口的:function adapter
  • 改变容器containers接口的:container adapter
  • 改变迭代器iterators接口的:iterator adapter

iterator adapter

STL应用率很多应用于迭代器身上的配接器,包括insert iterator、reverse iterator、iostream iterator。C++标准规定它们的接口可以由<iterator>获得,STL 将它们实际定义于std_iterator.h

insert iterator

是什么

所谓insert iterator,可以将一般迭代器的赋值操作转换为插入操作。包括三种:

  • 专用于尾插的back_insert_iterator
  • 专用于头插的front_insert_iterator
  • 可用于任意位置插入的insert_iterator

由于这三个insert iterator的使用不直观,所以STL提供了三个便利函数:back_iterator()、front_inserter()、inserter()

在这里插入图片描述

实现

back_iterator实现

其主要观念是:

  • 每一个insert iterator内部都维护由一个容器(必须由用户指定);容器当然有自己的迭代器,于是,当客户端对insert iertator做赋值操作时,就在insert iterator中被转换为对该容器的迭代器做insert操作。
  • 也就是说,insert iterator的operator=操作符中调用底层容器的push_front或者push_back()或者insert函数
  • 对于其他迭代器的行为比如operator++、operator–,没有提供这样的接口
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

front_inserter实现

在这里插入图片描述

inserter实现

在这里插入图片描述

reverse iterator

是什么

  • 所谓reverse iterator,可以将一般迭代器的行进方向逆转,使得原本的operator++变为–,原本的operator–变为++。
  • 如果stl算法接收的不是一般的正常的迭代器,而是这种逆转迭代器,它就会以从尾到头的方向来处理序列中的容器。比如:
   // 将所有元素逆序拷贝到iter所指位置上
    std::copy(id.rbegin(), id.rend(), iter)

实现

先来看看rbegin和rend
在这里插入图片描述
在这里插入图片描述
没有任何例外,只要双向序列容器提供了begin()、end(),它的rbegin()、rend()就是上面的形式。

#include <iterator>
#include <deque>
#include <algorithm>
#include <iostream>
#include <functional>
#include <stack>
using namespace std;
int main(){


    int ia[] = {99, 1, 2, 3, 4, 100};
    std::vector<int> vec(ia, ia + 6);
    std::cout << *vec.begin() << "\t" << *vec.rbegin() << "\n";  //99	100
    std::cout << *vec.end() << "\t" << *vec.rend() << "\n";   //dargget 133889	0

    auto iter = std::find(vec.begin(), vec.end(), 3);
    std::reverse_iterator<std::vector<int>::iterator> riter(iter);
    std::cout << *iter << "\t" << *riter << "\n";  // 3 2
}

在这里插入图片描述
可以看出,当迭代器被逆转方向时,虽然真正的位置不变,但是其逻辑位置变了。换句话说,当我们将一个正向迭代器区间转换为一个逆向迭代器区间后,不必再有任何额外的处理,就可以让接受这个逆向迭代器区间的算法,以相反的元素次数来处理区间中的每一个元素

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

iostream iterator

是什么

所谓iostream iterator,可以将迭代器绑定到某个iostream对象上:

  • 绑定到istream对象上的,叫做istream_iterator,用于输入功能
  • 绑定到ostream对象上的,叫做ostream_iterator,用于输出功能
#include <iterator>
#include <deque>
#include <algorithm>
#include <iostream>


int main(){
    std::ostream_iterator<int> outite(std::cout, " ");

    int ia[] = {0, 1, 2, 3, 4, 5};
    std::deque<int> id(ia, ia + 6);
    std::copy(id.begin(), id.end(), outite);  //0 1 2 3 4 5
    std::cout << std::endl;

    std::copy(id.begin(), id.end(), std::front_inserter(id));
    std::copy(id.begin(), id.end(), outite);  //5 4 3 2 1 0 0 1 2 3 4 5
    std::cout << std::endl;

    std::copy(id.begin(), id.end(), std::back_inserter(id));
    std::copy(id.begin(), id.end(), outite);  //5 4 3 2 1 0 0 1 2 3 4 5 5 4 3 2 1 0 0 1 2 3 4 5
    std::cout << std::endl;

    id = {1 , 2, 3, 4, 5};
    std::copy(id.begin(), id.end(), outite);  // 1 2 3 4 5
    std::cout << std::endl;
    std::copy(ia, ia + 2, std::inserter(id, std::find(id.begin(), id.end(), 5)));
    std::copy(id.begin(), id.end(), outite);  //1 2 3 4 0 1 5
    std::cout << std::endl;

    std::copy(id.rbegin(), id.rend(), outite);  //5 1 0 4 3 2 1
    std::cout << std::endl;

//    std::istream_iterator<int> inite(std::cin), eos;
//    std::copy(inite, eos, std::inserter(id, id.begin()));  //5 1 0 4 3 2 1
//    std::cout << std::endl;

}

实现

所谓绑定一个istream object,其实就是在istream iterator内部维护一个istream member,客户端对这个迭代器所做的operator++操作,都会被引导到调用迭代器内部所含的那个istream member的operator>>。如下
在这里插入图片描述
在这里插入图片描述
可以看出,只要客户端定义一个istream iterator并绑定到某个istream object,程序就立即停在istream_iterator<T>::read()函数等待输入,这不是我们所预期的行为。所以,请在绝对必要的时候才定义istream iterator

function adapter

function adapter是所有适配器中数量最庞大的一个,非常灵活。这些配接操作包括bind、negate(否定)、compose(组合),以及对一般函数或者成员函数的修饰(使其称为一个仿函数)。需要使用的话请引入<functional>

function adapter能够事先对一个函数完成参数的绑定、执行结果的否定以及多方函数的组合

function adapter的作用:通过它们之间的compose、bind、修饰能力,几乎可以无限的创建出各种可能的表达式。比如需要找出某个序列中所有不小于3的元素个数。那么

    cout<< count_if(vec.begin(), vec.end(),not1(bind2nd(less<int>(), 3)))<< endl;//>=3的个数,一个参数未定

是什么

我们可以知道:

  • 容器是以class template完成
  • 算法是以function template完成
  • 仿函数是一种将operator()重载的class template
  • 迭代器是一种将operator++和operator*等指针习惯行为重载的class template
  • 配接器:
    • 应用于容器和迭代器身上的配接器,都是一种class template
    • 应用于仿函数的配接器,

每一个function adapters,内藏了一个memeber object,其型别等于它所要配接的对象。当function adapter有了完全属于自己的一份修饰对象(的副本)在手,他就成了该修饰对象(的副本)的主人,也就有资格调用该修饰对象(一个仿函数)并在参数和返回值上动手脚了

在这里插入图片描述
在这里插入图片描述
所谓pred,会返回true或者false

实现

对返回值进行逻辑否定:not1、not2

在这里插入图片描述
在这里插入图片描述

对参数进行绑定:bind1st、bind2nd

在这里插入图片描述

在这里插入图片描述

用于函数指针:ptr_func

这种适配器可以使我们能够将一般函数当做仿函数使用。

  • 一般函数当做仿函数传给STL算法,就语言层面是可以的。
  • 但是如果不适用这个配接器先封装,这个一般函数将无配接能力,也就是无法和其他配接器接轨
    在这里插入图片描述
    在这里插入图片描述

用于成员函数指针:mem_func、mem_func_ptr

这种适配器能够让成员函数当做仿函数来使用。当容器的元素类型是X&、X*时,而且又有虚函数作为仿函数,就可以借由泛型算法完成多态调用

#include <iterator>
#include <deque>
#include <algorithm>
#include <iostream>
#include <functional>
#include <stack>
using namespace std;

class Shape{
public:
    virtual void display() = 0;
};

class Rect : public Shape{
public:
    virtual void display(){
        std::cout << "Rect" << "\n";
    }
};
class Circle : public Shape{
public:
    virtual void display(){
        std::cout << "Circle"<< "\n";
    }
};

int main(){
    std::vector<Shape *> v;

    v.push_back(new Rect);
    v.push_back(new Circle);

    for(auto i = v.begin(); i != v.end(); ++i){
        (*i)->display();
    }

    std::cout << "\n";
    for_each(v.begin(), v.end(), std::mem_fun(&Shape::display));
    std::cout << "\n";
}

在这里插入图片描述

container adapter

STL提供两个容器stack、queue,其实是一种适配器,它们将deque修饰为另一种容器风貌

stack

stack的底层是deque。
在这里插入图片描述
stack封装了所有deque的对外接口,只提供了符合stack原则的几个函数

queue

queue的底层是deque
在这里插入图片描述

queue封装了所有deque的对外接口,只提供了符合queue原则的几个函数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值