STL源码剖析 配接器

  • 配接器(adapters)在 STL组件的灵活组合运用功能上,扮演着轴承、转换器的角色。Adapter这个概念,事实上是一种设计模式(design pattern)。 «Design Patterns)) 一书提到23个最普及的设计模式,其中对odopter样式的定义如下:将 一个class的接口转换为另一个class的接口,使原本因接口不兼容而不能合作的classes,可以一起运作。

8 . 1 配接器之概观与分类

  • S T L所提供的各种配接器中,改变仿函数(functors)接口者,我们称为functionadapter,改变容器(containers)接口者,我们称为container adapter,改变迭代器 fi (iterators)接口者,我们称为 iterator adapter

8.1.1 应 用 于 容 器 ,container adapters

  • S TL提供的两个容器queue和 s ta c k ,其实都只不过是一种配接器。它们修 饰 deque的接口而成就出另一种容器风貌。这两个container adapters已于第4章介绍过。

8.1.2 应 用 于 迭 代 器 ,iterator adapters

  • S T L 提供了许多应用于迭代器身上的配接器,包 括 insert iterators, reverse iterators, iostream iterators. C++ Standard 规定它们的接口可以藉由 <iterator>获得,SGI S T L则将它们实际定义于 <stl_iterator.h>

Insert Iterators

  • 所 谓 insert iterators, 可以将一般迭代器的赋值(assign)操作转变为插入 (insert}-操作。这样的迭代器包括专司尾端插入操作的back_insert_-iterator, 专司头端插入操作的fro n t_ in se rt_ ite ra to r,以及可从任意位置执行插入操作的 insert_i te ra to ro 由于这三个iterator adapters的使用接口不是十分直观,给一般用户带来困扰,因此,STL更提供三个相应函数: back_inserter()、front_inserter()、 inserter(), 如图8-1所示,提升使用时的便利性。

Reverse Iterato rs

  • 所谓reverse iterators,可以将一般迭代器的行进方向逆转,使原本应该前进的 operator++ 变成了后退操作,使原本应该后退的 operator-- 变成了前进操作。 这种错乱的行为不是为了掩人耳目或为了欺敌效果,而是因为这种倒转筋脉的性质运用在“从尾端开始进行”的算法上,有很大的方便性• 稍后我有一些范例展示

lO S tream Iterato rs

  • 所 谓 iostream iterators,可以将迭代器绑定到某个iostream对象身上。绑定 到 istream 对 象 (例 如 std::cin)身上的,称 为 istream _ iterator,拥有输入功 能 ; 绑 定 到 ostream 对 象 (例 如 std::cout ) 身 上 的 ’称为 ostream _iterator,拥有输出功能。这种迭代器运用于屏幕输出,非常方便。以它为蓝图,稍加修改,便可适用于任何输出或输入装置上.例如,你可以在透彻了解 iostream Iterators的技术后,完成一个绑定到Internet Explorer cache身上的迭代器1 , 或是完成一个系结到磁盘目录上的一个迭代器2。
  • 请注意,不像稍后即将出场的仿函数配接器(functoradapters)总以仿函数作为参数,予 人 以 “拿某个配接器来修饰某个仿函数”的直观感受,这里所介绍的迭代器配接器(iterator adapters)很少以迭代器为直接参数% 所谓对迭代器的修 饰,只是一种观念上的改变(赋值操作变成插入操作啦、前进变成后退啦、绑定到特殊装置上啦…) 。你可以千变万化地写出适合自己所用的任何迭代器• 就这一点而言,为了将S T L 灵活运用于你的日常生活之中,iterator adapters的技术是非常重要的。
