C++STL: mapa y multimapa de contenedores asociativos

1. mapa

descripción general

Como una especie de contenedor asociativo, el contenedor de mapas almacena objetos de pares, es decir, pares clave-valor creados con la plantilla de clase de pares. Entre ellos, la clave y el valor de cada par clave-valor pueden ser cualquier tipo de datos, incluidos los tipos de datos básicos de C++ (int, double, etc.), estructuras o tipos definidos por clase.

Por lo general, cada par clave-valor almacenado en el contenedor del mapa utiliza una cadena como tipo de clave.

Al mismo tiempo, cuando se utiliza el contenedor de mapas para almacenar múltiples pares clave-valor, el contenedor se ordenará automáticamente de acuerdo con las reglas establecidas según el tamaño de la clave de cada par clave-valor. De forma predeterminada, el contenedor del mapa utiliza std::less<T>una intercalación (donde T representa el tipo de datos de la clave), que clasifica todos los pares clave-valor en orden ascendente según el tamaño de la clave. Por supuesto, de acuerdo con las necesidades de la situación real, podemos especificar manualmente las reglas de clasificación del contenedor del mapa, podemos elegir otras reglas de clasificación (por ejemplo) proporcionadas en la biblioteca estándar STL o personalizar las reglas de clasificación std::greater<T>.

Además, cabe señalar que para cada par clave-valor almacenado en el contenedor del mapa, el valor de la clave no se puede repetir ni modificar. En otras palabras, cada par clave-valor almacenado en el contenedor del mapa no solo es único, sino que también el tipo de clave se modifica con const, lo que significa que mientras el par clave-valor esté almacenado en el contenedor del mapa, el El valor de la clave ya no será ninguna modificación.

Como se mencionó anteriormente, el contenedor del mapa almacena los elementos del par clave-valor del tipo par; más precisamente, el contenedor almacena el tipo par <const K, T> (donde K y T representan los tipos de datos de la clave y el valor respectivamente). elementos del par clave-valor.

El contenedor del mapa se define en el archivo de encabezado <map> y reside en el espacio de nombres estándar. Por lo tanto, si desea utilizar el contenedor de mapas, el código debe contener la siguiente declaración:

#include <map>
using namespace std;

Tenga en cuenta que la segunda línea de código no es necesaria, de lo contrario, deberá indicar manualmente el espacio de nombres estándar cuando utilice el contenedor de mapas en programas posteriores (recomendado para principiantes).

La definición de plantilla para el contenedor de mapas es la siguiente:

template < class Key,                                     // 指定键(key)的类型
           class T,                                       // 指定值(value)的类型
           class Compare = less<Key>,                     // 指定排序规则
           class Alloc = allocator<pair<const Key,T> >    // 指定分配器对象的类型
           > class map;

Se puede ver que la plantilla del contenedor del mapa tiene 4 parámetros y los 2 últimos parámetros tienen valores predeterminados. En la mayoría de los escenarios, solo necesitamos establecer el valor de los primeros 2 parámetros, algunos escenarios pueden usar el tercer parámetro, pero el último parámetro casi nunca se usa.

función miembro

La siguiente tabla enumera los métodos de miembros comunes proporcionados por el contenedor de mapas y sus respectivas funciones:

función miembro Función
comenzar() Devuelve un iterador bidireccional que apunta al primer par clave-valor (nota, el primero ordenado) en el contenedor. Si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional de tipo constante.
fin() Devuelve un iterador bidireccional que apunta a la posición después del último elemento del contenedor (tenga en cuenta que es el último que se ha ordenado), generalmente utilizado junto con comenzar (). Si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional de tipo constante.
comenzar() Devuelve un iterador bidireccional inverso que apunta al último elemento (nota, el último elemento ordenado). Si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional inverso de tipo constante.
desgarrar() Devuelve un iterador bidireccional inverso que apunta a la posición anterior a la posición del primer elemento (nota, el primer elemento ordenado). Si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional inverso de tipo constante.
comenzar() Tiene la misma función que comenzar (), pero en base a ella, se agrega un atributo constante, que no se puede usar para modificar los pares clave-valor almacenados en el contenedor.
algunos() Tiene la misma función que end(), excepto que se agrega un atributo constante encima, que no se puede usar para modificar los pares clave-valor almacenados en el contenedor.
crbegin() Tiene la misma función que rbegin (), pero en base a ella, se agrega un atributo constante, que no se puede usar para modificar los pares clave-valor almacenados en el contenedor.
crédito() Tiene la misma función que rend(), excepto que, sobre esta base, se agrega un atributo constante, que no se puede usar para modificar los pares clave-valor almacenados en el contenedor.
encontrar (clave) Busque el par clave-valor cuya clave es clave en el contenedor del mapa. Si se encuentra correctamente, devuelva un iterador bidireccional que apunte al par clave-valor; de lo contrario, devuelva el mismo iterador que el método end(). Además, si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional de tipo constante.
límite_inferior(clave) Devuelve un iterador bidireccional que apunta al primer par clave-valor mayor o igual que la clave en el contenedor del mapa actual. Si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional de tipo constante.
límite_superior(clave) Devuelve un iterador que apunta al primer par clave-valor mayor que la clave en el contenedor del mapa actual. Si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional de tipo constante.
rango_igual(clave) Este método devuelve un objeto de par (contiene dos iteradores bidireccionales), donde los valores de retorno de los métodos pair.first y lower_bound() son equivalentes, y los valores de retorno de los métodos pair.first y lower_bound() son equivalentes. Es decir, este método devolverá un rango que contiene el par clave-valor cuya clave es clave (el par clave-valor del contenedor del mapa es único, por lo que el rango contiene como máximo un par clave-valor).
vacío() Devuelve verdadero si el contenedor está vacío; en caso contrario, falso.
tamaño() Devuelve el número de pares clave-valor almacenados en el contenedor del mapa actual.
tamaño máximo() Devuelve el número máximo de pares clave-valor que puede contener el contenedor del mapa. Los diferentes sistemas operativos tienen diferentes valores de retorno.
operador[] El contenedor del mapa sobrecarga el operador []. Siempre que conozca el valor de la clave de un par clave-valor en el contenedor del mapa, puede obtener directamente el valor correspondiente a través de la clave, al igual que obtener los elementos en la matriz.
en (clave) Encuentre el valor correspondiente a la clave clave en el contenedor del mapa; si no se encuentra, la función generará una excepción fuera de rango.
insertar() Inserte pares clave-valor en el contenedor del mapa.
borrar() Elimine la ubicación especificada, el valor clave (clave) especificado o el par clave-valor en el área especificada del contenedor del mapa. Los capítulos siguientes se centrarán en este método.
intercambio() Intercambie los pares clave-valor almacenados en los dos contenedores de mapas, lo que significa que los tipos de los dos pares clave-valor para la operación deben ser los mismos.
claro() Borre todos los pares clave-valor en el contenedor del mapa, incluso si el tamaño () del contenedor del mapa es 0.
lugar() Construye un nuevo par clave-valor en la posición especificada en el contenedor del mapa actual. Su efecto es el mismo que insertar pares clave-valor, pero más eficiente.
emplace_hint() Es esencialmente lo mismo que emplace() construye un nuevo par clave-valor en un contenedor de mapa, la diferencia es que el usuario debe proporcionar a este método un iterador que indique la ubicación donde se genera el par clave-valor y usarlo. como primer parámetro del método.
contar (clave) En el contenedor del mapa actual, busque la cantidad de pares clave-valor cuya clave es clave y devuélvala. Tenga en cuenta que dado que el valor clave de cada par clave-valor en el contenedor del mapa es único, el valor máximo de retorno de esta función es 1.

Varios métodos para crear un contenedor de mapas C++

La clase de plantilla del contenedor de mapas contiene varios constructores, por lo que hay muchas formas de crear un contenedor de mapas. A continuación se presentan varios métodos de uso común para crear un contenedor de mapas.

  1. Se puede crear un contenedor de mapas vacío llamando al constructor predeterminado de la clase de contenedor de mapas, por ejemplo:
std::map<std::string, int> myMap;

El contenedor myMap creado de esta manera está inicialmente vacío, es decir, no se almacena ningún par clave-valor. Dado que un contenedor de mapas vacío puede agregar nuevos pares clave-valor en cualquier momento según sea necesario, es más común crear un contenedor de mapas vacío.

  1. Por supuesto, al crear el contenedor del mapa, también se puede inicializar, por ejemplo:
std::map<std::string, int> myMap{ {"C语言教程",10},{"STL教程",20} };

Por lo tanto, el contenedor myMap contiene 2 pares clave-valor en su estado inicial.

Nuevamente, los pares clave-valor almacenados en el contenedor del mapa son esencialmente objetos de pares creados por la plantilla de clase de pares. Por lo tanto, el siguiente programa también puede crear exactamente el mismo contenedor myMap:

std::map<std::string, int> myMap{std::make_pair("C语言教程",10),std::make_pair("STL教程",20)};
  1. Además, en algunos escenarios, se puede utilizar un contenedor de mapas creado previamente para crear un nuevo contenedor de mapas. Por ejemplo:
std::map<std::string, int> newMap(myMap);

Por lo tanto, al llamar al constructor de copia (replicación) del contenedor de mapas, se puede crear con éxito un contenedor newMap exactamente igual a myMap.

En el estándar C++11, también se agrega un constructor de movimientos al contenedor del mapa. Se llama al constructor de movimiento cuando se pasa un objeto de mapa temporal como parámetro al contenedor del mapa que se va a inicializar. Por ejemplo:

// 创建一个会返回临时 map 对象的函数
std::map<std::string,int> disMap() {
    std::map<std::string, int>tempMap{ {"C语言教程",10},{"STL教程",20} };
    return tempMap;
}
// 调用 map 类模板的移动构造函数创建 newMap 容器
std::map<std::string, int> newMap(disMap());

Tenga en cuenta que ya sea que llame al constructor de copia o al constructor de copia, debe asegurarse de que los tipos de los dos contenedores sean exactamente iguales.

  1. La plantilla de clase de mapa también admite tomar pares clave-valor en el área especificada del contenedor de mapas establecido, crear e inicializar un nuevo contenedor de mapas. Por ejemplo:
std::map<std::string, int> myMap{ {"C语言教程",10},{"STL教程",20} };
std::map<std::string, int> newMap(++myMap.begin(), myMap.end());

Aquí, al llamar al iterador bidireccional del contenedor del mapa, se da cuenta de que al crear el contenedor newMap, se inicializa como un contenedor que contiene un par clave-valor {"STL Tutorial",20}.

  1. Por supuesto, sobre la base de la creación anterior de contenedores de mapas, podemos modificar manualmente las reglas de clasificación del contenedor de mapas. De forma predeterminada, el contenedor del mapa invoca la regla std::less<T> para ordenar todos los pares clave-valor en orden ascendente según el tamaño de clave de cada par clave-valor en el contenedor. Por lo tanto, las dos líneas siguientes para crear un contenedor de mapas son en realidad equivalentes:
std::map<std::string, int> myMap{ {"C语言教程",10},{"STL教程",20} };
std::map<std::string, int, std::less<std::string> > myMap{ {"C语言教程",10},{"STL教程",20} };

Para el contenedor myMap generado en los 2 métodos de creación anteriores, el orden de los pares clave-valor internos es el siguiente:

<"C语言教程", 10>
<"STL教程", 20>

El siguiente programa modifica manualmente las reglas de clasificación del contenedor myMap para ordenar en orden descendente:

std::map<std::string, int, std::greater<std::string> > myMap{ {"C语言教程",10},{"STL教程",20} };

En este punto, el orden de los pares clave-valor dentro del contenedor myMap es:

<"STL教程", 20>
<"C语言教程", 10>

En algunos escenarios específicos, también necesitamos personalizar las reglas de clasificación para el contenedor del mapa.

iterador

Ya sea el contenedor secuencial aprendido anteriormente o el contenedor asociativo, para implementar la operación transversal es necesario utilizar el iterador de este tipo de contenedor. Por supuesto, los contenedores de mapas no son una excepción.

La biblioteca estándar C++ STL equipa el contenedor de mapas con un iterador bidireccional. Esto significa que los iteradores del contenedor de mapas solo se pueden ++p、p++、--p、p--、*poperar y los iteradores solo se pueden comparar usando ==el operador o .!=

Vale la pena mencionar que, en comparación con los contenedores secuenciales, los contenedores de mapas proporcionan más métodos de miembros (como se muestra en la siguiente tabla) y, al llamarlos, podemos obtener fácilmente iteradores con significados específicos.

método miembro Función
comenzar() Devuelve un iterador bidireccional que apunta al primer par clave-valor (nota, el primero ordenado) en el contenedor. Si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional de tipo constante.
fin() Devuelve un iterador bidireccional que apunta a la posición después del último elemento del contenedor (tenga en cuenta que es el último que se ha ordenado), generalmente utilizado junto con comenzar (). Si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional de tipo constante.
comenzar() Devuelve un iterador bidireccional inverso que apunta al último elemento (nota, el último elemento ordenado). Si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional inverso de tipo constante.
desgarrar() Devuelve un iterador bidireccional inverso que apunta a la posición anterior a la posición del primer elemento (nota, el primer elemento ordenado). Si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional inverso de tipo constante.
comenzar() Tiene la misma función que comenzar (), pero en base a ella, se agrega un atributo constante, que no se puede usar para modificar los pares clave-valor almacenados en el contenedor.
algunos() Tiene la misma función que end(), excepto que se agrega un atributo constante encima, que no se puede usar para modificar los pares clave-valor almacenados en el contenedor.
crbegin() Tiene la misma función que rbegin (), pero en base a ella, se agrega un atributo constante, que no se puede usar para modificar los pares clave-valor almacenados en el contenedor.
crédito() Tiene la misma función que rend(), excepto que, sobre esta base, se agrega un atributo constante, que no se puede usar para modificar los pares clave-valor almacenados en el contenedor.
encontrar (clave) Busque el par clave-valor cuya clave es clave en el contenedor del mapa. Si se encuentra correctamente, devuelva un iterador bidireccional que apunte al par clave-valor; de lo contrario, devuelva el mismo iterador que el método end(). Además, si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional de tipo constante.
límite_inferior(clave) Devuelve un iterador bidireccional que apunta al primer par clave-valor mayor o igual que la clave en el contenedor del mapa actual. Si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional de tipo constante.
límite_superior(clave) Devuelve un iterador que apunta al primer par clave-valor mayor que la clave en el contenedor del mapa actual. Si el contenedor del mapa está calificado como constante, este método devuelve un iterador bidireccional de tipo constante.
equal_range(key) 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。

上表中多数的成员方法,诸如 begin()、end() 等,在学习顺序容器时已经多次使用过,它们的功能如下图所示:

在这里插入图片描述

注意,图中 Ei 表示的是 pair 类对象,即键值对。对于 map 容器来说,每个键值对的键的值都必须保证是唯一的。

  1. 下面程序以 begin()/end() 组合为例,演示如何遍历 map 容器:
#include <iostream>
#include <map>      // pair
#include <string>       // string
using namespace std;

