第9章 顺序容器
9.1 顺序容器概述
9.2 容器库概览
9.3 顺序容器操作
9.4 vector对象是如何增长
9.5 额外的string操作
9.6 容器适配器
9.1 顺序容器概述
-
顺序容器
为程序员提供了控制元素存储和访问顺序的能力。这种顺序不依赖于元素的值,而是与元素加入容器时的位置相对应。 -
所有顺序容器都提供了快速顺序访问元素的能力。但是容器有不同性能折中:
-
string 和vector
将 元素保存在连续的内存空间中。由下标计算地址非常快速,但是在中间位置添加或删除元素就会非常耗时 -
list和forward_list
设计目的是添加或删除元素都很快速,但是不支持元素的随机访问 -
deque
支持快速的随机访问,在中间位置添加或删除元素就会非常耗时。但是在两端添加或删除元素都是很快的 -
array
对象大小是固定的,不支持添加或删除元素以及改变容器大小的操作 -
确定使用哪种顺序容器
9.2 容器库概览
每个容器都定义在一个头文件中,文件名与类型名相同
顺序容器几乎可以保存任意类型的元素,容器元素的类型可以是另一个容器
容器操作
-
迭代器
有公共的接口:如果一个迭代器提供某个操作,那么所有提供相同操作的迭代器对这个操作的实现方式都是相同的 -
一个
迭代器范围
由一对迭代器表示,两个迭代器分别指向同一个容器中的元素或者是尾元素之后的位置。这两个迭代器通常被称为begin
和end
。左闭合区间
-
容器类型成员.
每个容器都定义了多个类型,如size_type
、iterator
和const_iterator
。 -
begin和end成员.
带r
的版本返回反向迭代器,以c
开头的版本则返回const迭代器。当不需要写访问时,应使用cbegin
和cend
-
每个容器类型都定义一个默认构造函数。除array之外,其他容器的默认构造函数都会创建一个指定类型的空容器,且都可以接受指定容器大小和元素初始值的参数
-
只有顺序容器的构造函数才接受大小参数,关联容器并不支持
-
标准库array具有固定大小
。当定义一个array时,除了指定元素类型,还有指定容器大小
虽然不能对内置数组类型进行拷贝或对象赋值操作,但array并无限制
-
赋值和swap.
array类型不支持assign,也不允许用花括号包围的值列表进行赋值
- swap操作交换两个相同类型容器的内容。除了array之外,swap只是交换了两个容器的内部数据结构,除string外,指向容器的迭代器、引用和指针在swap操作之后都不会失效,它们仍指向swap操作之前所指向的那些元素。swap两个array会真正交换它们的元素 -
容器大小操作.
成员函数size返回容器中元素的数目;empty当size为0时返回布尔值true,否则返回false;max_size返回一个大于或等于该类型容器所能容纳的最大元素值。 -
关系运算符.
只有当其元素类型也定义了相应的比较运算符时,我们才可以使用关系运算符来比较两个容器
9.3 顺序容器操作
-
向顺序容器添加元素
-
使用push_back
将一个元素追加到一个vector的尾部。除array和forward_list之外,每个顺序容器(包括string类型)都支持push_back。容器元素是拷贝,而不是对象本身。 -
使用push_front
-
在容器中特定位置添加元素.
insert
成员提供更一般的添加功能,允许在容器中任意位置插入0个或多个元素。vector、deque、list和string都支持insert成员。forward_list提供了特殊版本的insert成员。每个insert函数都接受一个迭代器作为其第一个参数,迭代器指出了在容器中什么位置放置新元素
slist.insert(iter,"hello"); //将“hello”添加到iter之前的位置
-
插入范围内元素.
如果传递给insert一对迭代器,则迭代器不能指向添加元素的目标容器 -
使用insert的返回值.
通过使用insert的返回值,可以在容器中一个特定位置反复插入元素
-
使用emplace操作.
引入三个新成员emplace_front
、emplace
、emplace_back
,允许将元素放置在容器头部、一个指定位置之前或容器尾部。当调用push或insert成员函数时,将元素类型对象传递它们,这些对象被拷贝到容器中。当调用一个emplace成员函数时,则是将参数传递给元素类型的构造函数。emplace成员使用这些参数在容器管理的内存空间中直接构造元素。
在调用emplace_back时,会在容器管理的内存空间中直接创建对象,传递给emplace函数的参数必须与元素类型的构造函数相匹配。而调用 push_back则会创建一个局部临时对象,并将其压入容器中。
-
访问元素.
包括array在内的每个顺序容器都有一个front成员函数,而除forward_list之外的所有顺序容器都有一个back成员函数。这两个操作分别返回首元素和尾元素的引用
直接的方法是调用front和back,而间接的方法是通过解引用begin返回的迭代器来获得首元素的引用,以及通过递减然后解引用end返回的迭代器来获得尾元素的引用
-
访问成员函数返回的是引用.
在容器中访问元素的成员函数(即,front、back、下标和at)返回的都是引用
-
下标操作和安全的随机访问.
如果确保下标是合法的,可以使用at
成员函数,如果下标越界,at会抛出一个out_of_range
异常
-
删除元素.
-
特殊的forward_list 操作
当在forward_list中添加或删除元素时,我们必须关注两个迭代器,一个指向处理元素,另一个指向前驱
-
改变容器大小.
使用resize
来增大或缩小容器。
-
向容器添加元素和从容器中删除元素的操作可能会使指向容器元素的指针、引用或迭代器失效
-
编写改变容器的循环程序
-
不要保存end返回的迭代器.
如果在一个循环中插入/删除 deque、string或vector中的元素,不要缓存end返回的迭代器
9.4 vector对象是如何增长
vector和string
的实现通常会分配比新空间需求更大的内存空间。容器预留这些空间作为备用,可用来保存更多新的元素
只有当需要的内存空间超过当前容量时,reserve调用才会改变vector的容量;如果需求大小大于当前容量,reserve至少分配与需求一样大的内存空间;如果需求大小小于或等于当前容量,reserve什么也不做
resize成员函数只改变容器中元素的数目,而不是容器的容量
capacity 和size.
容器的size是指已经保存的元素的数目,而capacity 则是不分配新的内存空间的前提下最多可以保存多少元素。标准库实现中一个空的vector的capacity也为0
9.5 额外的string操作
-
substr操作.
返回一个string,是原始string的一部分或全部拷贝。可以传递给substr一个可选的开始位置和计数值
-
string还提供了接受下标的版本.
下标指出了开始删除的位置,或是insert到给定值之前到位置
-
将以空字符结尾的字符数组insert到或assign给一个string
-
string类定义了两个额外的成员函数:
append 和 replace函数.
replace操作是调用erase和insert的一种简写形式
-
string搜索操作
搜索是大小写敏感的
compare函数
数值转换
9.6 容器适配器
-
除了顺序容器外,标准库还定义了三个顺序容器适配器:
stack
、queue
和priority_queue
。适配器
是标准库中的一个通用概念。容器、迭代器和函数都有适配器。一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型
-
定义一个适配器.
每个适配器都定义两个构造函数:默认构造函数创建一个空对象,接受一个容器的构造函数拷贝该容器来初始化适配器。默认情况下,stack和queue是基于deque实现的,priority_queue是在vector之上实现的
-
栈适配器.
stack类型定义在stack头文件中。
每个容器适配器都基于底层容器类型的操作定义了自己的特殊操作。只可以使用适配器操作,而不能使用底层容器类型的操作
-
队列适配器.
queue和priority_queue适配器定义在queue头文件中
标准库queue使用FIFO的存储和访问策略
priority_queue
允许为队列中的元素建立优先级