#include <iostream>
#include <algorithm>
#include <iterator>
#include <set>
#include <vector>
#include <functional>
#include <deque>template<class T>
struct display{void operator()(const T&x){std::cout << x << ' ';}
};struct even{bool operator()(int x)const{return x%2 ? false : true;}
};int main(int argc,char* argv[]){//将 outite绑定到cout 每次对outite指派一个兀素,就后接一个 ""std::ostream_iterator<int>outite(std::cout," ");int ia[] = {0,1,2,3,4,5};std::deque<int>id(ia,ia+6);//将所有元素拷贝到outite (那么也就是拷贝到cout)std::copy(id.begin(),id.end(),outite); //输出 0 1 2 3 4 5std::cout << std::endl;// 将 ia []的部分元素拷贝到id 内。使 用 front_insert_iterator<>// 注意,front_insert_iterqtor 会将 assign 操作改为 push_front 操作// vector不支持push_front (),这就是本例不以vector为示范对象的原因。std::copy(ia+1,ia+2,std::front_inserter(id));std::copy(id.begin(),id.end(),outite); //输出 1 0 1 2 3 4 5std::cout << std::endl;//将 ia []的部分元素拷贝到i d 内。使 用 back_insert_iterator.std::copy(ia+3,ia+4,std::back_inserter(id));std::copy(id.begin(),id.end(),outite); //输出 1 0 1 2 3 4 5 3std::cout << std::endl;//搜索元素 5 所在的位置std::deque<int>::iterator ite = std::find(id.begin(),id.end(),5);//将 ia[]的部分元素拷贝到i d 内。使 用 insert_iteratorstd::copy(ia+0,ia+3,std::inserter(id,ite));std::copy(id.begin(),id.end(),outite); //输出 1 0 1 2 3 4 0 1 2 5 3std::cout << std::endl;//将所有的元素逆向拷贝//rbegin ()和 rend ()与 reverse_iterator有关,见稍后源代码说明std::copy(id.rbegin(),id.rend(),outite);std::cout << std::endl;//以下,将inite绑定到cin.将元素拷贝到inite,直到eos出现std::istream_iterator<int>inite(std::cin),eos;//eos :end-of-streamstd::copy(inite,eos,std::inserter(id,id.begin()));// 由于很难在键盘上直接输入end-of-stream (end-of-file)符号// (而且不同系统的eof符号也不尽相同),因此,为了让上一行顺利执行,// 请单独取出此段落为一个独立程序,并准备一个文件,例 如 int.dat,内置 // 32 26 99 (自由格式),并 在 console之下利用piping方式执行该程序,如下:// c :\>type int.dat I thisprog / / 意思是将type int .dat的结果作为thisprog的输入std::copy(id.begin(),id.end(),outite);return 0;
}

8.1.3 应用于仿函数 , functor adapters

  • unctor adapters (亦称为function adapters) 是所有配接器中数量最庞大的一个族群,其配接灵活度也是前二者所不能及,可以配接、配接、再配接。这些配接操作包括系结(bind)、否 定 (negate), 组 合 (compose)、以及对一般函数或成员函数的修饰(使其成为一个仿函数)。 Standard规定这些配接器的接口可由<functional>获得,SGI S T L 则将它们实际定义于 <stl_function.h>
  • function adapters的价值在于,通过它们之间的绑定、组合、修饰能力,几乎 可以无限制地创造出各种可能的表达式(expression), 搭配STL算法一起演出
  • 例如,我们可能希望找出某个序列中所有不小于12的元素个数.虽然, “不小于”就是 “大于或等于”,我们因此可以选择STL内建的仿函数greater_equal,但 如果希望完全遵循题目语意(在某些更复杂的情况下,这可能是必要的),坚持找出 “不小于” 12的元素个数,可以这么做:

  •  请注意,所有期望获得配接能力的组件,本身都必须是可配接的(adaptable) 换句话说,一元仿函数必须继承自unary_function (7.1.1节 ),二元仿函数必须继承自binary_function (7.1.2 节 ) ,成员函数必须以mem_fun处理过,一般函数必须以ptr_fun处理过。一个未经ptr_fun处理过的一般函数,虽然也可以函 数指针(pointer to function)的形式传给STL算法使用 却无法拥有任何配接能力。

void print(int i){std::cout << i << std::endl;
}class Int{
public:explicit Int(int i) : m_i(i){};//这里有个既存的成员函数(稍后希望于STL体系中被复用)void print1() const {std::cout << '[' << m_i << ']';}private:int m_i;
};int main(int argc,char* argv[]){//将 outite绑定到cout 每次对outite指派一个兀素,就后接一个 ""std::ostream_iterator<int>outite(std::cout," ");int ia[6] = {2,21,12,7,19,23};std::vector<int>iv(ia,ia+6);//以下将所有元素拷贝到 outite. 有数种办法std::copy(iv.begin(),iv.end(),outite);//1,以函数指针搭配STL算法std::for_each(iv.begin(),iv.end(), print);//2,以修饰过的一般函数搭配STL算法std::for_each(iv.begin(),iv.end(),std::ptr_fun(print));Int t1 (3), t2 (7), t3 (20),t4(14), t5(68);std::vector<Int> Iv;Iv.push_back(t1);Iv.push_back(t2);Iv.push_back(t3);Iv.push_back(t4);Iv.push_back(t5);//3,以下,以修饰过的成员函数搭配STL算法std::for_each(Iv.begin(),Iv.end(),std::mem_fun_ref(&Int::print1));return 0;
}

 8.2 container adapters