int main() {
    // 创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},{"C语言教程","http://c.biancheng.net/c/"} };
    // 调用 begin()/end() 组合,遍历 map 容器
    for (auto iter = myMap.begin(); iter != myMap.end(); ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}

程序执行结果为:

C语言教程 http://c.biancheng.net/c/
STL教程 http://c.biancheng.net/stl/
  1. 除此之外,map 类模板中还提供了 find() 成员方法,它能帮我们查找指定 key 值的键值对,如果成功找到,则返回一个指向该键值对的双向迭代器;反之,其功能和 end() 方法相同。举个例子:
#include <iostream>
#include <map>      // pair
#include <string>       // string
using namespace std;

int main() {
    // 创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    // 查找键为 "Java教程" 的键值对
    auto iter = myMap.find("Java教程");
    // 从 iter 开始,遍历 map 容器
    for (; iter != myMap.end(); ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}

程序执行结果为:

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/">

在此基础上,通过调用 find() 方法,我们可以得到一个指向键为 “Java教程” 的键值对的迭代器,由此当使用 for 循环从该迭代器出开始遍历时,就只会遍历到最后 2 个键值对。

  1. 同时,map 类模板中还提供有 lower_bound(key) 和 upper_bound(key) 成员方法,它们的功能是类似的,唯一的区别在于:
  • lower_bound(key) 返回的是指向第一个键不小于 key 的键值对的迭代器;
  • upper_bound(key) 返回的是指向第一个键大于 key 的键值对的迭代器;

下面程序演示了它们的功能:

#include <iostream>
#include <map>      // pair
#include <string>       // string
using namespace std;

int main() {
    // 创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    // 找到第一个键的值不小于 "Java教程" 的键值对
    auto iter = myMap.lower_bound("Java教程");
    cout << "lower:" << iter->first << " " << iter->second << endl;
   
    // 找到第一个键的值大于 "Java教程" 的键值对
    iter = myMap.upper_bound("Java教程");
    cout <<"upper:" << iter->first << " " << iter->second << endl;
    return 0;
}

程序执行结果为:

lower:Java教程 http://c.biancheng.net/java/
upper:STL教程 http://c.biancheng.net/stl/

lower_bound(key) 和 upper_bound(key) 更多用于 multimap 容器,在 map 容器中很少用到。

  1. 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 <iostream>
#include <utility>  // pair
#include <map>      // map
#include <string>       // string
using namespace std;

int main() {
    // 创建并初始化 map 容器
    std::map<string, string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                   {"C语言教程","http://c.biancheng.net/c/"},
                                   {"Java教程","http://c.biancheng.net/java/"} };
    // 创建一个 pair 对象,来接收 equal_range() 的返回值
    pair <std::map<string, string>::iterator, std::map<string, string>::iterator> myPair = myMap.equal_range("C语言教程");
    // 通过遍历,输出 myPair 指定范围内的键值对
    for (auto iter = myPair.first; iter != myPair.second; ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}

程序执行结果为:

C语言教程 http://c.biancheng.net/c/

和 lower_bound(key)、upper_bound(key) 一样,该方法也更常用于 multimap 容器,因为 map 容器中各键值对的键的值都是唯一的,因此通过 map 容器调用此方法,其返回的范围内最多也只有 1 个键值对。

map获取键对应值的几种方法

我们知道,map 容器中存储的都是 pair 类型的键值对,但几乎在所有使用 map 容器的场景中,经常要做的不是找到指定的 pair 对象(键值对),而是从该容器中找到某个键对应的值。

注意,使用 map 容器存储的各个键值对,其键的值都是唯一的,因此指定键对应的值最多有 1 个。

庆幸的是,map 容器的类模板中提供了以下 2 种方法,可直接获取 map 容器指定键对应的值。

  1. map 类模板中对[]运算符进行了重载,这意味着,类似于借助数组下标可以直接访问数组中元素,通过指定的键,我们可以轻松获取 map 容器中该键对应的值。举个例子:
#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;

int main() {
    // 创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    string cValue = myMap["C语言教程"];
    cout << cValue << endl;
    return 0;
}

程序执行结果为:

http://c.biancheng.net/c/

可以看到,在第 11 行代码中,通过指定键的值为 “C语言教程”,借助重载的 [ ] 运算符,就可以在 myMap 容器中直接找到该键对应的值。

注意,只有当 map 容器中确实存有包含该指定键的键值对,借助重载的 [ ] 运算符才能成功获取该键对应的值;反之,若当前 map 容器中没有包含该指定键的键值对,则此时使用 [ ] 运算符将不再是访问容器中的元素,而变成了向该 map 容器中增添一个键值对。其中,该键值对的键用 [ ] 运算符中指定的键,其对应的值取决于 map 容器规定键值对中值的数据类型,如果是基本数据类型,则值为 0;如果是 string 类型,其值为 “”,即空字符串(即使用该类型的默认值作为键值对的值)。举个例子:

#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;

int main() {
    // 创建空 map 容器
    std::map<std::string, int>myMap;
    int cValue = myMap["C语言教程"];
    for (auto i = myMap.begin(); i != myMap.end(); ++i) {
        cout << i->first << " "<< i->second << endl;
    }
    return 0;
}

程序执行结果为:

C语言教程 0

显然,对于空的 myMap 容器来说,其内部没有以 “C语言教程” 为键的键值对,这种情况下如果使用 [ ] 运算符获取该键对应的值,其功能就转变成了向该 myMap 容器中添加一个<"C语言教程",0>键值对(由于 myMap 容器规定各个键值对的值的类型为 int,该类型的默认值为 0)。

实际上,[ ] 运算符确实有“为 map 容器添加新键值对”的功能,但前提是要保证新添加键值对的键和当前 map 容器中已存储的键值对的键都不一样。例如:

#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;

int main() {
    // 创建空 map 容器
    std::map<string, string>myMap;
    myMap["STL教程"]="http://c.biancheng.net/java/";
    myMap["Python教程"] = "http://c.biancheng.net/python/";
    myMap["STL教程"] = "http://c.biancheng.net/stl/";
    for (auto i = myMap.begin(); i != myMap.end(); ++i) {
        cout << i->first << " " << i->second << endl;
    }
    return 0;
}

程序执行结果为:

Python教程 http://c.biancheng.net/python/
STL教程 http://c.biancheng.net/stl/

注意,程序中第 9 行代码已经为 map 容器添加了一个以 “STL教程” 作为键的键值对,则第 11 行代码的作用就变成了修改该键对应的值,而不再是为 map 容器添加新键值对。

  1. 除了借助 [ ] 运算符获取 map 容器中指定键对应的值,还可以使用 at() 成员方法。和前一种方法相比,at() 成员方法也需要根据指定的键,才能从容器中找到该键对应的值;不同之处在于,如果在当前容器中查找失败,该方法不会向容器中添加新的键值对,而是直接抛出 out_of_range 异常。举个例子:
#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;

int main() {
    // 创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    cout << myMap.at("C语言教程") << endl;
    // 下面一行代码会引发 out_of_range 异常
    // cout << myMap.at("Python教程") << endl;
    return 0;
}

程序执行结果为:

http://c.biancheng.net/c/

程序第 11 行代码处,通过 myMap 容器调用 at() 成员方法,可以成功找到键为 “C语言教程” 的键值对,并返回该键对应的值;而第 13 行代码,由于当前 myMap 容器中没有以 “Python教程” 为键的键值对,会导致 at() 成员方法查找失败,并抛出 out_of_range 异常。

  1. 除了可以直接获取指定键对应的值之外,还可以借助 find() 成员方法间接实现此目的。和以上 2 种方式不同的是,该方法返回的是一个迭代器,即如果查找成功,该迭代器指向查找到的键值对;反之,则指向 map 容器最后一个键值对之后的位置(和 end() 成功方法返回的迭代器一样)。举个例子:
#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;

int main() {
    // 创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    map< std::string, std::string >::iterator myIter = myMap.find("C语言教程");
    cout << myIter->first << " " << myIter->second << endl;
    return 0;
}

程序执行结果为:

C语言教程 http://c.biancheng.net/c/

注意,此程序中如果 find() 查找失败,会导致第 12 行代码运行出错。因为当 find() 方法查找失败时,其返回的迭代器指向的是容器中最后一个键值对之后的位置,即不指向任何有意义的键值对,也就没有所谓的 first 和 second 成员了。

  1. 如果以上方法都不适用,我们还可以遍历整个 map 容器,找到包含指定键的键值对,进而获取该键对应的值。比如:
#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;

int main() {
    // 创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    for (auto iter = myMap.begin(); iter != myMap.end(); ++iter) {
        // 调用 string 类的 compare() 方法,找到一个键和指定字符串相同的键值对
        if (!iter->first.compare("C语言教程")) {
            cout << iter->first << " " << iter->second << endl;
        }
    }
    return 0;
}

程序执行结果为:

C语言教程 http://c.biancheng.net/c/

本节所介绍的几种方法中,仅从“在 map 容器存储的键值对中,获取指定键对应的值”的角度出发,更推荐使用 at() 成员方法,因为该方法既简单又安全。

map insert()插入数据的4种方式

前面讲过,C++ STL map 类模板中对[]运算符进行了重载,即根据使用场景的不同,借助[]运算符可以实现不同的操作。举个例子:

#include <iostream>
#include <map>  //map
#include <string> //string
using namespace std;

int main()
{
    std::map<string, string> mymap{ {"STL教程","http://c.biancheng.net/java/"} };
    // 获取已存储键值对中,指定键对应的值
    cout << mymap["STL教程"] << endl;
    // 向 map 容器添加新键值对
    mymap["Python教程"] = "http://c.biancheng.net/python/";
    // 修改 map 容器已存储键值对中,指定键对应的值
    mymap["STL教程"] = "http://c.biancheng.net/stl/";
    for (auto iter = mymap.begin(); iter != mymap.end(); ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
   
    return 0;
}

程序执行结果为:

http://c.biancheng.net/java/
Python教程 http://c.biancheng.net/python/
STL教程 http://c.biancheng.net/stl/

可以看到,当操作对象为 map 容器中已存储的键值对时,则借助 [ ] 运算符,既可以获取指定键对应的值,还能对指定键对应的值进行修改;反之,若 map 容器内部没有存储以 [ ] 运算符内指定数据为键的键值对,则使用 [ ] 运算符会向当前 map 容器中添加一个新的键值对。

实际上,除了使用 [ ] 运算符实现向 map 容器中添加新键值对外,map 类模板中还提供有 insert() 成员方法,该方法专门用来向 map 容器中插入新的键值对。

注意,这里所谓的“插入”,指的是 insert() 方法可以将新的键值对插入到 map 容器中的指定位置,但这与 map 容器会自动对存储的键值对进行排序并不冲突。当使用 insert() 方法向 map 容器的指定位置插入新键值对时,其底层会先将新键值对插入到容器的指定位置,如果其破坏了 map 容器的有序性,该容器会对新键值对的位置进行调整。

自 C++ 11 标准后,insert() 成员方法的用法大致有以下 4 种。

  1. 无需指定插入位置,直接将键值对添加到 map 容器中。insert() 方法的语法格式有以下 2 种:
// 1、引用传递一个键值对
pair<iterator,bool> insert (const value_type& val);
// 2、以右值引用的方式传递键值对
template <class P>
  pair<iterator,bool> insert (P&& val);

其中,val 参数表示键值对变量,同时该方法会返回一个 pair 对象,其中 pair.first 表示一个迭代器,pair.second 为一个 bool 类型变量:

  • 如果成功插入 val,则该迭代器指向新插入的 val,bool 值为 true;
  • 如果插入 val 失败,则表明当前 map 容器中存有和 val 的键相同的键值对(用 p 表示),此时返回的迭代器指向 p,bool 值为 false。

以上 2 种语法格式的区别在于传递参数的方式不同,即无论是局部定义的键值对变量还是全局定义的键值对变量,都采用普通引用传递的方式;而对于临时的键值对变量,则以右值引用的方式传参。

举个例子:

#include <iostream>
#include <map>  // map
#include <string> // string
using namespace std;

int main()
{
    // 创建一个空 map 容器
    std::map<string, string> mymap;
   
    // 创建一个真实存在的键值对变量
    std::pair<string, string> STL = { "STL教程","http://c.biancheng.net/stl/" };
   
    // 创建一个接收 insert() 方法返回值的 pair 对象
    std::pair<std::map<string, string>::iterator, bool> ret;
   
    // 插入 STL,由于 STL 并不是临时变量,因此会以第一种方式传参
    ret = mymap.insert(STL);
    cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    
    // 以右值引用的方式传递临时的键值对变量
    ret = mymap.insert({ "C语言教程","http://c.biancheng.net/c/" });
    cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    
    // 插入失败样例
    ret = mymap.insert({ "STL教程","http://c.biancheng.net/java/" });
    cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    return 0;
}

程序执行结果为:

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>

从执行结果中不难看出,程序中共执行了 3 次插入操作,其中成功了 2 次,失败了 1 次:

  • 对于插入成功的 insert() 方法,其返回的 pair 对象中包含一个指向新插入键值对的迭代器和值为 1 的 bool 变量。
  • 对于插入失败的 insert() 方法,同样会返回一个 pair 对象,其中包含一个指向 map 容器中键为 “STL教程” 的键值对和值为 0 的 bool 变量。

另外,在程序中的第 22 行代码,还可以使用如下 2 种方式创建临时的键值对变量,它们是等价的:

// 调用 pair 类模板的构造函数
ret = mymap.insert(pair<string,string>{ "C语言教程","http://c.biancheng.net/c/" });
// 调用 make_pair() 函数
ret = mymap.insert(make_pair("C语言教程", "http://c.biancheng.net/c/"));
  1. 除此之外,insert() 方法还支持向 map 容器的指定位置插入新键值对,该方法的语法格式如下:
// 以普通引用的方式传递 val 参数
iterator insert (const_iterator position, const value_type& val);
// 以右值引用的方式传递 val 键值对参数
template <class P>
  iterator insert (const_iterator position, P&& val);

其中 val 为要插入的键值对变量。注意,和第 1 种方式的语法格式不同,这里 insert() 方法返回的是迭代器,而不再是 pair 对象:

  • 如果插入成功,insert() 方法会返回一个指向 map 容器中已插入键值对的迭代器;
  • 如果插入失败,insert() 方法同样会返回一个迭代器,该迭代器指向 map 容器中和 val 具有相同键的那个键值对。

举个例子:

#include <iostream>
#include <map>  // map
#include <string> // string
using namespace std;

int main()
{
    // 创建一个空 map 容器
    std::map<string, string> mymap;
   
    // 创建一个真实存在的键值对变量
    std::pair<string, string> STL = { "STL教程","http://c.biancheng.net/stl/" };
    // 指定要插入的位置
    std::map<string, string>::iterator it = mymap.begin();
    // 向 it 位置以普通引用的方式插入 STL
    auto iter1 = mymap.insert(it, STL);
    cout << iter1->first << " " << iter1->second << endl;
    
    // 向 it 位置以右值引用的方式插入临时键值对
    auto iter2 = mymap.insert(it, std::pair<string, string>("C语言教程", "http://c.biancheng.net/c/"));
    cout << iter2->first << " " << iter2->second << endl;
    
    // 插入失败样例
    auto iter3 = mymap.insert(it, std::pair<string, string>("STL教程", "http://c.biancheng.net/java/"));
    cout << iter3->first << " " << iter3->second << endl;
    return 0;
}

程序执行结果为:

STL教程 http://c.biancheng.net/stl/
C语言教程 http://c.biancheng.net/c/
STL教程 http://c.biancheng.net/stl/

再次强调,即便指定了新键值对的插入位置,map 容器仍会对存储的键值对进行排序。也可以说,决定新插入键值对位于 map 容器中位置的,不是 insert() 方法中传入的迭代器,而是新键值对中键的值。

  1. insert() 方法还支持向当前 map 容器中插入其它 map 容器指定区域内的所有键值对,该方法的语法格式如下:
template <class InputIterator>
 void insert (InputIterator first, InputIterator last);

其中 first 和 last 都是迭代器,它们的组合<first,last>可以表示某 map 容器中的指定区域。

举个例子:

#include <iostream>
#include <map>  // map
#include <string> // string
using namespace std;

int main()
{
    // 创建并初始化 map 容器
    std::map<std::string, std::string>mymap{ {"STL教程","http://c.biancheng.net/stl/"},
                                                {"C语言教程","http://c.biancheng.net/c/"},
                                                {"Java教程","http://c.biancheng.net/java/"} };
    // 创建一个空 map 容器
    std::map<std::string, std::string>copymap;
    // 指定插入区域
    std::map<string, string>::iterator first = ++mymap.begin();
    std::map<string, string>::iterator last = mymap.end();
    // 将<first,last>区域内的键值对插入到 copymap 中
    copymap.insert(first, last);
    // 遍历输出 copymap 容器中的键值对
    for (auto iter = copymap.begin(); iter != copymap.end(); ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}

程序执行结果为:

Java教程 http://c.biancheng.net/java/p
STL教程 http://c.biancheng.net/stl/

此程序中,<first,last> 指定的区域是从 mumap 容器第 2 个键值对开始,之后所有的键值对,所以 copymap 容器中包含有 2 个键值对。

  1. 除了以上一种格式外,insert() 方法还允许一次向 map 容器中插入多个键值对,其语法格式为:
void insert ({val1, val2, ...});

其中,vali 都表示的是键值对变量。

举个例子:

#include <iostream>
#include <map>  // map
#include <string> // string
using namespace std;

int main()
{
    // 创建空的 map 容器
    std::map<std::string, std::string>mymap;
    // 向 mymap 容器中添加 3 个键值对
    mymap.insert({ {"STL教程", "http://c.biancheng.net/stl/"},
                   { "C语言教程","http://c.biancheng.net/c/" },
                   { "Java教程","http://c.biancheng.net/java/" } });
    for (auto iter = mymap.begin(); iter != mymap.end(); ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}

程序执行结果为:

C语言教程 http://c.biancheng.net/c/
Java教程 http://c.biancheng.net/java/
STL教程 http://c.biancheng.net/stl/

map emplace()和emplace_hint()方法

学习 map insert() 方法时提到,C++ STL map 类模板中还提供了 emplace() 和 emplace_hint() 成员函数,也可以实现向 map 容器中插入新的键值对。

值得一提的是,实现相同的插入操作,无论是用 emplace() 还是 emplace_hont(),都比 insert() 方法的效率高。

  1. 和 insert() 方法相比,emplace() 和 emplace_hint() 方法的使用要简单很多,因为它们各自只有一种语法格式。其中,emplace() 方法的语法格式如下:
template <class... Args>
 pair<iterator,bool> emplace (Args&&... args);

参数 (Args&&… args) 指的是,这里只需要将创建新键值对所需的数据作为参数直接传入即可,此方法可以自行利用这些数据构建出指定的键值对。另外,该方法的返回值也是一个 pair 对象,其中 pair.first 为一个迭代器,pair.second 为一个 bool 类型变量:

  • 当该方法将键值对成功插入到 map 容器中时,其返回的迭代器指向该新插入的键值对,同时 bool 变量的值为 true;
  • 当插入失败时,则表明 map 容器中存在具有相同键的键值对,此时返回的迭代器指向此具有相同键的键值对,同时 bool 变量的值为 false。

下面程序演示 emplace() 方法的具体用法:

#include <iostream>
#include <map>  // map
#include <string> // string
using namespace std;

int main()
{
    // 创建并初始化 map 容器
    std::map<string, string>mymap;
    // 插入键值对
    pair<map<string, string>::iterator, bool> ret = mymap.emplace("STL教程", "http://c.biancheng.net/stl/");
    cout << "1、ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    // 插入新键值对
    ret = mymap.emplace("C语言教程", "http://c.biancheng.net/c/");
    cout << "2、ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    // 失败插入的样例
    ret = mymap.emplace("STL教程", "http://c.biancheng.net/java/");
    cout << "3、ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    return 0;
}

程序执行结果为:

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>

可以看到,程序中共执行了 3 次向 map 容器插入键值对的操作,其中前 2 次都成功了,第 3 次由于要插入的键值对的键和 map 容器中已存在的键值对的键相同,因此插入失败。

  1. emplace_hint() 方法的功能和 emplace() 类似,其语法格式如下:
template <class... Args>
 iterator emplace_hint (const_iterator position, Args&&... args);

显然和 emplace() 语法格式相比,有以下 2 点不同:

  1. 该方法不仅要传入创建键值对所需要的数据,还需要传入一个迭代器作为第一个参数,指明要插入的位置(新键值对键会插入到该迭代器指向的键值对的前面);
  2. 该方法的返回值是一个迭代器,而不再是 pair 对象。当成功插入新键值对时,返回的迭代器指向新插入的键值对;反之,如果插入失败,则表明 map 容器中存有相同键的键值对,返回的迭代器就指向这个键值对。

下面程序演示 emplace_hint() 方法的用法:

#include <iostream>
#include <map>  // map
#include <string> // string
using namespace std;

int main()
{
    // 创建并初始化 map 容器
    std::map<string, string>mymap;
    // 指定在 map 容器插入键值对
    map<string, string>::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/

注意,和 insert() 方法一样,虽然 emplace_hint() 方法指定了插入键值对的位置,但 map 容器为了保持存储键值对的有序状态,可能会移动其位置。


2. multimap

概述

在掌握 C++ STL map 容器的基础上,本节介绍一个和 map 相似的关联容器,即 multimap 容器。

所谓“相似”,指的是 multimap 容器具有和 map 相同的特性,即 multimap 容器也用于存储 pair<const K, T> 类型的键值对(其中 K 表示键的类型,T 表示值的类型),其中各个键值对的键的值不能做修改;并且,该容器也会自行根据键的大小对存储的所有键值对做排序操作。和 map 容器的区别在于,multimap 容器中可以同时存储多(≥2)个键相同的键值对。

和 map 容器一样,实现 multimap 容器的类模板也定义在<map>头文件,并位于 std 命名空间中。因此,在使用 multimap 容器前,程序应包含如下代码:

#include <map>
using namespace std;

multimap 容器类模板的定义如下:

template < class Key,                                   // 指定键(key)的类型
           class T,                                     // 指定值(value)的类型
           class Compare = less<Key>,                   // 指定排序规则
           class Alloc = allocator<pair<const Key,T> >  // 指定分配器对象的类型
           > class multimap;

可以看到,multimap 容器模板有 4 个参数,其中后 2 个参数都设有默认值。大多数场景中,我们只需要设定前 2 个参数的值,有些场景可能会用到第 3 个参数,但最后一个参数几乎不会用到。

成员函数

成员方法 功能
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 个。

另外值的一提的是,由于 multimap 容器可存储多个具有相同键的键值对,因此上表中的 lower_bound()、upper_bound()、equal_range() 以及 count() 成员方法会经常用到。

创建C++ multimap容器的方法

multimap 类模板内部提供有多个构造函数,总的来说,创建 multimap 容器的方式可归为以下 5 种。

  1. 通过调用 multimap 类模板的默认构造函数,可以创建一个空的 multimap 容器:
std::multimap<std::string, std::string> mymultimap;
  1. 当然,在创建 multimap 容器的同时,还可以进行初始化操作。比如:
// 创建并初始化 multimap 容器
multimap<string, string>mymultimap{ {"C语言教程", "http://c.biancheng.net/c/"},
                                    {"Python教程", "http://c.biancheng.net/python/"},
                                    {"STL教程", "http://c.biancheng.net/stl/"} };

注意,使用此方式初始化 multimap 容器时,其底层会先将每一个{key, value}创建成 pair 类型的键值对,然后再用已建好的各个键值对初始化 multimap 容器。

实际上,我们完全可以先手动创建好键值对,然后再用其初始化 multimap 容器。下面程序使用了 2 种方式创建 pair 类型键值对,再用其初始化 multimap 容器,它们是完全等价的:

// 借助 pair 类模板的构造函数来生成各个pair类型的键值对
multimap<string, string> mymultimap{
    pair<string,string>{"C语言教程", "http://c.biancheng.net/c/"},
    pair<string,string>{ "Python教程", "http://c.biancheng.net/python/"},
    pair<string,string>{ "STL教程", "http://c.biancheng.net/stl/"}
};
// 调用 make_pair() 函数,生成键值对元素
// 创建并初始化 multimap 容器
multimap<string, string> mymultimap{
    make_pair("C语言教程", "http://c.biancheng.net/c/"),
    make_pair("Python教程", "http://c.biancheng.net/python/"),
    make_pair("STL教程", "http://c.biancheng.net/stl/")
};
  1. 除此之外,通过调用 multimap 类模板的拷贝(复制)构造函数,也可以初始化新的 multimap 容器。例如:
multimap<string, string> newmultimap(mymultimap);

由此,就成功创建一个和 mymultimap 完全一样的 newmultimap 容器。

在 C++ 11 标准中,还为 multimap 类增添了移动构造函数。即当有临时的 multimap 容器作为参数初始化新 multimap 容器时,其底层就会调用移动构造函数来实现初始化操作。举个例子:

// 创建一个会返回临时 multimap 对象的函数
multimap<string, string> dismultimap() {
    multimap<string, string>tempmultimap{ {"C语言教程", "http://c.biancheng.net/c/"},{"Python教程", "http://c.biancheng.net/python/"} };
    return tempmultimap;
}  
// 调用 multimap 类模板的移动构造函数创建 newMultimap 容器
multimap<string, string> newmultimap(dismultimap());

上面程序中,由于 dismultimap() 函数返回的 tempmultimap 容器是一个临时对象,因此在实现初始化 newmultimap 容器时,底层调用的是 multimap 容器的移动构造函数,而不再是拷贝构造函数。

注意,无论是调用复制构造函数还是调用拷贝构造函数,都必须保证这 2 个容器的类型完全一致。

  1. multimap 类模板还支持从已有 multimap 容器中,选定某块区域内的所有键值对,用作初始化新 multimap 容器时使用。例如:
// 创建并初始化 multimap 容器
multimap<string, string>mymultimap{ {"C语言教程", "http://c.biancheng.net/c/"},
                                    {"Python教程", "http://c.biancheng.net/python/"},
                                    {"STL教程", "http://c.biancheng.net/stl/"} };
multimap<string, string>newmultimap(++mymultimap.begin(), mymultimap.end());

这里使用了 multimap 容器的迭代器,选取了 mymultimap 容器中的最后 2 个键值对,用于初始化 newmultimap 容器。

  1. 前面讲到,multimap 类模板共可以接收 4 个参数,其中第 3 个参数可用来修改 multimap 容器内部的排序规则。默认情况下,此参数的值为std::less<T>,这意味着以下 2 种创建 multimap 容器的方式是等价的:
multimap<char, int> mymultimap{ {'a',1},{'b',2} };
multimap<char, int, std::less<char>> mymultimap{ {'a',1},{'b',2} };

mymultimap 容器中键值对的存储顺序为:

<a,1>
<b,2>

下面程序利用了 STL 模板库提供的std::greater<T>排序函数,实现令 multimap 容器对存储的键值对做降序排序:

multimap<char, int, std::greater<char>> mymultimap{ {'a',1},{'b',2} };

其内部键值对的存储顺序为:

<b,2>
<a,1>

在某些特定场景中,我们还可以为 multimap 容器自定义排序规则。

Supongo que te gusta

Origin blog.csdn.net/crossoverpptx/article/details/131722643
Recomendado
Clasificación