📔 C++ Primer 0x0B 学习笔记
更好的阅读体验(实时更新与修正)
- 关联容器支持高效的关键字查找和访问,
map和set是两个主要的关联容器 - 标准库提供8个关联容器
- 按关键字有序保存元素
-
map:关联数组,保存关键字-值对 -
set:关键字即值,即值保存关键字的容器,底层实现是红黑树 -
multimap:关键字可重复出现的map -
multiset :关键字可重复出现的set
- 无序集合
-
unordered_map:用哈希函数组织的map -
unordered_set:用哈希函数组织的set -
unordered_multimap:哈希组织的map ,关键字可重复出现 -
unordered_multiset:哈希组织的set,关键字可重复出现
11.1 使用关联容器
- 类似顺序容器,关联容器也是模板,使用的时候要明确关键字和值的类型
-
set可以去重 - 可以对关联容器的元素进行列表初始化
11.2 关联容器概述
- 关联容器不支持顺序容器的位置相关的操作如
push_front或push_back - 关联容器的迭代器都是双向的
11.2.1 定义关联容器
- 定义一个
map,必须既指明关键字类型又指明值类型 - 定义一个
set,只需指明关键字类型,因为set中没有值 - 每个关联容器都定义了一个默认构造函数,创建一个指定类型的空容器
- 关联容器初始化:同类型容器的拷贝,一个值范围,初始化列表
11.2.2 关键字类型的要求
- 有序容器要求所提供的操作必须在关键字类型上定义一个严格弱序(可以自己定义)
list迭代器不支持比较运算 - 为了使用自定义的操作,必须在定义关联容器类型时提供此操作的类型
11.2.3 pair 类型
-
pair类型保存两个数据成员(first和second),类似容器。定义在头文件utility中 -
pair的默认构造函数对数据进行值初始化 - 函数要返回一个
pair可以对返回值进行列表初始化return {fisrt,second},早期C++不允许 - 可以使用
make_pair来产生pair对象
11.3 关联容器操作
关联容器定义了几个类型别名
-
key_type此容器类型的关键字类型 -
mapped_type每个关键字关联的类型,只适用于map -
value_type对于set和key_value相同,对于map为pair<const key_type,mapped_type>
11.3.1 关联容器迭代器
- 当解引用一个关联容器迭代器,我们会得到一个类型为容器的
value_type 的值的引用 - 对于
map而言,value_type是一个pair类型,first保存const的关键字,second保存值。我们可以改变pair的值,但不能改变关键字成员的值 -
set的迭代器是const的,可以用一个set迭代器来读取元素的值,但不能修改 - 当使用一个迭代器遍历一个
map、multimap、set或multiset时,迭代器按关键字升序遍历元素 - 我们通常不对关联容器使用泛型算法,关键字是
const这一特性意味着不能将关联容器传递给修改或重排容器元素的算法;关联容器可用于只读取元素的算法,但是这类算法很多都要搜索序列,关联容器不支持通过它们的关键字进行快速查找,所以也没什么用。 - 使用关联容器专用的
find成员会比调用泛型算法的find快得多 - 实际编程中,我们对一个关联容器使用算法要么是将它当作源序列,要么当作一个目的位置,可以使用泛型算法的
copy将元素从一个关联容器拷贝到另一个序列
11.3.2 添加元素
- 由于
map和set(以及对应的无序类型)包含不重复的关键字,因此插入一个已存在的元素对容器没有任何影响 - 向一个
map进行insert操作时,必须记住元素是pair -
insert或emplace的返回值依赖于容器类型和参数,对于不包含重复关键字的容器,添加单一元素的insert和emplace返回一个pair告诉我们插入是否成功,first成员是一个迭代器指向具有给定关键字的元素,second成员是个bool值,告诉我们插入成功还是已经在容器中了。如果是可重复类型的只返回一个迭代器
11.3.3 删除元素
关联容器提供三个版本的erase
-
c.erase(k),从c中删除每个关键字为k的元素,返回一个size_type值,指出删除的元素数量,这个是顺序容器没有的 -
c.erase(),从c中删除迭代器p指定的元素,p必须指向一个真实元素不能是c.end(),返回一个指向p之后元素的迭代器 -
c.erase(b,e),删除迭代器对表示的范围中的元素,返回e
11.3.4 map的下标操作
-
set,multimap,unordered_multimap类型不支持下标 - 对一个
map使用下标操作,其行为与数组或vecotr上的下标操作很不相同,使用一个不在容器中的关键字作为下标,会添加一个具有此关键字的元素到map中 -
c[k]返回关键字为k的元素,如果k不在c中则添加一个关键字为k的元素,对其值进行值初始化 -
c.at(k)访问关键字为k的元素,带参数检查,如果k不再c中,跑出一个out_of_range异常 - 对
map进行下标操作会得到一个mapped_type对象,解引用map迭代器时,会得到一个value_type对象,这与vector、string是不同的 -
map的下标运算符返回的是一个左值,我们既可以读也可以写元素
11.3.5 访问元素
-
lower_bound和upper_bound不适用于无序容器 - 下标和
at操作适用于非const的map和unordered_map -
c.find(k)返回一个迭代器,指向关键字为k的元素,如果k不在容器中,则返回尾后迭代器 -
c.count(k)返回关键字等于k的元素数量,对于不允许重复关键字的容器,返回值永远是0或1 -
c.lower_bound(k)返回一个迭代器,指向第一个关键字不小于k的元素,如果关键字不在容器中,则lower_bound会返回关键字的第一个安全插入点(不影响容器中元素顺序的插入位置) -
c.upper_bound(k)返回一个迭代器,指向第一个关键字大于k的元素 -
c.equal_range(k)返回一个迭代器pair表示关键字等于k的元素范围,若k不存在,pair的两成员均等于c.end()(两个关键字都指向可以插入的位置) - 对
map使用find代替下标操作 - 如果
multimap或multiset中有多个元素具有给定关键字,则这些关键字会在容器中相邻存储find(k)返回一个迭代器指向第一个关键字为k的元素 - 可以通过
lower_bound和upper_bound,获得一对迭代器对形成的范围,表示具有该关键字的元素的范围,如果没有与给定关键字匹配的元素,lower_bound和upper_bound返回相同的迭代器
11.4 无序容器
- 无需关联容器不使用比较运算符来组织元素,而是使用一个哈希函数和关键字类型的
==运算符 - 如果关键字类型固有就是无序的,或者性能测试发现问题可以用哈希技术解决,就可以使用无序容器
- 无序容器的性能依赖于哈希函数的质量和桶的数量和大小
- 标准库为内置类型提供了
hash模板,还为一些标准库类型如string、智能指针类型定义了hash,所以我们可以直接定义关键字是内置类型(包括指针)、string、智能指针类型的无序容器 - 不能直接定义关键字类型为自定义类型的无序容器,必须提供我们自己的
hash模板版本。为了使用自定义类型作为关键字,我们可以采用另一种方法:提供函数来替代==运算符和哈希值计算函数










