顺序容器存储的都是 C++ 基本数据类型,而关联容器则大不一样,此类容器在存储元素值的同时,还会为各元素额外再配备一个值(又称为“键”,其本质也是一个 C++ 基础数据类型或自定义类型的元素),它的功能是在使用关联容器的过程中,如果已知目标元素的键的值,则直接通过该键就可以找到目标元素,而无需再通过遍历整个容器的方式。
弃用顺序容器,转而选用关联容器存储元素,往往就是看中了关联容器可以快速查找、读取或者删除所存储的元素,同时该类型容器插入元素的效率也比序列式容器高。
也就是说,使用关联容器存储的元素,都是一个一个的“键值对”(
关联容器所具备的这些特性,归咎于 STL 标准库在实现该类型容器时,底层选用了 「红黑树」这种数据结构来组织和存储各个键值对。
C++ STL 标准库提供了 4 种关联容器,分别为 map、set、multimap、multiset,其各自的特点如下表所示:
关联式容器名称 | 特点 |
---|---|
map | 定义在 |
set | 定义在 |
multimap | 定义在 |
multiset | 定义在 |
除此之外,C++ 11 还新增了 4 种哈希容器,即 unordered_map、unordered_multimap 以及 unordered_set、unordered_multiset。严格来说,它们也属于关联式容器,但哈希容器底层采用的是哈希表,而不是红黑树。
我们知道,关联容器存储的是“键值对”形式的数据,比如:
<"C语言教程", "http://c.biancheng.net/c/">
<"Python教程", "http://c.biancheng.net/python/">
<"Java教程", "http://c.biancheng.net/java/">
如上所示,每行都表示一个键值对,其中第一个元素作为键(key),第二个元素作为值(value)。
注意,基于各个关联容器存储数据的特点,只有各个键值对中的键和值全部对应相等时,才能使用 set 和 multiset 关联容器存储,否则就要选用 map 或者 multimap 关联容器。
考虑到“键值对”并不是普通类型数据,C++ STL 标准库提供了 pair 类模板,其专门用来将 2 个普通元素 first 和 second(可以是 C++ 基本数据类型、结构体、类自定的类型)创建成一个新元素
注意,pair 类模板定义在
// 1) 默认构造函数,即创建空的 pair 对象
pair();
// 2) 直接使用 2 个元素初始化成 pair 对象
pair (const first_type& a, const second_type& b);
// 3) 拷贝(复制)构造函数,即借助另一个 pair 对象,创建新的 pair 对象
template pair (const pair& pr);
在 C++ 11 标准中,在引入右值引用的基础上,pair 类模板中又增添了如下 2 个构造函数:
// 4) 移动构造函数
template pair (pair&& pr);
// 5) 使用右值引用参数,创建 pair 对象
template pair (U&& a, V&& b);
除此之外,C++ 11 标准中 pair 类模板还新增加了如下一种构造函数:pair (piecewise_construct_t pwc, tuple
,但该构造 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/"));
// 调用第 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/
上面程序在创建 pair4 对象时,调用了 make_pair() 函数,它也是
在上面程序的基础上,C++ 11 还允许我们手动为 pair1 对象赋值,比如:
pair1.first = "Java教程";
pair1.second = "http://c.biancheng.net/java/";
cout << "new pair1: " << pair1.first << " " << pair1.second << endl;
执行结果为:
new pair1: Java教程 http://c.biancheng.net/java/
同时,上面程序中 pair4 对象的创建过程,还可以写成如下形式,它们是完全等价的:
pair pair4 = make_pair("C++教程", "http://c.biancheng.net/cplus/");
cout << "pair4: " << pair4.first << " " << pair4.second << endl;
头文件中除了提供创建 pair 对象的方法之外,还为 pair 对象重载了 <、<=、>、>=、==、!=
这 6 个运算符,其运算规则是:对于进行比较的 2 个 pair 对象,先比较 pair.first 元素的大小,如果相等则继续比较 pair.second 元素的大小。注意,对于进行比较的 2 个 pair 对象,其对应的键和值的类型比较相同,否则将没有可比性,同时编译器提示没有相匹配的运算符,即找不到合适的重载运算符。
举个例子:
#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
#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;
}
程序执行结果为:
pair1: pair2 20
pair2: pair 10