8.2.1 stack
stack的底层由deque构成。从以下接口可清楚看出stack与deque的关系:

  •  C++ standard 规定客户端必须能够从<stack>中获得stack的接口,SGI STL 则把所有的实现细节定义于的<stl_stack.h>内,请参考4.5节。class stack封住了所有的deque对外接口,只开放符合stack原则的几个函数,所以我们说 stack是一个配接器,一个作用于容器之上的配接器。

 8.2.2 queue

  • queue的底层由deque构成。从以下接口可清楚看出queue与deque的关系:

  •  规定客户端必须能够从 <queue> 中获得queue的接口,SGI STL 则把所有的实现细节定义于<stl_queue.h>内,请参考4.6节。class queue封住了所有的deque对外接口,只开放符合queue原则的几个函数,所以我们说 queue是一个配接器,一个作用于容器之上的配接器。

8.3 iterator adapters

  • 本章稍早已说过iterator adapters的意义和用法,以下研究其实现细节

8.3.1 insert iterators

  • 下面是三种insert iterators的完整实现列表。其中的主要观念是,每一个 insert iterators内部都维护有一个容器(必须由用户指定):容器当然有自己的迭代器,于是,当客户端对insert iterators做赋值(tm i敢)操作时,就在insert iterators中被转为对该容器的迭代器做插入(insert)操作,也就是说,在 insert iterators的operator =操作符中调用底层容器的push_front ()或push_back ()或 insert () 操 作 函 数 。
  • 至 于 其 它 的 迭 代 器 惯 常 行 为 如 operator++, operator++ (int)  operator*都被关闭功能,更 没 有 提 供 operator-- (int)或 operator--或 operator->等功能(因此被类型被定义为output„iterator_tag)
  •  换句话说,insert iterators的前进、后退、取值、成员取用等操作都是没有意义的,甚至是不允许的。

8.3.2 reverse iterators

  • 所谓reverse iterator,就是将迭代器的移动行为倒转。如果STL算法接受的不 是一般正常的迭代器,而是这种逆向迭代器,它就会以从尾到头的方向来处理序列中的元素。例如:

  • 首先我们看看rbeginO和 rend().任何STL容器都提供有这两个操作, 我在第4, 5两章介绍各种容器时,鲜有列出这两个成员函数,现在举个例子瞧瞧:

  •  没有任何例外!只要双向序列容器提供了 begin(), end(),它 的 rbegin、rend() 就是上面那样的型式。单向序列容器如slist不可使用reserve iterators. 有些容器如 stack、queue、priority .qu eue 并不提供begin(), end(), 当然也就没有 rbegin/ rend()

  •  为什么 “正向迭代器”和 “与其相应的逆向迭代器”取出不同的元素呢?这并不是一个潜伏的错误,而是一个刻意为之的特征,主要是为了配合迭代器区间的“前闭后开”习 惯 (1.9.5节 ) . 从图8-3的 rbeginO和 end()关系可以看出, 当迭代器被逆转方向时,虽然其实体位置(真正的地址)不变,但其逻辑位置(迭代器所代表的元素)改变了(必须如此改变):

  •  唯有这样,才能保持正向迭代器的一切惯常行为。换句话说,唯有这样,当我们将一个正向迭代器区间转换为一个逆向迭代器区间后,不必再有任何额外处理,就可以让接受这个逆向迭代器区间的算法,以相反的元素次序来处理区间中的每一个元素.例如(以下出现于8.1.2节实例之中):
  • 有了这些认知,现在我们来看看reverse_iterator的源代码
  •  这是一个迭代器配接器(iter己tor adapter), 用来将某个迭代器逆反前进方向
  • 使前进为后退,后退为前进

 

 

