Standard Template Library(STL,标准模板库)

article/2025/9/30 0:47:14

Standard Template Library(STL,标准模板库)

STL(标准模板库)是C++标准程序库的核心,它深刻影响了标准程序库的整体结构。

STL是一个泛型(generic)程序库,提供一系列软件方案,利用先进,高效的算法来管理数据。

STL的所有组件都由template(模板)构成,故其元素可以是任意型别。

STL组件(STL Components)

通过C++开发者们精心设计的组件,共同构筑起了STL的基础。而在这些组件之中最为重要的要数迭代器,容器,算法

  • 容器 Containers ,用来管理某类对象的合计。STL库的中每一个容器都会有其自身的优缺点,所以为了应付程序中的不同需求,STL准备了不同的容器类型,例如dynamic arrays,linked list,binary tree等
  • 迭代器 Iterators,用来在一个对象群集的元素上进行相关的遍历动作。而使用迭代器的主要好处则是,为所有的容器提供一组很小的且相对安全的公共接口。而利用这个接口,某项操作就可以在这个容器对象的集群运行,或者行进至群集内的下一个元素

(注:迭代器的接口和一般的指针差不多,以operator++累进,以operator* 指向所指的元素值。因而,我们可以将迭代器视为一种smart poiner,能够把“前进至下一个元素”的意图转换成合适的操作

  • 算法 Algorithms,用来处理群集内的元素

STL的基本观念就是将数据和操作分离。(重中之重)

数据由容器类别加以管理,操作则由可定制的算法定义之。迭代器在二者之间从当粘合剂,使其任何算法都可以和任何容器交互运作

在这里插入图片描述

STL的一个根本特性:所有组件都可以针对任意性别运作。顾名思义,所谓的Standard template library表示其内的所有组件都是“可接受任意型别”的template,前提是这些型别能够执行必要的操作。故,STL成了泛型编程(generic programming)概念下的一个出色案例

STL当然也能够提供更加泛型化的组件。通过特定的配接器和仿函数,使用者可以补充,约束或者订制算法,满足特别的需求

容器 (Containers)

容器类别(简称为容器)用来管理一组元素。为了适应不同的需求,STL提供了不同类型的容器

在这里插入图片描述

总的来说,STL的容器可分为两类:

  1. 序列式容器Sequence containers,此乃可序群集,其中的每个元素均有固定位置—取决于插入元素的时机和其地址,与元素值本身无关。STL提供三个定义好的序列式容器:vector,deque,list
  2. 关联式容器Associative container,此乃已序群集,元素位置取决于特定的排序准则。如果你将六个元素置于这样的群集中,则它们的位置取决于元素本身的值,和元素的插入次序无关。STL提供了四个关联式容器:set,multiset,map,multimap

关联式容器也可被视为特殊的序列式容器,因为已序群集正是根据某个排序准则排列而成

**注意事项:**STL所提供的群集型别彼此独立,各自实现,毫无关联,即其间并无classes继承关系

关联式容器自动对其元素进行排序,这并不意味着它就是用来排序的。当然,STL的使用者可以对序列式容器的元素加以手动排序。而关联式自动对其元素进行排序的主要优点是,当你搜寻元素时,可获得更佳的效率,自动排序只不过是关联式容器的一个有用的副作用而已

注:客观来说,对关联式容器插入大量元素进行排序与一次性对Vector插入大量元素在进行排序所损耗的时间相比,vector所耗费的时间更少,效率更佳

序列式容器(Sequence containers)

STL内部预先定义好了以下三个序列式的容器:

  • vector
  • deque
  • list

此外,你也可将string和array当作一种序列式容器

Vector

​ Vector将其元素置于一个dynamic array中加以管理。它允许随机存取,也就是你可以利用索引直接存取任何一个元素。在vector尾部附加元素或者移除元素均非常的快速,但在array中部或者头部安插元素就比较费时费力了

#include<iostream>
#include<vector>
using namespace std;int main()
{vector<int> vi;for(int i=1;i<=6;++i)vi.push_back(i);for(int i=0;i<vi.size();++i)cout<<vi[i]<<ends;cout<<endl;return 0;
}

程序输出结果:

1 2 3 4 5 6

知识点:

  • #include< vector >含入vectors的头文件
  • push_back()函数可为容器的尾部安插元素
  • size()成员函数返回容器中的元素的个数
  • 可通过subscript(下标)操作符[],存取vector内的某个元素
Deque

​ 所谓deque,是“double-ended queue”的缩写。它是一个dynamic array,可以向两端发展,因此不论在尾部或者是头部安插元素都十分的迅速。在中间部分安插元素则比较费时费力,因为必须移动其他元素

#include<iostream>
#include<deque>
using namespace std;int main()
{deque<double> dd;for(int i=1;i<=6;++i)dd.push_front(i*1.1);for(auto it=dd.begin();it!=dd.end();++it)cout<<*it<<ends;cout<<endl;return 0;
}

程序输出结果:

6.6 5.5 4.4 3.3 2.2 1.1

知识点:

  • #include< deque>含入deques的头文件
  • push_front()函数可为容器的头部安插元素
  • begin()成员函数返回容器中第一个元素的地址,end()成员函数返回容器中最后一个元素的地址

push_front()函数会将元素安插于群集前端。需要注意的是,这种安插方式造成的结果就是,元素排放的次序与安插次序恰好相反,因为每个元素都安插于上一个元素的前面

当然,我们也可使用成员函数push_back()在deque尾端附加元素。vector并未提供push_front()函数,因为这样做的话,其时间性能很差,在vector的头端安插一个元素,需要移动全部的元素!

一般而言,STL容器只提供通常具备良好时间效能的成员函数(所谓“良好”的时间效能,通常意味着具有常数复杂度或者对数复杂度),以防止程序员调用性能很差的函数

List

​ List由双向链表(double linked list)实作而成。这意味着list内的每个元素都以一部分内存指示其前趋元素和后继元素。List不提供随机存取

List的优势:在任何位置上执行安插或删除动作动作都非常迅速,因为只须改变链接(links)就行。这表示在list中间位置移动元素比在vector和deque快得多

#include<iostream>
#include<list>
using namespace std;int main() 
{list<char> lc;for(char c='a';c<='z';++c)lc.push_back(c);while(!lc.empty()) {cout<<lc.front()<<ends;lc.pop_front();}cout<<endl;return 0;
}

程序输出结果:

a b c d e f g h i j k l m n o p q r s t u v w x y z

知识点:

  • #include< list >含入list的头文件
  • empty()成员函数的返回值告诉我们容器中是否还有元素
  • front()成员函数会返回容器的第一个元素
  • pop_front()成员函数会删除容器中的第一个元素,需要注意的是pop_front()并不会返回被删除的元素

lists并没有提供以operator[]直接存取元素的能力,因为lists不支持随机存取,如果采用operator[]会导致不良效能。使用迭代器一样可以遍历并打印所有元素

Arrays

​ 这种容器不是类别(class),而是C/C++语言核心所支持的一个型别(type):具有静态大小或者动态大小的array

#include<iostream>
#include<array>
using namespace std;int main()
{array<int,6> ai = { 1,2,3,4,5,6 };for(auto it=ai.begin();it!=ai.end();++it)cout<<*it<<ends;cout<<endl;return 0;
}

程序输出结果:

1 2 3 4 5 6

关联式容器(Associative Containers)

​ 关联式容器依据特定的排序准则,自动为其元素排序。排序准则以函数形式呈现,用来比较元素值(value)或元素键(key)。通常关联式容器由二叉树(binary tree)视作出来。在二叉树中,每个元素(节点)都有一个父节点和两个子节点;左子树的所有元素都比自己小,右子树的所有元素都比自己大

关联式容器的差别主要在于元素的类型以及处理重复元素时的方式(态度)

  • Sets

Set的内部元素依据其值自动排序,每个元素值只能出现一次,不允许重复

  • Multisets

Multisets和set相同,只不过它允许重复元素,也就是说multiset可包含多个数值相同的元素

  • Maps

Map的元素都是“实值/键值”所形成的一个对组(key/value pairs)。每个元素有一个键,是排序准则的基础。每一个键只能出现一次,不允许重复。Map可被视为关联式数组,也就是具有任意索引型别的数组

  • Multimaps

Multimap和map相同,但允许重复元素,也就是说multimap可包含多个键值(key)相同的元素。Multimap可被当作“字典”使用

我们可以将set视为一种特殊的map:其元素实值就是键值。实际产品中,所有这些关联式容器通常都由二叉树(binary tree)实作而成

Set和Muliset
#include<iostream>
#include<set>
using namespace std;int main()
{typedef set<int> IntSet;IntSet I_set;I_set.insert(3);I_set.insert(5);I_set.insert(1);I_set.insert(2);I_set.insert(4);I_set.insert(1); // 重复插入,忽略该操作 IntSet::iterator it;for(it=I_set.begin();it!=I_set.end();++it)cout<<*it<<ends;cout<<endl;return 0;} 

程序输出结果:

1 2 3 4 5

知识点:

  • #include< set >含入set和muliset的头文件
  • 所有关联式容器都提供有一个insert()成员函数,用来安插新元素,新元素会根据排序准则自动安插到正确的位置
  • 需要注意的是,我们不能使用序列容器的push_back()和push_front()函数,它们在这里毫无意义,因为我们没有权力指定新元素的位置
  • 如果你想用multiset而不是set,唯一需要改变的就是容器的型别(set和multiset的设定被置于同一个头文件中)
Maps和Multimaps

​ Map的元素是成对的键值/实值(key/valule)。因此其声明,元素安插,元素存取皆和set有所不同,以下为multimap的使用案例

#include<iostream>
#include<map>
#include<string>
using namespace std;int main()
{typedef multimap<int,string> IntStringMMap;IntStringMMap coll;coll.insert(make_pair(5,"tagged"));coll.insert(make_pair(2,"a"));coll.insert(make_pair(1,"this"));coll.insert(make_pair(4,"of"));coll.insert(make_pair(6,"strings"));coll.insert(make_pair(1,"is"));coll.insert(make_pair(3,"multimap"));IntStringMMap::iterator it;for(it=coll.begin();it!=coll.end();++it) cout<<it->second<<ends;cout<<endl;return 0;}

程序输出结果:

this is a multimap of tagged strings

知识点:

  • #include< map >含入map和mulimap的头文件
  • map容器中的元素是成对的键值/实值(key/value pair),所以你必须首先生成这个pair,再将它插入群集内部。辅助函数make_pair()正是为了这个目的而打造的
  • 迭代器所指的是“键值/实值”对组(key/value pair),因此我们无法一口气打印它们,必须取出pair的成员,亦即所谓的first和second

将Maps当做关联式数组

一个“键值/实值”对组所形成的群集中,如果所有键值都是独一无二的,我们可将它视为一个关联式数组(associative array)

#include<iostream>
#include<map>
#include<string>
using namespace std;int main()
{typedef map<string,double> StringDoubleMap;StringDoubleMap Dic;// 如果我们指定一个容器里不存在的索引,会导致产生一个对应的新元素 Dic["VAT"] = 0.15;Dic["Pi"] = 3.1415;Dic["an arbitrary number"] = 12345.4321;Dic["Null"] = 0;StringDoubleMap::iterator it;for(it=Dic.begin();it!=Dic.end();++it)cout<<"key: "<<it->first<<ends<<"value: "<<it->second<<endl;return 0;
}

程序输出结果:

key: Null value: 0
key: Pi value: 3.1415
key: VAT value: 0.15
key: an arbitrary number value: 12345.4

知识点:

  • 当我们声明容器型别时,必须同时指定键值(key)和实值(value)的型别(type)

  • Maps允许我们使用operator[]安插元素

  • 我们俗称的关联数组就是,索引可以采用任何型别(type)

  • Multimaps不允许我们使用subscript(下标)操作符,因为multimaps允许单一索引对应到多个不同元素,而下标操作符却只能处理单一实值

  • 存取multimaps或maps的元素时,我们必须透过pair结构的first成员和second成员,才能取得键值(key)和实值(value)

容器配接器(Container Adapter)

​ 除了以上数个根本的容器类别,为满足特殊的需求,C++标准程序库还提供了一些特别的(并且预先定义好的)容器配接器,根据基本容器类别实作而成

  • Stacks

Stack容器对容器采取LIFO(后进先出)管理策略

  • Queues

Queue容器对元素采取FIFO(先进先出)管理策略。也就是说,它是一个普通的缓冲区(buffer)

  • Priority Queues

Priority Queues容器中的元素可以拥有不同的优先权。该容器所谓的优先权,乃是基于程序员提供的排序准则(缺省时使用operator<)而定义。Priority Queues的效果相当于这样的一个buffer:“下一个元素永远是queue中优先级最高的元素”

迭代器(Iterators)

​ 迭代器是一个“可遍历STL容器内全部或部分元素”的对象。一个迭代器用来指出容器中的一个特定位置

基本操作如下:

  • operator*

返回当前位置上的元素值。如果该元素拥有成员,你可以透过迭代器,直接以operator->取用它们

  • operator++

将迭代器前进至下一元素。大多数迭代器还可使用operator–退回到前一个元素

  • operator==和operator!=

判断两个迭代器是否指向同一位置

  • operator=

为迭代器赋值(将其所指元素的位置赋值过去)

以上这些操作和C/C++“操作array元素”时的指针接口一致。其不同之处在于,迭代器是个所谓的smart pointers,具有遍历复杂数据结构的能力,迭代器下层的运行机制取决于其所遍历的数据结构。因此,每一种容器类型都必须提供自己的迭代器

事实上,每一种容器都将其迭代器以嵌套的方式定于容器的内部。因此我们可以发现,各种迭代器的接口相同,型别却不同

而以上的思想便因此引出了泛型程序设计的概念:所有操作行为都使用相同接口,虽然它们的型别不同。因此,我们可以使用template将泛型操作公式化,使之得以顺利运行那些“能够满足接口需求”的任何型别

容器类型中,与迭代器相关且经常使用的成员函数:

  • begin()

返回一个迭代器,指向容器的起始点,也就是第一元素(如果有的话)的位置

  • end()

返回一个迭代器,指向容器的结束点。结束点在最后一个元素之后,这样的迭代器又称为“逾尾(past-the-end)”迭代器

通过以上两个成员函数,begin()和end()形成了一个半开区间(half-open range),从第一个元素开始,到最后一个元素的下一个位置结束。

在这里插入图片描述

这样的半开区间有两个优点:

  1. 为“遍历元素时,循环的结束时机”提供了一个简单的判断依据。只要尚未到达end(),循环就可以继续进行
  2. 不必对空区间特殊处理手法。空区间的begin()就等于end()

迭代器简单使用的案例:

#include<iostream>
#include<list>
using namespace std;int main()
{list<char> lc;for(char c='a';c<='z';++c)lc.push_back(c);list<char>::const_iterator cit;for(cit=lc.begin();cit!=lc.end();++cit)cout<<*cit<<ends;cout<<endl;return 0; 
}

程序输出结果:

a b c d e f g h i j k l m n o p q r s t u v w x y z

知识点:

  • 迭代器cit声明于循环之前,其型别是“指向容器中的不可变元素”的迭代器
  • 任何一种容器都定义有两种迭代器型别:
  1. container::iterator - 这种迭代器以“读/写”模式遍历元素
  2. container::const_iterator - 这种迭代器以“只读”模式遍历元素

总的来说,迭代器cit从第一个元素开始,逐一访问了每一个元素,直到抵达结束点为止。如果容器内没有任何元素,lc.begin()等于lc.end(),循环根本不会执行

在这里插入图片描述

循环代码:

list<char>::const_iterator cit;for(cit=lc.begin();cit!=lc.end();++cit)cout<<*cit<<ends;

需要我们注意的是,在该循环的代码中,我们使用了“前置型递增(preincrement)"++cit,因为它比”后置式递增(postincrement)“cit++效率高。因为,后置式的递增需要一个额外的临时对象,因为它必须存放迭代器的原本位置并将它返回,所以一般情况下最好使用++cit

故,我们建议优先采用前置式递增和前置式递减操作符

迭代器分类

​ STL总是只提供效率上比较出色的操作,因此,如果容器允许随机存取(例如vectors或deques),那么它们的迭代器也能进行随机操作

STL预先定义好的所有容器,其迭代器均属于以下两种类型:

  • 双向迭代器(Bidirectional iterator)

顾名思义,双向迭代器可以进行双向行进:以递增运算前进或以递减运算后退。list,set,multiset,map和multimap这些容器所提供的迭代器都属此类

  • 随机存取迭代器(Random access iterator)

随机存取迭代器不但具备双向迭代器的所有属性,还具备随机访问的能力。更明确地说,它们提供了“迭代器算术运算”必要的操作符(和“一般指针的算术运算”完全对应)。vector,deque和strings所提供的迭代器都属此类

注:但,为了撰写尽可能与容器型别无关的泛型程序代码,我们最好不要使用随机存取迭代器所特有的操作

算法(Algorithms)

​ 为了处理容器内的元素,STL提供了一些标准算法,包括搜寻,排序,拷贝,重新排序,修改,数值运算等十分基本而普遍的算法

算法并非容器类别的成员函数,而是一种搭配迭代器使用的全局函数,这样的好处在于,所有算法只需实作一份,就可以对所有容器运作,不必为每一种容器量身定制

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;int main()
{vector<int> vi;vector<int>::iterator it;vi.push_back(2);vi.push_back(1); vi.push_back(5);vi.push_back(4);vi.push_back(6);vi.push_back(3);it = min_element(vi.begin() ,vi.end());cout<<"min: "<<*it<<endl;it = max_element(vi.begin() ,vi.end());cout<<"max: "<<*it<<endl;sort(vi.begin() ,vi.end());for(it=vi.begin();it!=vi.end();++it)cout<<*it<<ends;cout<<endl;it = find(vi.begin(),vi.end(),3);reverse(it,vi.end());for(it=vi.begin();it!=vi.end();++it)cout<<*it<<ends;cout<<endl;return 0;  } 

程序输出结果:

min: 1
max: 6
1 2 3 4 5 6
1 2 6 5 4 3

知识点:

  • 为了调用算法,你必须含入头文件< algorithm >
  • 算法min_element()返回最小元素的位置(如果最小元素不只一个,则返回第一个最小元素的位置)
  • 算法max_element()返回最大元素的位置(如果最大元素不只一个,则返回第一个最小元素的位置)
  • 算法sort(),顾名思义,将“由两个参数设定出来”的区间内的所有元素加以排序,我们可以有选择性传入一个排序准则
  • 算法find(),它在给定范围中搜寻某个值,如果find()成功了,便返回一个迭代器,指向目标元素,如果失败,返回一个“逾尾”迭代器,亦即find()所接受的第二参数
  • 算法reverse(),将区间内的元素反转放置

处理多个区间

​ 有数个算法(或者说需要)同时处理多个区间,通常来看,我们必须设定第一个区间的起点和终点,至于其他区间,你只需设定起点即可,终点通常可由第一区间元素数量推导出来。

案例:算法equal()从头开始逐一比较vi_1和vi_2的所有元素

if(equal(vi_1.begin(),vi_1.end(),vi_2.begin())) {....
}

vi_2之中参与比较的元素数量,间接取决于vi_2内的元素数量

注:如果某个算法用来处理多个区间,那么当我们调用它时,务必确保第二(以及其他)区间拥有的元素个数,至少和第一区间内的元素个数相同。特别是,执行涂写动作时,务必确保目标区间够大

#include<iostream>
#include<vector>
#include<list>
#include<algorithm>
using namespace std;int main()
{list<int> li(9);vector<int> vi;for(int i=1;i<=9;++i)vi.push_back(i);copy(vi.begin(),vi.end(),li.begin());for(auto it=li.begin();it!=li.end();++it)cout<<*it<<ends;cout<<endl;return 0; 
}

程序输出结果:

1 2 3 4 5 6 7 8 9

知识点:

  • 算法copy(),将第一区间内的全部元素拷贝至目标区间,第一区间的起点和终点都已指定,第二区间只指出起点。然而,由于该算法执行的是覆写动作而非安插操作,所以目标区间必须拥有足够的元素来被覆写
  • 要想让目标区间够大,你要不一开始就给它一个正确大小,要不就显式地改变其大小。这两个办法都只适用于序列式容器(vectors,deques,lists)
  • 关联式容器不可能被当做覆写型算法的操作目标

迭代器之配接器(Iterator Adapters)

​ 迭代器(Iterators)是一个相对纯粹的抽象概念,任何的东西,只要其行为类似迭代器,它就是一个迭代器。C++标准程序库提供了数个预先定义的特殊迭代器,亦即所谓迭代器配接器(iterator adapters)。它们不仅起辅助作用,还能赋予整个迭代器抽象概念更强大的能力

以下介绍三种迭代器配接器(iterator adapters):

  1. Insert iterators(安插型迭代器)
  2. Stream iterators(流迭代器)
  3. Reverse iterators(逆向迭代器)

Insert Iterators(安插型迭代器)

​ Inserters可以使算法以安插(insert)方式而非覆写(overwrite)方式运作。使用它,可以解决算法的“目标空间不足”问题

Inserter iterators内部将接口做了新的定义:

  • 如果我们对某个元素设值(assign),会引发“对其所属群集的安插(insert)操作"。至于插入位置,则视三种不同的insert iterators而定
#include<iostream>
#include<vector>
#include<deque>
#include<list>
#include<set>
#include<algorithm>
#include<iterator>
using namespace std;void print(int val) {cout<<val<<ends;
}int main()
{list<int> li;for(int i=1;i<=9;++i)li.push_back(i);for_each(li.begin(),li.end(),print); cout<<endl<<endl;vector<int> vi;copy(li.begin(),li.end(),back_inserter(vi));for_each(vi.begin(),vi.end(),print);cout<<endl<<endl;deque<int> di;copy(li.begin(),li.end(),front_inserter(di));for_each(di.begin(),di.end(),print);cout<<endl<<endl;set<int> si;copy(li.begin(),li.end(),inserter(si,si.begin()));for_each(si.begin(),si.end(),print);cout<<endl;return 0;
}

程序输出结果:

1 2 3 4 5 6 7 8 91 2 3 4 5 6 7 8 99 8 7 6 5 4 3 2 11 2 3 4 5 6 7 8 9

知识点:

1.Back_inserters(安插于容器最尾端)

​ Back_inserters的内部调用push_back(),在容器尾端插入元素(即”追加“动作);只有在提供有push_back()成员函数的容器中,back_inserters才能派上用场,即vector,deque,list

2.Front_inserters(安插于容器最前端)

​ Front_inserters的内部调用push_front(),在容器尾端插入元素(即”追加“动作);只有在提供有push_front()成员函数的容器中,front_inserters才能派上用场,即deque和list

3.General inserters(一般性安插器)

​ 这种一般性的inserters,简称就叫inserts,它的作用是将元素插入”初始化时接收之第二参数“所指位置的前方。Inserts内部调用成员函数insert(),并以新值和新位置作为参数。所有STL容器都提供有insert()成员函数,因此,这是唯一可用于关联式容器身上的一种预先定义好的inserter

Stream Iterators(流迭代器)

​ 另一种非常有用的迭代器配接器是stream iterator,这是一种用来读写stream的迭代器

#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<iterator>
using namespace std;int main()
{vector<string> vs;copy(istream_iterator<string>(cin),istream_iterator<string>(),back_inserter(vs));sort(vs.begin(),vs.end());unique_copy(vs.begin(),vs.end(),ostream_iterator<string>(cout,"\n"));return 0;
}

知识点:

  1. istream_iterator<string>(cin) 这会产生一个可从”标准输入流cin“读取数据的stream iterator。其中template参数string表示这个stream iterator专门读取该种型别的元素的职责
  2. istream_iterator<string>()调用istream iterators的default构造函数,产生一个代表”流结束符号“的迭代器,它代表的意义是:你不能再从中读取任何东西
  3. 关于算法copy():只要不断逐一前进的那个第一参数不同于第二参数,算法copy()就持续动作
  4. 算法unique_copy()在处理的过程中会消除毗邻的重复值

Reverse Iterator(逆向迭代器)

​ 第三种预先定义的迭代器配接器就是reverse iterators,此物像是倒转经脉似地以逆向方式进行所有操作。它将递增运算转换韦递减运算,反之亦然。所有容器都可以透过成员函数rbegin()和rend()产生出reverse iterator

#include<iostream>
#include<vector>
#include<algorithm>
#include<iterator>
using namespace std;int main()
{vector<int> vi;for(int i=1;i<=9;++i)vi.push_back(i);copy(vi.rbegin(),vi.rend(),ostream_iterator<int>(cout," "));cout<<endl;return 0; 
}

程序输出结果:

9 8 7 6 5 4 3 2 1

知识点:

  • 当某个位置上并没有任何合法元素时,我们永远不要使用operator* 或 operator->
    eam iterator。其中template参数string表示这个stream iterator专门读取该种型别的元素的职责
  1. istream_iterator<string>()调用istream iterators的default构造函数,产生一个代表”流结束符号“的迭代器,它代表的意义是:你不能再从中读取任何东西
  2. 关于算法copy():只要不断逐一前进的那个第一参数不同于第二参数,算法copy()就持续动作
  3. 算法unique_copy()在处理的过程中会消除毗邻的重复值

Reverse Iterator(逆向迭代器)

​ 第三种预先定义的迭代器配接器就是reverse iterators,此物像是倒转经脉似地以逆向方式进行所有操作。它将递增运算转换韦递减运算,反之亦然。所有容器都可以透过成员函数rbegin()和rend()产生出reverse iterator

#include<iostream>
#include<vector>
#include<algorithm>
#include<iterator>
using namespace std;int main()
{vector<int> vi;for(int i=1;i<=9;++i)vi.push_back(i);copy(vi.rbegin(),vi.rend(),ostream_iterator<int>(cout," "));cout<<endl;return 0; 
}

程序输出结果:

9 8 7 6 5 4 3 2 1

知识点:

  • 当某个位置上并没有任何合法元素时,我们永远不要使用operator* 或 operator->

http://chatgpt.dhexx.cn/article/PsB8dVYC.shtml

相关文章

pyqt学习笔记

pyqt学习笔记 文章目录 pyqt学习笔记前言pyqt主要模块开发环境安装qtpython选择使用anaconda集成版本&#xff1a;anaconda的特点&#xff1a;安装步骤&#xff1a; pycharm导入anaconda:pycharm设置qtdesigner&#xff0c;ui转py工具: 前言 gui学习是一个比较重要的内容&…

[ PyQt入门教程 ] PyQt5开发环境搭建和配置

PyQt5工具可以快速实现简单的界面开发&#xff0c;包括界面设计、布局管理以及业务逻辑实现&#xff08;信号与槽&#xff09;。简单说就是使用PyQt5工具可以快速画一个控件摆放整齐、界面整洁有序、布局合理的界面。 课程目标 可以动手实现简单的GUI程序。系列文章主要以动手…

PyQt(QtDesigner+Python)编写程序的使用教程(简单版)

有同学问我具体怎么实现QtDesignerPython&#xff0c;简单写一下方便查看 1.安装好后Qtdesinger,打开软件&#xff0c;操作控件设计好想要的界面&#xff1b; 2.将Qtdesinger编写的.ui文件&#xff0c;使用PyUIC&#xff08;需要自己安装配置好&#xff09;软件转到.py文件 …

Python开发:PyQT安装教程

不管开发什么程序&#xff0c;一个友好的用户界面都是至关重要的&#xff0c;然而Python自身并没有集成GUI&#xff0c;但是好在自Python诞生之日起&#xff0c;就有许多优秀的GUI工具集被整合到Python当中&#xff0c;使得Python也可以在图形界面编程领域大展身手。所以从这一…

PyQt5学习教程

介绍 Qt&#xff08;官方发音 [kju:t]&#xff0c;音同 cute&#xff09;是一个跨平台的 C 开发库&#xff0c;主要用来开发图形用户界面&#xff08;Graphical User Interface&#xff0c;GUI&#xff09;程序&#xff0c;当然也可以开发不带界面的命令行&#xff08;Command…

PyQt完整入门教程 | 例程附代码

关注、星标公众号&#xff0c;直达精彩内容 来源&#xff1a;cnblogs 作者&#xff1a;lovesoo 1、GUI开发框架简介 pyqt是个好东西&#xff0c;可以做完整的测试方案、脚本、工具进行整合复用等等&#xff0c;本文将以一个实例和大家一起分享。先给自己挖个坑开个头&#xff0…

PyQt初级教程

PyQt5简介 这是一个PyQt5的入门教程.目的是帮助你使用PyQt5.本教程创建并在Linux上测试.PyQt4教程则覆盖了PyQt4,对应Python的2.x和3.x的Qt4的库. 原作地址&#xff1a;http://zetcode.com/gui/pyqt5/ 原翻译地址 &#xff1a;http://blog.csdn.net/neverstop_2009/article/c…

PyQt4入门教程(2)_PyQt4的第一个程序

注&#xff1a;文中译者的话将用方括号【】标出。 这一部分我们将学习PyQt中一些基本的函数。 一个简单的例子 这是一个能够显示出一个窗口的简单例子。目前为止我们已经可以对这个窗口干很多事情了&#xff0c;比如说改变它的尺寸&#xff0c;最大化&#xff0c;最小化………

一、PyQt基础知识

一、基础知识 &#xff08;一&#xff09;简介 1. 什么是PyQt5 PyQt是基于Digia公司强大的图形程序框架Qt的Python接口&#xff0c;由一组Python模块构成&#xff0c;它是一个创建GUI应用程序的工具包&#xff0c;由Phil Thompson开发。 自从1998年首次将Qt移植到Python上形…

PyQt完整入门教程

https://blog.csdn.net/baidu_37503452?spm1000.2115.3001.5343 1、GUI开发框架简介 19年来&#xff0c;一直在做Android ROM相关测试&#xff0c;也有了一定的积累&#xff1b;20年&#xff0c;计划把之前完整的测试方案、脚本、工具进行整合复用。 第一期计划是开发一个GUI的…

PyQt上手教程汇总

根据此前的PyQt学习&#xff0c;这里对PyQt的学习过程进行最后的总结 前文链接&#xff1a;由于前文标题名字取了一样的,以下内容按照前后顺序排列 (1)PyQt上手教程&#xff08;一&#xff09;_机械刘怀洋的博客-CSDN博客 (2)PyQt上手教程&#xff08;一&#xff09;_机械刘…

pyqt基础教程

PYQT是python版本的QT界面程序包&#xff0c;大家写过C、C#都做过界面设计&#xff0c;python也一样&#xff0c;非常简单。跟着我的思路走&#xff0c;五分钟学会&#xff01; 1、主体框架先造一个窗口 以下程序直接抄过去。 以下程序直接抄过去。 以下程序直接抄过去。 #!…

Mac 下安装pip,卸载pip方法

mac下直接安装pip和卸载pip的方法如下&#xff1a; 1、pip的安装&#xff1a; 输入 sudo easy_install pip 就可以安装 pip 了。 验证pip安装是否成功&#xff1a; 输入&#xff1a;pip 结果找不到文件。 尝试输入&#xff1a;pip3 -V 或者 pip3 则说明已经安装成功了。 …

pip3在Ubuntu下的安装、升级、卸载

一、参考资料 pip 常用命令 pip 官方文档 二、安装pip包 如何在 Ubuntu 20.04 上安装 Python Pip - 知乎 (zhihu.com) 1. 离线安装 Installation pip下载地址 1.1 为 Python 3 安装 pip 方式一 # 下载get-pip.py脚本 wget https://bootstrap.pypa.io/pip/3.6/get-pip.p…

conda和pip卸载包的注意事项

安装和import时包的名称不一致时&#xff0c;比如scikit-learn和sklearn&#xff08;scikit意思是科学工具箱&#xff0c;通常被缩写成sk, 比如scikit-image和skimage也是同样的情况&#xff09; 卸载的时候还是应该用包的全称&#xff0c;也就是与安装时一致用pip安装的就要用…

Python 技巧篇-pip卸载python库实例演示,查看pip命令大全方法

因为安装的 PyHook3 没安装对吧&#xff0c;有点问题&#xff0c;就想着把它卸载掉&#xff0c;然后再重新安装一个&#xff0c;那应该怎么卸载呢&#xff1f; 非常简单&#xff0c;就是 pip uninstall xxx&#xff0c;正好和我们安装时的 pip install xxx 对应&#xff0c;下…

windows下将python自带的pip卸载了,怎么重新装pip

文章目录 windows下将python自带的pip卸载了&#xff0c;怎么重新装pipLinux下将python自带的pip卸载了&#xff0c;怎么重新装pip新装Linux系统没有pip怎么装pip方法1方法2方法3总结 Linux换pip为国内镜像源|pip换源|pip修改源 windows下将python自带的pip卸载了&#xff0c;怎…

解决“更新pip版本竟将pip卸载了,提示No module named ‘pip‘”

更新pip后&#xff0c;报错无权限之类的&#xff0c;结果在运行pip发现被卸载了。 ModuleNotFoundError: No module named ‘pip’ ![在这里插入图片描述](https://img-blog.csdnimg.cn/fe4c7037709d4c7495627a706eb0ff28.png?x-oss-processimage/watermark,type_d3F5LXplbmhl…

如何使用Pip卸载软件包?

Python Pip command provides search, install, update, uninstall packages. We can use pip command to uninstall packages easily even there are some alternatives like easy_install. Python Pip命令提供搜索,安装,更新,卸载软件包。 即使有easy_install之类的替代方…