通过学习所有的序列式容器不难发现,无论是哪种序列式容器,其存储的都是 C++ 基本数据类型(诸如 int、double、float、string 等)或使用结构体自定义类型的元素。例如,如下是一个存储 int 类型元素的 vector 容器: std::vector
关联式容器则大不一样,此类容器在存储元素值的同时,还会为各元素额外再配备一个值(又称为“键”,其本质也是一个 C++ 基础数据类型或自定义类型的元素),它的功能是在使用关联式容器的过程中,如果已知目标元素的键的值,则直接通过该键就可以找到目标元素,而无需再通过遍历整个容器的方式。
弃用序列式容器,转而选用关联式容器存储元素,往往就是看中了关联式容器可以快速查找、读取或者删除所存储的元素,同时该类型容器插入元素的效率也比序列式容器高。
也就是说,使用关联式容器存储的元素,都是一个一个的“键值对”(
注意,关联式容器所具备的这些特性,归咎于 STL 标准库在实现该类型容器时,底层选用了 「红黑树」这种数据结构来组织和存储各个键值对。
关联式容器名称 |
特点 |
map |
定义在 |
set |
定义在 |
multimap |
定义在 |
multiset |
定义在 |
考虑到“键值对”并不是普通类型数据,C++ STL 标准库提供了 pair 类模板,
其专门用来将 2 个普通元素 first 和 second(可以是 C++ 基本数据类型、结构体、类自定的类型)创建成一个新元素
通过其构成的元素格式不难看出,使用 pair 类模板来创建“键值对”形式的元素,再合适不过。
注意,pair 类模板定义在
#include
#include // pair
#include // string
using namespace std;
int main() {
// 调用构造函数 1,也就是默认构造函数
pair pair1;
// 调用第 2 种构造函数
pair pair2("STL教程","http://c.biancheng.net/stl/");
// 调用拷贝构造函数
pair pair3(pair2);
//调用移动构造函数
pair pair4(make_pair("C++教程", "http://c.biancheng.net/cplus/"));
//等价于
//pair pair4 = make_pair("C++教程", "http://c.biancheng.net/cplus/");
// 调用第 5 种构造函数
pair pair5(string("Python教程"), string("http://c.biancheng.net/python/"));
cout << "pair1: " << pair1.first << " " << pair1.second << endl;
cout << "pair2: "<< pair2.first << " " << pair2.second << endl;
cout << "pair3: " << pair3.first << " " << pair3.second << endl;
cout << "pair4: " << pair4.first << " " << pair4.second << endl;
cout << "pair5: " << pair5.first << " " << pair5.second << endl;
return 0;
}
程序输出结果为:
pair1: 0
pair2: STL教程 http://c.biancheng.net/stl/
pair3: STL教程 http://c.biancheng.net/stl/
pair4: C++教程 http://c.biancheng.net/cplus/
pair5: Python教程 http://c.biancheng.net/python/
在上面程序的基础上,C++ 11 还允许我们手动为 pair1 对象赋值,比如:
pair1.first = "Java教程";
pair1.second = "http://c.biancheng.net/java/";
cout << "new pair1: " << pair1.first << " " << pair1.second << endl;
#include
#include // pair
#include // string
using namespace std;
int main() {
pair pair1("STL教程", 20);
pair pair2("C++教程", 20);
pair pair3("C++教程", 30);
//pair1和pair2的key不同,value相同
if (pair1 != pair2) {
cout << "pair != pair2" << endl;
}
//pair2和pair3的key相同,value不同
if (pair2 != pair3) {
cout << "pair2 != pair3" << endl;
}
return 0;
}
程序执行结果为:
pair != pair2
pair2 != pair3
最后需要指出的是,pair类模板还提供有一个 swap() 成员函数,能够互换 2 个 pair 对象的键值对,其操作成功的前提是这 2 个 pair 对象的键和值的类型要相同。例如:
#include
#include // pair
#include // string
using namespace std;
int main() {
pair pair1("pair", 10);
pair pair2("pair2", 20);
//交换 pair1 和 pair2 的键值对
pair1.swap(pair2);
cout << "pair1: " << pair1.first << " " << pair1.second << endl;
cout << "pair2: " << pair2.first << " " << pair2.second << endl;
return 0;
}
map 容器存储的都是 pair 对象,也就是用 pair 类模板创建的键值对。其中,各个键值对的键和值可以是任意数据类型,包括 C++ 基本数据类型(int、double 等)、使用结构体或类自定义的类型。
通常情况下,map 容器中存储的各个键值对都选用 string 字符串作为键的类型。
在使用 map 容器存储多个键值对时,该容器会自动根据各键值对的键的大小,按照既定的规则进行排序。默认情况下,map 容器选用std::less
另外需要注意的是,使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改。换句话说,map 容器中存储的各个键值对不仅键的值独一无二,键的类型也会用 const 修饰,这意味着只要键值对被存储到 map 容器中,其键的值将不能再做任何修改。
1.map存储的都是pair对象,通常选用string作为键的类型
2.有多个键值对时,容器会自动排序,我们也可以选择手动指定规则排序
3.键的值不能重复,并且一旦确定就不能再修改
map 容器的模板定义如下:
template < class Key, // 指定键(key)的类型
class T, // 指定值(value)的类型
class Compare = less, // 指定排序规则
class Alloc = allocator > // 指定分配器对象的类型
> class map;
map 容器模板有 4 个参数,其中后 2 个参数都设有默认值。大多数场景中,我们只需要设定前 2 个参数的值,有些场景可能会用到第 3 个参数,但最后一个参数几乎不会用到。
//通过调用 map 容器类的默认构造函数,创建出一个空的 map 容器
std::mapmyMap;
//在创建 map 容器的同时,进行初始化
std::mapmyMap{ {"C语言教程",10},{"STL教程",20} };
//map 容器中存储的键值对,其本质都是 pair 类模板创建的 pair 对象
std::mapmyMap{std::make_pair("C语言教程",10),std::make_pair("STL教程",20)};
//在某些场景中,可以利用先前已创建好的 map 容器,再创建一个新的 map 容器
std::mapnewMap(myMap);
//创建一个会返回临时 map 对象的函数
std::map disMap() {
std::maptempMap{ {"C语言教程",10},{"STL教程",20} };
return tempMap;
}
//调用 map 类模板的移动构造函数创建 newMap 容器
std::mapnewMap(disMap());
//取已建 map 容器中指定区域内的键值对,创建并初始化新的 map 容器
std::mapmyMap{ {"C语言教程",10},{"STL教程",20} };
std::mapnewMap(++myMap.begin(), myMap.end());
//程序手动修改了 myMap 容器的排序规则,令其作降序排序:
std::map >myMap{ {"C语言教程",10},{"STL教程",20} };
表 1 C++ map容器常用成员方法
成员方法 |
功能 |
begin() |
返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
end() |
返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
rbegin() |
返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
rend() |
返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
cbegin() |
和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
cend() |
和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
crbegin() |
和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
crend() |
和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
find(key) |
在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
lower_bound(key) |
返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
upper_bound(key) |
返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
equal_range(key) |
该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。 |
empty() |
若容器为空,则返回 true;否则 false。 |
size() |
返回当前 map 容器中存有键值对的个数。 |
max_size() |
返回 map 容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。 |
operator[] |
map容器重载了 [] 运算符,只要知道 map 容器中某个键值对的键的值,就可以向获取数组中元素那样,通过键直接获取对应的值。 |
at(key) |
找到 map 容器中 key 键对应的值,如果找不到,该函数会引发 out_of_range 异常。 |
insert() |
向 map 容器中插入键值对。 |
erase() |
删除 map 容器指定位置、指定键(key)值或者指定区域内的键值对。后续章节还会对该方法做重点讲解。 |
swap() |
交换 2 个 map 容器中存储的键值对,这意味着,操作的 2 个键值对的类型必须相同。 |
clear() |
清空 map 容器中所有的键值对,即使 map 容器的 size() 为 0。 |
emplace() |
在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。 |
emplace_hint() |
在本质上和 emplace() 在 map 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。 |
count(key) |
在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。 |
#include
#include
程序执行结果为:
myMap size==3
STL教程 http://c.biancheng.net/stl/
Python教程 http://c.biancheng.net/python/
C语言教程 http://c.biancheng.net/c/
标准库为 map 容器配备的是双向迭代器(bidirectional iterator)。这意味着,map 容器迭代器只能进行 ++p、p++、--p、p--、*p 操作,并且迭代器之间只能使用 == 或者 != 运算符进行比较。
成员方法 |
功能 |
begin() |
返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
end() |
返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
rbegin() |
返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
rend() |
返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
cbegin() |
和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
cend() |
和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
crbegin() |
和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
crend() |
和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
find(key) |
在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
lower_bound(key) |
返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
upper_bound(key) |
返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
equal_range(key) |
该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。 |
#include
#include
程序执行结果为:
C语言教程 http://c.biancheng.net/c/
STL教程 http://c.biancheng.net/stl/
map 类模板中还提供了 find() 成员方法,它能帮我们查找指定 key 值的键值对,
如果成功找到,则返回一个指向该键值对的双向迭代器;
反之,其功能和 end() 方法相同。
举个例子:
#include
#include
程序执行结果为:
Java教程 http://c.biancheng.net/java/
STL教程 http://c.biancheng.net/stl/
此程序中,创建并初始化的 myMap 容器,默认会根据各键值对中键的值,对各键值对做升序排序,其排序的结果为:
<"C语言教程","http://c.biancheng.net/c/">
<"Java教程","http://c.biancheng.net/java/">
<"STL教程","http://c.biancheng.net/stl/">
同时,map 类模板中还提供有 lower_bound(key) 和 upper_bound(key) 成员方法,它们的功能是类似的,唯一的区别在于:
lower_bound(key) 返回的是指向第一个键不小于 key 的键值对的迭代器;
upper_bound(key) 返回的是指向第一个键大于 key 的键值对的迭代器;
#include
#include
程序执行结果为:
lower:Java教程 http://c.biancheng.net/java/
upper:STL教程 http://c.biancheng.net/stl/
lower_bound(key) 和 upper_bound(key) 更多用于 multimap 容器,在 map 容器中很少用到。
equal_range(key) 成员方法可以看做是 lower_bound(key) 和 upper_bound(key) 的结合体,该方法会返回一个 pair 对象,其中的 2 个元素都是迭代器类型,其中 pair.first 实际上就是 lower_bound(key) 的返回值,而 pair.second 则等同于 upper_bound(key) 的返回值。
显然,equal_range(key) 成员方法表示的一个范围,位于此范围中的键值对,其键的值都为 key。
举个例子:
#include
#include //pair
#include
程序执行结果为:
C语言教程 http://c.biancheng.net/c/
和 lower_bound(key)、upper_bound(key) 一样,该方法也更常用于 multimap 容器,因为 map 容器中各键值对的键的值都是唯一的,因此通过 map 容器调用此方法,其返回的范围内最多也只有 1 个键值对。
#include
#include
程序执行结果为:
http://c.biancheng.net/c/
注意,只有当 map 容器中确实存有包含该指定键的键值对,借助重载的 [ ] 运算符才能成功获取该键对应的值;反之,若当前 map 容器中没有包含该指定键的键值对,则此时使用 [ ] 运算符将不再是访问容器中的元素,而变成了向该 map 容器中增添一个键值对。其中,该键值对的键用 [ ] 运算符中指定的键,其对应的值取决于 map 容器规定键值对中值的数据类型,如果是基本数据类型,则值为 0;如果是 string 类型,其值为 "",即空字符串(即使用该类型的默认值作为键值对的值)。
#include
#include
程序执行结果为:
C语言教程 0
实际上,[ ] 运算符确实有“为 map 容器添加新键值对”的功能,但前提是要保证新添加键值对的键和当前 map 容器中已存储的键值对的键都不一样。例如:
#include
#include
程序执行结果为:
Python教程 http://c.biancheng.net/python/
STL教程 http://c.biancheng.net/stl/
#include
#include
程序执行结果为:
http://c.biancheng.net/c/
除了可以直接获取指定键对应的值之外,还可以借助 find() 成员方法间接实现此目的。和以上 2 种方式不同的是,该方法返回的是一个迭代器,即如果查找成功,该迭代器指向查找到的键值对;反之,则指向 map 容器最后一个键值对之后的位置(和 end() 成功方法返回的迭代器一样)。
#include
#include
程序执行结果为:
C语言教程 http://c.biancheng.net/c/
注意,此程序中如果 find() 查找失败,会导致第 13 行代码运行出错。因为当 find() 方法查找失败时,其返回的迭代器指向的是容器中最后一个键值对之后的位置,即不指向任何有意义的键值对,也就没有所谓的 first 和 second 成员了。
如果以上方法都不适用,我们还可以遍历整个 map 容器,找到包含指定键的键值对,进而获取该键对应的值。比如:
#include
#include
本节所介绍的几种方法中,仅从“在 map 容器存储的键值对中,获取指定键对应的值”的角度出发,更推荐使用 at() 成员方法,因为该方法既简单又安全。
#include
#include
实际上,除了使用 [ ] 运算符实现向 map 容器中添加新键值对外,map 类模板中还提供有 insert() 成员方法,该方法专门用来向 map 容器中插入新的键值对。
注意,这里所谓的“插入”,指的是 insert() 方法可以将新的键值对插入到 map 容器中的指定位置,但这与 map 容器会自动对存储的键值对进行排序并不冲突。当使用 insert() 方法向 map 容器的指定位置插入新键值对时,其底层会先将新键值对插入到容器的指定位置,如果其破坏了 map 容器的有序性,该容器会对新键值对的位置进行调整。
1) 无需指定插入位置,直接将键值对添加到 map 容器中。insert() 方法的语法格式有以下 2 种:
//1、引用传递一个键值对
pairinsert (const value_type& val);
//2、以右值引用的方式传递键值对
template
pairinsert (P&& val);
其中,val 参数表示键值对变量,同时该方法会返回一个 pair 对象,其中 pair.first 表示一个迭代器,pair.second 为一个 bool 类型变量:
如果成功插入 val,则该迭代器指向新插入的 val,bool 值为 true;
如果插入 val 失败,则表明当前 map 容器中存有和 val 的键相同的键值对(用 p 表示),此时返回的迭代器指向 p,bool 值为 false。
#include
#include
程序执行结果为:
ret.iter = <{STL教程, http://c.biancheng.net/stl/}, 1>
ret.iter = <{C语言教程, http://c.biancheng.net/c/}, 1>
ret.iter = <{STL教程, http://c.biancheng.net/stl/}, 0>
另外,在程序中的第 21 行代码,还可以使用如下 2 种方式创建临时的键值对变量,它们是等价的:
//调用 pair 类模板的构造函数
ret = mymap.insert(pair{ "C语言教程","http://c.biancheng.net/c/" });
//调用 make_pair() 函数
ret = mymap.insert(make_pair("C语言教程", "http://c.biancheng.net/c/"));
2) 除此之外,insert() 方法还支持向 map 容器的指定位置插入新键值对,该方法的语法格式如下:
//以普通引用的方式传递 val 参数
iterator insert (const_iterator position, const value_type& val);
//以右值引用的方式传递 val 键值对参数
template
iterator insert (const_iterator position, P&& val);
其中 val 为要插入的键值对变量。注意,和第 1 种方式的语法格式不同,这里 insert() 方法返回的是迭代器,而不再是 pair 对象:
如果插入成功,insert() 方法会返回一个指向 map 容器中已插入键值对的迭代器;
如果插入失败,insert() 方法同样会返回一个迭代器,该迭代器指向 map 容器中和 val 具有相同键的那个键值对。
#include
#include
程序执行结果为:
STL教程 http://c.biancheng.net/stl/
C语言教程 http://c.biancheng.net/c/
STL教程 http://c.biancheng.net/stl/
再次强调,即便指定了新键值对的插入位置, map 容器仍会对存储的键值对进行排序。也可以说,决定新插入键值对位于 map 容器中位置的,不是 insert() 方法中传入的迭代器,而是新键值对中键的值。
3) insert() 方法还支持向当前 map 容器中插入其它 map 容器指定区域内的所有键值对,该方法的语法格式如下:
template
void insert (InputIterator first, InputIterator last);
其中 first 和 last 都是迭代器,它们的组合可以表示某 map 容器中的指定区域。
#include
#include
程序执行结果为:
Java教程 http://c.biancheng.net/java/
STL教程 http://c.biancheng.net/stl/
4) 除了以上一种格式外,insert() 方法还允许一次向 map 容器中插入多个键值对,其语法格式为:
void insert ({val1, val2, ...});
其中,vali 都表示的是键值对变量。
#include
#include
和 insert() 方法相比,emplace() 和 emplace_hint() 方法的使用要简单很多,因为它们各自只有一种语法格式。其中,emplace() 方法的语法格式如下:
template
pairemplace (Args&&... args);
参数 (Args&&... args) 指的是,这里只需要将创建新键值对所需的数据作为参数直接传入即可,此方法可以自行利用这些数据构建出指定的键值对。另外,该方法的返回值也是一个 pair 对象,其中 pair.first 为一个迭代器,pair.second 为一个 bool 类型变量:
当该方法将键值对成功插入到 map 容器中时,其返回的迭代器指向该新插入的键值对,同时 bool 变量的值为 true;
当插入失败时,则表明 map 容器中存在具有相同键的键值对,此时返回的迭代器指向此具有相同键的键值对,同时 bool 变量的值为 false。
#include
#include
程序执行结果为:
1、ret.iter = <{STL教程, http://c.biancheng.net/stl/}, 1>
2、ret.iter = <{C语言教程, http://c.biancheng.net/c/}, 1>
3、ret.iter = <{STL教程, http://c.biancheng.net/stl/}, 0>
emplace_hint() 方法的功能和 emplace() 类似,其语法格式如下:
template
iterator emplace_hint (const_iterator position, Args&&... args);
显然和 emplace() 语法格式相比,有以下 2 点不同:
该方法不仅要传入创建键值对所需要的数据,还需要传入一个迭代器作为第一个参数,指明要插入的位置(新键值对键会插入到该迭代器指向的键值对的前面);
该方法的返回值是一个迭代器,而不再是 pair 对象。当成功插入新键值对时,返回的迭代器指向新插入的键值对;反之,如果插入失败,则表明 map 容器中存有相同键的键值对,返回的迭代器就指向这个键值对。
#include
#include //map
#include //string
using namespace std;
int main()
{
//创建并初始化 map 容器
std::mapmymap;
//指定在 map 容器插入键值对
map::iterator iter = mymap.emplace_hint(mymap.begin(),"STL教程", "http://c.biancheng.net/stl/");
cout << iter->first << " " << iter->second << endl;
iter = mymap.emplace_hint(mymap.begin(), "C语言教程", "http://c.biancheng.net/c/");
cout << iter->first << " " << iter->second << endl;
//插入失败样例
iter = mymap.emplace_hint(mymap.begin(), "STL教程", "http://c.biancheng.net/java/");
cout << iter->first << " " << iter->second << endl;
return 0;
}
程序执行结果为:
STL教程 http://c.biancheng.net/stl/
C语言教程 http://c.biancheng.net/c/
STL教程 http://c.biancheng.net/stl/
所谓“相似”,指的是 multimap 容器具有和 map 相同的特性,即 multimap 容器也用于存储 pair
multimap 容器类模板的定义如下:
template < class Key, // 指定键(key)的类型
class T, // 指定值(value)的类型
class Compare = less, // 指定排序规则
class Alloc = allocator > // 指定分配器对象的类型
> class multimap;
multimap 容器模板有 4 个参数,其中后 2 个参数都设有默认值。
大多数场景中,我们只需要设定前 2 个参数的值,有些场景可能会用到第 3 个参数,但最后一个参数几乎不会用到。
//通过调用 multimap 类模板的默认构造函数,可以创建一个空的 multimap 容器
multimapmymultimap1;
//在创建 multimap 容器的同时,还可以进行初始化操作
multimapmymultimap2{ {"C语言教程", "http://c.biancheng.net/c/"},
{"Python教程", "http://c.biancheng.net/python/"},
{"STL教程", "http://c.biancheng.net/stl/"} };
//底层会先将每一个{key, value}创建成 pair 类型的键值对,
//然后再用已建好的各个键值对初始化 multimap 容器。
//实际上,我们完全可以先手动创建好键值对,然后再用其初始化 multimap 容器
//借助 pair 类模板的构造函数来生成各个pair类型的键值对
multimapmymultimap{
pair{"C语言教程", "http://c.biancheng.net/c/"},
pair{ "Python教程", "http://c.biancheng.net/python/"},
pair{ "STL教程", "http://c.biancheng.net/stl/"}
};
//调用 make_pair() 函数,生成键值对元素
//创建并初始化 multimap 容器
multimapmymultimap{
make_pair("C语言教程", "http://c.biancheng.net/c/"),
make_pair("Python教程", "http://c.biancheng.net/python/"),
make_pair("STL教程", "http://c.biancheng.net/stl/")
};
//通过调用 multimap 类模板的拷贝(复制)构造函数,也可以初始化新的 multimap 容器
multimapnewmultimap3(mymultimap);//成功创建一个和 mymultimap 完全一样的 newmultimap 容器
//从已有 multimap 容器中,选定某块区域内的所有键值对,用作初始化新 multimap 容器时使用。
multimapmymultimap4{ {"C语言教程", "http://c.biancheng.net/c/"},
{"Python教程", "http://c.biancheng.net/python/"},
{"STL教程", "http://c.biancheng.net/stl/"} };
multimapnewmultimap4(++mymultimap.begin(), mymultimap.end());
//利用模板库提供的std::greater排序函数,实现令 multimap 容器对存储的键值对做降序排序
multimap>mymultimap{ {'a',1},{'b',2} };//降序
multimap>mymultimap{ {'a',1},{'b',2} };//升序
//在某些特定场景中,我们还可以为 multimap 容器自定义排序规则
成员方法 |
功能 |
begin() |
返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
end() |
返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
rbegin() |
返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
rend() |
返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
cbegin() |
和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
cend() |
和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
crbegin() |
和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
crend() |
和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
find(key) |
在 multimap 容器中查找首个键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
lower_bound(key) |
返回一个指向当前 multimap 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
upper_bound(key) |
返回一个指向当前 multimap 容器中第一个大于 key 的键值对的迭代器。如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
equal_range(key) |
该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对。 |
empty() |
若容器为空,则返回 true;否则 false。 |
size() |
返回当前 multimap 容器中存有键值对的个数。 |
max_size() |
返回 multimap 容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。 |
insert() |
向 multimap 容器中插入键值对。 |
erase() |
删除 multimap 容器指定位置、指定键(key)值或者指定区域内的键值对。 |
swap() |
交换 2 个 multimap 容器中存储的键值对,这意味着,操作的 2 个键值对的类型必须相同。 |
clear() |
清空 multimap 容器中所有的键值对,使 multimap 容器的 size() 为 0。 |
emplace() |
在当前 multimap 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。 |
emplace_hint() |
在本质上和 emplace() 在 multimap 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。 |
count(key) |
在当前 multimap 容器中,查找键为 key 的键值对的个数并返回。 |
和 map 容器相比,multimap 未提供 at() 成员方法,也没有重载 [] 运算符。这意味着,map 容器中通过指定键获取指定指定键值对的方式,将不再适用于 multimap 容器。其实这很好理解,因为 multimap 容器中指定的键可能对应多个键值对,而不再是 1 个
#include
#include //map
using namespace std;
int main()
{
//创建并初始化 multimap 容器
multimapmymultimap{ {'a',10},{'b',20},{'b',15}, {'c',30} };
//输出 mymultimap 容器存储键值对的数量
cout << mymultimap.size() << endl;
//输出 mymultimap 容器中存储键为 'b' 的键值对的数量
cout << mymultimap.count('b') << endl;
for (auto iter = mymultimap.begin(); iter != mymultimap.end(); ++iter) {
cout << iter->first << " " << iter->second << endl;
}
return 0;
}
程序执行结果为:
4
2
a 10
b 20
b 15
c 30
只要是 multimap 容器提供的成员方法,map 容器都提供,并且它们的用法是相同的。
multimap基本与map相同,其本质不同在于map中的键值是一一对应的,而multimap中一个键可以对应多个值
因此multimap中不提供[]、at(),这些通过指定键获取指定指定键值对的方式。
其他基本一致,理解了本质就可以融会贯通。
和 map、multimap 容器不同,使用 set 容器存储的各个键值对,要求键 key 和值 value 必须相等。
举个例子,如下有 2 组键值对数据:
{<'a', 1>, <'b', 2>, <'c', 3>}
{<'a', 'a'>, <'b', 'b'>, <'c', 'c'>}
显然,第一组数据中各键值对的键和值不相等,而第二组中各键值对的键和值对应相等。对于 set 容器来说,只能存储第 2 组键值对,而无法存储第一组键值对。
基于 set 容器的这种特性,当使用 set 容器存储键值对时,只需要为其提供各键值对中的 value 值(也就是 key 的值)即可。仍以存储上面第 2 组键值对为例,只需要为 set 容器提供 {'a','b','c'} ,该容器即可成功将它们存储起来。
map、multimap 容器都会自行根据键的大小对存储的键值对进行排序,set 容器也会如此,只不过 set 容器中各键值对的键 key 和值 value 是相等的,根据 key 排序,也就等价为根据 value 排序。
使用 set 容器存储的各个元素的值必须各不相同。
更重要的是,从语法上讲 set 容器并没有强制对存储元素的类型做 const 修饰,即 set 容器中存储的元素的值是可以修改的。但是,C++ 标准为了防止用户修改容器中元素的值,对所有可能会实现此操作的行为做了限制,使得在正常情况下,用户是无法做到修改 set 容器中元素的值的。
对于初学者来说,切勿尝试直接修改 set 容器中已存储元素的值,这很有可能破坏 set 容器中元素的有序性,最正确的修改 set 容器中元素值的做法是: 先删除该元素,然后再添加一个修改后的元素。
即set容器要求键值对应相等,set容器也会自行根据键的大小对存储键值对进行排序,set 容器存储的各个元素的值必须各不相同。想要修改set容器中元素的值,最正确的做法是先删除该元素,然后再添加一个修改后的元素。
set 容器的类模板定义如下:
template < class T, // 键 key 和值 value 的类型
class Compare = less, // 指定 set 容器内部的排序规则
class Alloc = allocator // 指定分配器对象的类型
> class set;
由于 set 容器存储的各个键值对,其键和值完全相同,也就意味着它们的类型相同,因此 set 容器类模板的定义中,仅有第 1 个参数用于设定存储数据的类型。
对于 set 类模板中的 3 个参数,后 2 个参数自带默认值,且几乎所有场景中只需使用前 2 个参数,第 3 个参数不会用到。
//调用默认构造函数,创建空的 set 容器
set myset1;
//该容器采用默认的std::less规则,会对存储的 string 类型元素做升序排序。
// 在创建 set 容器的同时,对其进行初始化
set myset2{"http://c.biancheng.net/java/",
"http://c.biancheng.net/stl/",
"http://c.biancheng.net/python/"};
//在创建新 set 容器的同时,将已有 set 容器中存储的所有元素全部复制到新 set 容器中
set copyset3(myset2);
//等同于 set copyset = myset
//取已有 set 容器中的部分元素,来初始化新 set 容器。
set myset4{ "http://c.biancheng.net/java/",
"http://c.biancheng.net/stl/",
"http://c.biancheng.net/python/" };
set copyset4(++myset.begin(), myset.end());
//手动修改 set 容器中的排序规则
set > myset{
"http://c.biancheng.net/java/",
"http://c.biancheng.net/stl/",
"http://c.biancheng.net/python/"};
表 1 C++ set 容器常用成员方法
成员方法 |
功能 |
begin() |
返回指向容器中第一个(注意,是已排好序的第一个)元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
end() |
返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
rbegin() |
返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
rend() |
返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
cbegin() |
和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。 |
cend() |
和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。 |
crbegin() |
和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。 |
crend() |
和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。 |
find(val) |
在 set 容器中查找值为 val 的元素,如果成功找到,则返回指向该元素的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
lower_bound(val) |
返回一个指向当前 set 容器中第一个大于或等于 val 的元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
upper_bound(val) |
返回一个指向当前 set 容器中第一个大于 val 的元素的迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
equal_range(val) |
该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的值为 val 的元素(set 容器中各个元素是唯一的,因此该范围最多包含一个元素)。 |
empty() |
若容器为空,则返回 true;否则 false。 |
size() |
返回当前 set 容器中存有元素的个数。 |
max_size() |
返回 set 容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。 |
insert() |
向 set 容器中插入元素。 |
erase() |
删除 set 容器中存储的元素。 |
swap() |
交换 2 个 set 容器中存储的所有元素。这意味着,操作的 2 个 set 容器的类型必须相同。 |
clear() |
清空 set 容器中所有的元素,即令 set 容器的 size() 为 0。 |
emplace() |
在当前 set 容器中的指定位置直接构造新元素。其效果和 insert() 一样,但效率更高。 |
emplace_hint() |
在本质上和 emplace() 在 set 容器中构造新元素的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示新元素生成位置的迭代器,并作为该方法的第一个参数。 |
count(val) |
在当前 set 容器中,查找值为 val 的元素的个数,并返回。注意,由于 set 容器中各元素的值是唯一的,因此该函数的返回值最大为 1。 |
下面程序演示表 1 中部分成员函数的用法:
#include
#include
#include
using namespace std;
int main()
{
//创建空set容器
std::set myset;
//空set容器不存储任何元素
cout << "1、myset size = " << myset.size() << endl;
//向myset容器中插入新元素
myset.insert("http://c.biancheng.net/java/");
myset.insert("http://c.biancheng.net/stl/");
myset.insert("http://c.biancheng.net/python/");
cout << "2、myset size = " << myset.size() << endl;
//利用双向迭代器,遍历myset
for (auto iter = myset.begin(); iter != myset.end(); ++iter) {
cout << *iter << endl;
}
return 0;
}
程序执行结果为:
1、myset size = 0
2、myset size = 3
http://c.biancheng.net/java/
http://c.biancheng.net/python/
http://c.biancheng.net/stl/
休息一下,缓缓再更~