8.3.3 stream iterators 

  • 所 谓 streamfterotors,可以将迭代器绑定到一个stream (数据流)对象身上。 绑 定 到 istrea m对象 (例如 std::cin ) 者,称 为 istream_iterator ,拥有输入能力;
  • 绑定到ostream 对 象 (例 如 std:: cout) 者,称为 拥有输出能力。两者的用法在8.1.2节的例子中都有示范
  • 乍听之下真神奇。所谓绑定一个i stream object,其实就是在isiream iterator内部维护一个istream member,客户端对于这个迭代器所做的operator++操 作 ,会 被 导 引 调 用 迭 代 器 内 部 所 含 的 那 个 istream member的输入操作(operator») 。这个迭代器是个Input Iterator,不具备operator--o下面的源代码和注释说明了一切。

 8,4 function adapters

  • 一般而言,对于C++ template语法有了某种程度的了解之后,我们很能够理解或想象,容器是以class templates完成,算 法 以 function templates完成,仿函数是一种将operator() 重 载 的 class template,
  • 迭代器则是一种将 operator++和 operator*等指针习惯常行为重载的class template.然而配接器呢?应用于容 器身上和迭代器身上的配接器,已于本章稍早介绍过,都是一种class template.可 应用于仿函数身上的配接器呢?如 何 能 够 “事先”对一个函数完成参数的绑定、
    执行结果的否定、甚至多方函数的组合?请注意我用“事先” 一词。我的意思是,最后修饰结果(视为一个表达式,expression) 将被传给STL算法使用,STL算法 才是真正使用这表达式的主格。而我们都知道,只有在真正使用(调用)某个函数(或仿函数)时,才有可能对参数和执行结果做任何干涉。

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

  • 以下直接列出源代码。源代码中的注释配合先前的概念解说,应该足以让你彻底认识这些仿函数配接器。源代码中常出现的pred 一词,是 predicate的缩写, 意指会返回真假值(bool)的表达式。

 

