STL(Standard Template Library)里有很多组成部分,但是主要有三个,容器、迭代器和算法
容器用来管理某个特定对象的集合。每一种容器都有自己的优点和缺点,在项目中根据不同的需求,使用不同的容器。容器可以是数组、链表或者类字典。
迭代器用于遍历对象集合的元素。这些集合可以是容器或容器的子集。每一个容器类都提供了它自己的迭代器类型。
算法用来处理的元素的集合。例如,可以进行搜索、排序等操作。
STL的数据和操作是分离的。数据存储在容器里,使用算法进行操作。迭代器是这两者之间的粘合剂,让算法与容器可以进行交互。
某种程度上,STL的概念与面向对象编程的原则相背,
STL数据和算法是分离的而不是结合。STL是泛型编程的一个很好的例子。容器和算法分别是任意类型和类的泛型。STL提供了更通用的组件。还可以通过使用适配器和仿函数来达到特殊要求。
容器
容器管理着一系列元素,为了满足不同的需求,STL提供了多种类型的容器,见下图。
主要可以分为三大类
序列容器是有序集合,其中的每个元素都有特定位置。这个位置取决于插入的时间和地点,但是它跟元素的值无关。STL包含5个预定义的序列容器类:array、vector、deque、list和forward_list。
关联容器是把元素的值按照某种规则排好顺序的集合。STL包含4个预定义的关联容器类:set、multiset、map和multimap。
无序(关联)容器是无序集合。主要关注一个元素是否在集合中。不论是插入的顺序还是插入元素的值对元素的位置都没有任何影响。STL包含4个预定义的无序容器类:unordered_set、unordered_multiset、unordered_map和unordered_multimap。
序列容器通常是数组或链表的实现
关联容器通常是二叉树的实现
无序容器通常是哈希表的实现
容器适配器
容器适配器提供顺序容器的特殊接口
stack 堆栈适配器(LIFO)
queue 改写容器来提供队列(FIFO数据结构)
priority_queue 改写容器来提供优先级队列
迭代器
根据迭代器所支持的操作,一般分为下面5种。
前向迭代器只能利用递增运算符进行前向迭代。像unordered_set、unordered_multiset、unordered_map和unordered_multimap这些容器都“至少”是用前向迭代器(某些情况下可以提供双向迭代器)。
双向迭代器是可以用递增运算符向前迭代,或者用递减运算符向后迭代。像list、set、multiset、map和multimap的迭代器都是双向迭代器。
随机访问迭代器具有双向迭代器的所有属性。此外,他们还可以进行随机访问。这种迭代器本身支持运算操作,可以改变偏移量,也可以利用关系运算符(< 和 >)比较迭代器。像vector、deque、array和string的迭代器都是这类迭代器。
输入迭代器能够在迭代时读取或处理一些值。如Input stream iterators。
输出迭代器能够在迭代时输出一些值。如Inserters、和output stream iterators。
算法
STL提供了一些标准算法来处理集合元素。这些算法一般提供最基本的功能,如搜索、排序、复制、修改和数值处理。
算法不是容器类的成员函数,而是全局函数。算法可以操作不同容器类型的元素,甚至可以操作用户自定义的容器类型。总之,既减少了代码量,又增强了性能。
迭代器适配器
任何操作起来像迭代器的东西都可以当作迭代器。所以可以写出像迭代器的一些类但又执行不一样的操作。C++标准库提供的一些预定义的特殊迭代器,即迭代器适配器。
一般分为四类:
Insert iterators也称为inserters,用来将“赋值新值”操作转换为“安插新值”操作。通过这种迭代器,算法可以执行安插(insert)行为而非覆盖(overwrite)行为。所有Insert迭代器都隶属于Output迭代器类型,所以它只提供赋值(assign)新值的能力。通常算法会将数值赋值给目的迭代器,如copy()算法
Stream iterators是一种迭代器配接器,可以把stream当成算法的原点和终点。更明确的说,一个istream迭代器可以用来从input stream中读元素,而一个ostream迭代器可以用来对output stream写入元素。Stream迭代器的一种特殊形式是所谓的stream缓冲区迭代器,用来对stream缓冲区进行直接读取和写入操作。
Reverse iterators重新定义递增运算和递减运算,使其行为正好倒置。
Move iterators很像一个底层迭代器(必须至少有一个InputIterator)。如果这个迭代器被当作输入迭代器来用,要注意的是,值的操作是移动而不是复制。
重点介绍向量:
向量容器使用动态数组存储、管理对象。因为数组是一个随机访问数据结构,所以可以随机访问向量中的元素。在数组中间或是开始处插入一个元素是费时的,特别是在数组非常大的时候更是如此。然而在数组末端插入元素却很快。
实现向量容器的类名是vector(容器是类模板)。包含vector类的头文件名是vector。所以,如果要在程序里使用向量容器,就要在程序中包含下面语句:
#include
此外,在定义向量类型对象时,必须指定该对象的类型,因为vector类是一个类模板。例如,语句:
vector intList;
将intList声明为一个元素类型为int的向量容器对象。类似地,语句:
vector stringList;将stringList声明为一个元素类型为string的向量容器对象。
声明向量对象
vector类包含了多个构造函数,其中包括默认构造函数。因此,可以通过多种方式来声明和初始化向量容器。表一描述了怎样声明和初始化指定类型的向量容器。
表一 各种声明和初始向量容器的方法
语句作用
vector vecList;创建一个没有任何元素的空向量vecList(使用默认构造函数)
vector vecList(otherVecList)创建一个向量vecList,并使用向量otherVecList中的元素初始化该向量。向量vecList与向量otherVecList的类型相同
vector vecLIst(size);
创建一个大小为size的向量vecList,并使用默认构造函数初始化该向量
vector vecList(n,elem);创建一个大小为n的向量vecList,该向量中所有的n个元素都初始化为elem
vector vecList(begin,end);创建一个向量vecList,并初始化该向量(begin,end)中的元素。即,从begin到end-1之间的所有元素
在介绍了如何声明向量顺序容器之后,让我们开始讨论如何操作向量容器中的数据。首先,必须要知道下面几种基本操作:
元素插入
元素删除
遍历向量容器中的元素
假设vecList是一个向量类型容器。表二给出了在vecList中插入元素和删除元素的操作,这些操作是vector类的成员函数。表二还说明了如何使用这些操作。
表二 向量容器上的各种操作
语句作用
vecList.clear()从容器中删除所有元素
vecList.erase(position)删除由position指定的位置上的元素
vecList.erase(beg,end)删除从beg到end-1之间的所有元素
vecList.insert(position, elem)将elem的一个拷贝插入到由position指定的位置上,并返回新元素的位置
vecList.inser(position, n, elem)将elem的n个拷贝插入到由 position指定的位置上
vecList.insert(position, beg, end)将从beg到end-1之间的所有元素的拷贝插入到vecList中由position指定的位置上
vecList.push_back(elem)将elem的一个拷贝插入致List的末尾
vecList.pop_back()删除最后元素
vecList.resize(num)将元素个数改为num。如果size()增加,默认的构造函数负责创建这些新元素
vecList.resize(num, elem)将元素个数改为num。如果size()增加,默认的构造函数将这些新元素初始化为elem
在向量容器中声明迭代器
vector类包含了一个typedef iterator,这是一个public成员。通过iterator,可以声明向量容器中的迭代器。例如,语句:
vector::iterator intVeciter; 将intVecIter声明为int类型的向量容器迭代器。
因为iterator是一个定义在vector类中的typedef,所以必须使用容器名(vector)、容器元素类型和作用域符来使用iterator。
表达式:
++intVecIter
将迭代器intVecIter加1,使其指向容器中的下一个元素。表达式:*intVecIter
返回当前迭代器位置上的元素。
注意,迭代器上的这些操作和指针上的相应操作是相同的。运算符*作为单目运算符使用时,称为递引用运算符。
下面将讨论如何使用迭代器来操作向量容器中的数据。假设有下面语句:
vector intList;
vector::iterator intVecIter;
第一行中的语句将intList声明为元素为int类型的向量容器。第二行中的语句将intVecIter声明为元素为int类型的向量容器的迭代器。
容器与函数begin和end
所有容器都包含成员函数begin和end。函数begin返回容器中第一个元素的位置;函数end返回容器中最后一个元素的位置。这两个函数都没有参数。在执行下面语句:
intVecIter = intList.begin();
迭代器intVecIter指向容器intList中第一个元素。
下面的for循环将intList中所有元素输出互标准输出设备上:
for (intVecIter = intList.begin(); intVecIter != intList.end();
cout<<*intVecList<<" ";
可以通过表三中给出的操作直接访问向量容器中的元素。
表三 访问向量容器中元素的操作
表达式作用
vecList.at(index)返回由index指定的位置上的元素
vecList[index]返回由index指定的位置上的元素
vecList.front()返回第一个元素 (不检查容器是否为空)
vecList.back()返回最后一个元素(不检查容器是否为空)
表三说明:可以按照数组的方式来处理向量中的元素(注意,在C++中,数组下标从0始。,向量容器中第一个元素的位置也是0)。
徽号类中还包含:返回容器中当前元素个数的成员函数,返回可以插入到容器中的元素的最大个数的成员函数等。表四描述其中 一些操作(假设vecCont是向量容器)。
表四 计算向量容器大小的操作
表达式作用
vecCont.capacity()返回不重新分配空间可以插入到容器vecCont中的元素的最大个数
vecCont.empty()容器vecCont为空,返回true;否则,返回false
vecCont.size()返回容器vecCont中当前的个数
vecCont.max_size()返回可以插入到容器vecCont中的元素的最大个数