C++系列内容的学习目录 → \rightarrow →C++学习系列内容汇总。
一个容器就是一些特定类型对象的集合。顺序容器(sequential container)为程序员提供了控制元素存储和访问顺序的能力。这种顺序不依赖于元素的值,而是与元素加入容器时的位置相对应。
下表列出了标准库中的顺序容器,所有容器都提供了快速顺序访问元素的能力,但是这些容器在两个方面都有不同的性能折中。第一个方面是,向容器中添加或从容器中删除元素的代价;第二个方面是,非顺序访问容器中元素的代价。
容器类型 | 简介 | 博客链接 |
---|---|---|
vector | 可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。 | C++提高编程(三)—— STL常用容器(2) :vector容器 |
deque | 双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。 | C++提高编程(三)—— STL常用容器(3) :deque容器 |
list | 双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除操作速度都很快。 | C++提高编程(三)—— STL常用容器(7) :list容器 |
forward_list | 单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。 | |
array | 固定大小数组。支持快速随机访问。不能添加或删除元素。 | |
string | 与vector相似的容器,但专门用于保存字符。随机访问快。在尾部位置插入/删除操作速度都很快。 | C++提高编程(三)—— STL常用容器(1) :string容器 |
除了固定大小的array外,其他容器都提供高效、灵活的内存管理。我们可以添加和删除元素,扩张和收缩容器的大小。容器保存元素的策略对容器操作的效率有着固有的、甚至是重大的影响。在某些情况下,存储策略还会影响特定容器是否支持特定操作。
例如,string和vector将元素保存在连续的内存空间中。由于元素是连续存储的,由元素的下标来计算其地址是非常快速的。但是,在这两种容器的中间位置添加或者删除元素就会非常耗时。因为在一次插入或删除操作后,需要移动插入或删除位置之后的所有元素来保持连续存储。而且,添加一个元素有时可能还需要分配额外的存储空间。在这种情况下,每个元素都必须移动到新的存储空间中。
list和forward_list两个容器的设计目的是令容器任何位置的添加或删除操作都很快速。作为代价,这两个容器不支持元素的随机访问。为了访问一个元素,我们只能遍历整个容器。而且,与vector、deque和array相比,这两个容器的额外内存开销也很大。
deque是一个更为复杂的数据结构。与string和vector类似,deque支持快速的随机访问。与string和vector一样,在deque的中间位置添加或删除元素的代价(可能)很高。但是,在deque的两端添加或删除元素都是很快的,与list或forward_list添加删除元素的速度相当。
forward_list和array是新C++标准增加的类型。与内置数组相比,array是一种更安全、更容易使用的数组类型。与内置数组类似,array对象的大小是固定的。因此,array不支持添加和删除元素以及改变容器大小的操作。forward_list的设计目标是达到与最好的手写的单向链表数据结构相当的性能。因此,forward_list没有size操作,因为保存或计算其大小就会比手写链表多出额外的开销。对其他容器而言,size保证是一个快速的常量时间的操作。
确定使用哪种顺序容器的基本原则:
如果程序既需要随机访问元素,又需要在容器中间位置插入元素,那该怎么办?答案取决于在list或forward_list中访问元素与vector或deque中插入/删除元素的相对性能。一般来说,应用中占主导地位的操作(执行的访问操作更多还是插入删除更多)决定了容器类型的选择,在此情况下,对两种容器分别测试应用的性能可能就是必要的了。
如果不确定应该使用哪种容器,那么可以在程序中只使用vector和list公共的操作:使用选代器,不使用下标操作,避免随机访问。这样,在必要时选择使用vector或list都很方便。
2. 容器适配器除了顺序容器之外,标准库还定义了三个顺序容器适器:stack(栈)、queue(队列)和priority_queue(优先队列)。适配器(adaptor)是标准库中的一个通用概念,容器、迭代器和函数都有适配器。本质上,一个适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样。一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型。例如,stack适配器接受一个顺序容器(除array或forward_list外),并使其操作起来像一个stack一样。
下表列出了一些容器适配器。
容器适配器类型 | 简介 | 博客链接 |
---|---|---|
stack | 一种先进后出(FILO)的数据结构,只有一个出口。 | C++提高编程(三)—— STL常用容器(5) :stack容器 |
queue | 一种先进先出(FIFO)的数据结构,有两个出口。 | C++提高编程(三)—— STL常用容器(6) :queue容器 |
priority_queue | 每次从队列中取出具有最高优先级的元素。 |
默认情况下,stack和queue是基于deque实现的,priority_queue是基于vector之上实现的。
3. 关联容器关联容器和顺序容器有着根本的不同:关联容器中的元素是按关键字来保存和访问的。与之相对,顺序容器中的元素是按它们在容器中的位置顺序来保存和访问的。
关联容器支持高效的关键字查找和访问,两个主要的关联容器(associative-container)类型是map和set,map中的元素是一些关键字 - 值(key-value)对:关键字起到索引的作用,值则表示与索引相关联的数据,set中每个元素只包含一个关键字。set支持高效的关键字查询操作——检查一个给定关键字是否在set中,例如,在某些文本处理过程中,可以用一个set来保存想要忽略的单词。字典则是一个很好的使用map的例子:可以将单词作为关键字,将单词释义作为值。
下表列出了一些关联容器。
容器类型 | 简介 | 博客链接 |
---|---|---|
set | 关键字即值,即只保存关键字的容器 | C++提高编程(三)—— STL常用容器(8) :set / multiset容器 |
map | 关联数组;保存关键字 - 值对 | C++提高编程(三)—— STL常用容器(9) :map / multimap容器 |
multiset | 关键字可重复出现的set | C++提高编程(三)—— STL常用容器(8) :set / multiset容器 |
multimap | 关键字可重复出现的map | C++提高编程(三)—— STL常用容器(9) :map / multimap容器 |