#include <iostream>
#include <algorithm>
#include <iterator>
#include <set>
#include <vector>
#include <functional>
#include <deque>class Shape{
public:virtual void display() = 0;
};class Rect : public Shape{
public:virtual void display() {std::cout << "Rect ";}
};class Circle : public Shape{
public:virtual void display() {std::cout << "Circle ";}
};class Square : public Shape{
public:virtual void display() {std::cout << "Square ";}
};int main(int argc,char* argv[]){std::vector<Shape*>Iv{};Iv.push_back(new Rect);Iv.push_back(new Circle);Iv.push_back(new Square);Iv.push_back(new Circle);Iv.push_back(new Rect);//打印  输出for (int i = 0; i < Iv.size(); ++i) {(Iv[i])->display();}std::cout << std::endl;std::for_each(Iv.begin(),Iv.end(),std::mem_fun(&Shape::display));std::cout << std::endl;return 0;
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446422.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中科大 计算机网络3 网络边缘Edge

网络结构 边缘系统 网络核心 接入网 方块&#xff1a;边缘系统(主机) 圆的&#xff1a;网络核心&#xff0c;数据交换作用 连接边缘系统和网络核心的叫做接入网&#xff08;access&#xff09;&#xff0c;把边缘的主机接入到网络核心&#xff08;所以是分布式的&#xff09; …

STL源码剖析 入门开始 STL概论与版本简介

源代码之中时而会出现一些全局函数调用操作&#xff0c;尤其是定义于<stl_construct.h> 之中用于对象构造与析构的基本函数&#xff0c;以及定义于<stl_uninitialized.h>之 中 用 于 内 存 管 理 的 基 本 函 数 &#xff0c; 以及定义于<stl_algobase.h>之中…

中科大 计算机网络4 网络核心Core 分组交换 电路交换

网络核心 电路交换&#xff08;线路交换&#xff09;&#xff1a;打电话之前&#xff0c;先建立一条链路&#xff08;物理&#xff09; 分组交换&#xff1a;存储转发的方式 电路交换&#xff08;线路交换&#xff09; 通过信令&#xff08;控制信息&#xff0c;如&#xf…

STL 源码剖析 空间配置器

以STL的运用角度而言&#xff0c;空间配置器是最不需要介绍的东西&#xff0c;它总是隐藏在一切组件&#xff08;更具体地说是指容器&#xff0c;container&#xff09; 的背后但是STL的操作对象都存放在容器的内部&#xff0c;容器离不开内存空间的分配为什么不说allocator是内…

中科大 计算机网络7 分组延迟 分组丢失 吞吐量

分组丢失和延迟的原因 队列太长没有意义&#xff0c;用户需求 排队&#xff1a;输出能力<到来的分组&#xff0c;需要等待 四种分组延迟 节点处理延迟&#xff1a;确定的 排队延迟&#xff1a;随机&#xff0c;取决于网络情况 一个比特的传输时间&#xff1a; R1Mbps …

STL源码剖析 迭代器iterator的概念 和 traits编程技法

iterator模式定义如下&#xff1a;提供一种方法&#xff0c;使之能够依序巡访某个 聚合物(容器)所含的各个元素&#xff0c;而又无需暴露该聚合物的内部表述方式.STL的中心思想在于&#xff1a;将数据容器(containers)和算法(algorithms)分开&#xff0c;彼此独立设计&#xff…

中科大 计算机网络11 应用层原理

应用层大纲 传输层向应用层提供的服务&#xff0c;形式是Socket API&#xff08;原语&#xff09; 一些网络应用的例子 互联网层次中&#xff0c;应用层协议最多 流媒体应用&#xff1a;直播 网络核心最高的层次就是网络层 应用进程通信方式 C/S&#xff1a; 客户端&…

STL源码剖析 序列式容器 vector 和 ilist

Vector list 单向链表 ilistlist的删除操作&#xff0c;也只有指向被删除元素的迭代器会失效&#xff0c;其他迭代器不会受到影响

中科大 计算机网络5 接入网和物理媒体

接入网 接入网&#xff1a;把边缘&#xff08;主机&#xff09;接入核心&#xff08;路由器&#xff0c;交换机&#xff09; 骨干网【连接主机和主机】和接入网中都有物理媒体 接入方式&#xff1a;有线和无线 带宽共享/独享 接入网&#xff1a;住宅接入modem modem调制解调…

STL源码剖析 序列式容器 deque双端队列

相较于vector的内存拷贝&#xff0c;deque在内存不足时只需要进行内存的拼接操作即可&#xff0c;不需要重新配置、复制、释放等操作&#xff0c;代价就是迭代器的架构不是一个普通的指针&#xff0c;比较复杂d e q u e 的迭代器 deque是分段连续空间。维持其“整体连续”假象…

中科大 计算机网络6 Internet结构和ISP

互联网的结构 端系统通过接入ISPs接入互联网 n个ISP互相连接&#xff1a; IXP,Internet exchage point:互联网接入点&#xff0c;互联网交互点 ISP&#xff1a;互联网服务提供商&#xff0c;提供接入&#xff0c;提供网络【中国移动&#xff0c;中国电信】 ICP&#xff1a…

STL源码剖析 Stack栈 queue队列

随机迭代器用于随机数据访问&#xff0c;所以栈stack不具备此功能

中科大 计算机网络8 协议层次和服务模型

协议层次 协议层次&#xff1a;现实生活中的例子 分层 分层处理和实现复杂系统 图中&#xff0c;左边是模块&#xff0c;右边是分层 计算机的设计是分层&#xff0c;每一层实现一个或一组功能&#xff0c;下层向上层提供服务&#xff1b;但效率比较低 对等层实体通过协议来交换…

STL源码剖析 heap堆结构

heap一般特指max-heap&#xff0c;即最大的元素位于heap和array的首部 heap不提供遍历功能&#xff0c;也不提供迭代功能

中科大 计算机网络9 互联网历史

总纲 计算机网络 早期1960以前 1961-1972 NCP协议&#xff1a;相当于现在的TCP和IP协议 每个节点即是数据的源也是数据的目标

STL源码剖析 序列式容器 slist

STL l i s t 是个双向链表(double linked lis t) 。SGI STL提供了一个单向链 表 (single linked lis t) , 名 为 slist s l i s t 和 l i s t 的主要差别在于&#xff0c;前者的迭代器属于单向的Forwardlterotor, 后者的迭代器属于双向的Bidirectional Iterator.为此&#xff0…

中科大 计算机网络12 Web和HTTP

Web与HTTP 对象&#xff1a;web页中其实是对象链接 URL&#xff1a;通用资源定位符【任何对象都可以使用URL来唯一标识】 用户名&#xff1a;口令【支持匿名访问&#xff0c;用户名和口令不计】 端口&#xff1a;HTTP&#xff1a;80 FTP&#xff1a;21【使用默认端口号&#x…

STL源码剖析 关联式容器 树 红黑树、二叉搜索树、平衡二叉搜索树

所谓关联式容器&#xff0c;观念上类似关联式数据库(实际上则简单许多)&#xff1a;每笔数据(每个元素)都有一个键值(key)和一个实值(value) 2。当元素被插入到关联式 容器中时&#xff0c;容器内部结构(可能是RB-tree,也可能是hash-table)便依照其键 值大小&#xff0c;以某种…

北京大学 软件工程1 软件 软件工程 软件开发 软件工程框架

软件的定义 重新定义软件 新一代信息技术 区块链 创造性思维 软件的特点 软件的种类 支撑软件&#xff1a;VC&#xff0c;PyCharm等 应用软件&#xff1a;QQ&#xff0c;微信 软件工程的起源 软件开发的三个阶段 软件工程概念的提出 软件工程的定义 软件工程将系统化&#…