STL 容器的概念 STL 容器的概念

在实际的开发过程中,数据结构本身的重要性不会逊于操作于数据结构的算法的重要性,当程序中存在着对时间要求很高的部分时,数据结构的选择就显得更加重要。

经典的数据结构数量有限,但是我们常常重复着一些为了实现向量、链表等结构而编写的代码,这些代码都十分相似,只是为了适应不同数据的变化而在 细节上有所出入。STL容器就为我们提供了这样的方便,它允许我们重复利用已有的实现构造自己的特定类型下的数据结构,通过设置一些模版类,STL容器对 最常用的数据结构提供了支持,这些模板的参数允许我们指定容器中元素的数据类型,可以将我们许多重复而乏味的工作简化

容器部分主要由头文 件<vector>,<list>,<deque>,<set>,<map>,<stack> 和<queue>组成。对于常用的一些容器和容器适配器(可以看作由其它容器实现的容器),可以通过下表总结一下它们和相应头文件的对应关系。

容器的概念

管理一组指定类型的元素。

STL 容器的概念
STL 容器的概念

容器的分类


序列式容器

        是一种线性结构,然后根据位置来存储和访问这些元素,这就是序列式容器。
        Vector(向量)、deque(双端队列)、list(列表)

关联式容器

       是一种非线性的树结构,和插入顺序无关
       通过键(key)来高效地查找和读取元素
       Set(集合)、multiset(多重结合)、map(映射)、multimap(多重映射)

容器适配器

       容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。
       例如:stack(栈)适配器可使任何一种顺序容器以栈的方式工作。
       系统提供了三种序列式容器适配器:stack(栈)、queue(队列)以及 priority_queue(优先级队列)。所有的适配器都会在其基础顺序容器上定义一个新接口。

使用STL的好处

       STL是C++的一部分,因此不用额外安装什麽,它被内建在你的编译器之内。 
       STL的一个重要特点是数据结构和算法的分离。尽管这是个简单的概念,但是这种分 离确实使得STL变得非常通用。例如,STL的sort()函数可以用来操作vector, list等容器。 
       STL具有高可重用性,高性能,高移植性,跨平台的优点。
              高可重用性:STL中几乎所有的代码都采用了模板类和模版函数的方式实现,这 相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。
              高性能:如map可以高效地从十万条记录里面查找出指定的记录,因为map是 采用红黑树的变体实现的。(红黑树是平横二叉树的一种)
              高移植性:如在项目A上用STL编写的模块,可以直接移植到项目B上。
              跨平台:如用windows的Visual Studio编写的代码可以在Mac OS的XCode 上直接编译。 
       程序员可以不用思考STL具体的实现过程,只要能够熟练使用STL就OK了。
       

了解到STL的这些好处,我们知道STL无疑是最值得C++程序员骄傲的一部分。每一个C++程序员都应该好好学习STL。只有能够熟练使用STL的程序员,才是好的C++程序员。

标准容器类                             特点

顺序性容器

Vector(向量)                             动态数组,从后面快速的插入与删除,快速直接访问任何元素
List(列表)                                  双链表,从任何地方快速插入与删除
Deque(双端队列)                      从前面或后面快速的插入与删除,快速直接访问任何元素

关联容器

Set(集合)                                   快速查找,不允许重复值 Multiset(多重集合) 快速查找,允许重复值
Map(映射)                               (key,value)基于关键字快速查找,不允许重复值
Multimap(多重映射)                 (key,value)基于关键字快速查找,允许重复值

容器适配器

Stack(栈)                                  后进先出
Queue(队列)                             先进先出
priority_queue (优先队列)         最高优先级元素总是第一个出列

c++ 的vector、array和数组的比较

在c++11中,STL中提拱了一个新的容器std::array,该容器在某些程度上替代了之前版本的std::vector的使用,更可以替代之前的自建数组的使用。那针对这三种不同的使用方式,先简单的做个比较:

相同点:
1. 三者均可以使用下表运算符对元素进行操作,即vector和array都针对下标运算符[]进行了重载
2. 三者在内存的方面都使用连续内存,即在vector和array的底层存储结构均使用数组

不同点:
1. vector属于变长容器,即可以根据数据的插入删除重新构建容器容量;但array和数组属于定长容量。
2. vector和array提供了更好的数据访问机制,即可以使用front和back以及at访问方式,使得访问更加安全。而数组只能通过下标访问,在程序的设计过程中,更容易引发访问错误。
3. vector和array提供了更好的遍历机制,即有正向迭代器和反向迭代器两种
4. vector和array提供了size和判空的获取机制,而数组只能通过遍历或者通过额外的变量记录数组的size
5. vector和array提供了两个容器对象的内容交换,即swap的机制,而数组对于交换只能通过遍历的方式,逐个元素交换的方式使用
6. array提供了初始化所有成员的方法fill
7. vector提供了可以动态插入和删除元素的机制,而array和数组则无法做到,或者说array和数组需要完成该功能则需要自己实现完成
8. 由于vector的动态内存变化的机制,在插入和删除时,需要考虑迭代的是否失效的问题。

基于上面的比较,在使用的过程中,可以将那些vector或者map当成数组使用的方式解放出来,可以直接使用array;也可以将普通使用数组但对自己使用的过程中的安全存在质疑的代码用array解放出来。

容器共性机制研究

容器的共通能力 

        所有容器提供的都是值(value)语意,而引用(reference)语意。容器执行插入 元素的操作时,内部实施拷贝动作。所以STL容器内存储的元素必须能够被拷贝(必须提供拷贝构造函数)。
        除了queue与stack外,每个容器都提供可返回迭代器的函数,运用返回的迭代器就可以访问元素。
        通常STL不会丢出异常。要求使用者确保传入正确的参数。
        每个容器都提供了一个默认构造函数跟一个默认拷贝构造函数。
                如已有容器vecIntA。
                vector<int> vecIntB(vecIntA); //调用拷贝构造函数,复制vecIntA到vecIntB中。
        与大小相关的操作方法(c代表容器):
                c.size(); //返回容器中元素的个数
                c.empty(); //判断容器是否为空 

        比较操作(c1,c2代表容器):
                c1 == c2 判断c1是否等于c2
                c1 != c2 判断c1是否不等于c2
                c1 = c2 把c2的所有元素指派给c1

各个容器的使用时机

STL 容器的概念
STL 容器的概念

Vector的使用场景:比如软件历史操作记录的存储,我们经常要查看历史记录,比如上一次的记录,上上次的记录,但却不会去删除记录,因为记录是事实的描述。

deque的使用场景:比如排队购票系统,对排队者的存储可以采用deque,支持头 端的快速移除,尾端的快速添加。如果采用vector,则头端移除时,会移动大量的数据,速度慢。

vector与deque的比较:
       一:vector.at()比deque.at()效率高,比如vector.at(0)是固定的,deque的开始位置却是不固定的。
       二:如果有大量释放操作的话,vector花的时间更少,这跟二者的内部实现有关。
       三:deque支持头部的快速插入与快速移除,这是deque的优点。

list的使用场景:比如公交车乘客的存储,随时可能有乘客下车,支持频繁的不确定位置元素的移除插入。

set的使用场景:比如对手机游戏的个人得分记录的存储,存储要求从高分到低分的顺序排列。 

map的使用场景:比如按ID号存储十万个用户,想要快速要通过ID查找对应的用 户。二叉树的查找效率,这时就体现出来了。如果是vector容器,最坏的情况下可能要遍历完整个容器才能找到该用户。