C++STL: mapa e multimapa de contêineres associativos

1. mapa

visão geral

Como uma espécie de contêiner associativo, o contêiner de mapa armazena objetos pares, ou seja, pares chave-valor criados com o modelo de classe par. Entre eles, a chave e o valor de cada par de valores-chave podem ser qualquer tipo de dados, incluindo tipos de dados básicos C++ (int, double, etc.), estruturas ou tipos definidos por classe.

Normalmente, cada par de valores-chave armazenado no contêiner do mapa usa string como tipo de chave.

Ao mesmo tempo, ao usar o contêiner de mapa para armazenar vários pares de valores-chave, o contêiner classificará automaticamente de acordo com as regras estabelecidas de acordo com o tamanho da chave de cada par de valores-chave. Por padrão, o contêiner do mapa usa std::less<T>um agrupamento (onde T representa o tipo de dados da chave), que classifica todos os pares de valores-chave em ordem crescente de acordo com o tamanho da chave. Claro, de acordo com as necessidades da situação real, podemos especificar manualmente as regras de classificação do contêiner do mapa, podemos escolher outras regras de classificação (por exemplo) fornecidas na biblioteca padrão STL ou personalizar as regras de classificação std::greater<T>.

Além disso, deve-se notar que para cada par de valores-chave armazenado no contêiner do mapa, o valor da chave não pode ser repetido nem modificado. Em outras palavras, cada par de valores-chave armazenado no contêiner do mapa não é apenas único, mas também o tipo de chave é modificado com const, o que significa que, enquanto o par de valores-chave estiver armazenado no contêiner do mapa, o o valor da chave não sofrerá mais nenhuma modificação.

Conforme mencionado anteriormente, o contêiner do mapa armazena os elementos do par chave-valor do tipo par, mais precisamente, o contêiner armazena o tipo pair<const K, T> (onde K e T representam os tipos de dados da chave e do valor, respectivamente). elementos do par chave-valor.

O contêiner do mapa é definido no arquivo de cabeçalho <map> e reside no namespace std. Portanto, se você quiser usar o contêiner de mapas, o código deverá conter a seguinte instrução:

#include <map>
using namespace std;

Observe que a segunda linha de código não é necessária. Caso contrário, você precisará indicar manualmente o namespace std ao usar o contêiner de mapa em programas subsequentes (recomendado para iniciantes).

A definição do modelo para o contêiner do mapa é a seguinte:

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

Pode-se observar que o modelo de contêiner de mapa possui 4 parâmetros, e os 2 últimos parâmetros possuem valores padrão. Na maioria dos cenários, só precisamos definir o valor dos 2 primeiros parâmetros, alguns cenários podem usar o terceiro parâmetro, mas o último parâmetro quase nunca é usado.

função de membro

A tabela a seguir lista os métodos de membros comuns fornecidos pelo contêiner de mapa e suas respectivas funções:

função de membro Função
começar() Retorna um iterador bidirecional apontando para o primeiro (observe, o primeiro par de valores-chave classificado) no contêiner. Se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional do tipo const.
fim() Retorna um iterador bidirecional apontando para a posição após o último elemento do contêiner (observe que é o último que foi classificado), geralmente usado em conjunto com Begin(). Se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional do tipo const.
rbegin() Retorna um iterador bidirecional reverso apontando para o último elemento (nota, o último classificado). Se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional reverso do tipo const.
render() Retorna um iterador bidirecional reverso apontando para a posição anterior à posição do primeiro elemento (nota, o primeiro classificado). Se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional reverso do tipo const.
cbegin() Tem a mesma função que start(), mas com base nele é adicionado um atributo const, que não pode ser usado para modificar os pares chave-valor armazenados no contêiner.
um pouco() Tem a mesma função que end(), exceto que um atributo const é adicionado sobre ele, que não pode ser usado para modificar os pares chave-valor armazenados no contêiner.
crbegin() Tem a mesma função que rbegin(), mas com base nele é adicionado um atributo const, que não pode ser usado para modificar os pares chave-valor armazenados no contêiner.
crédito() Tem a mesma função que rend(), exceto que em sua base é adicionado um atributo const, que não pode ser usado para modificar os pares chave-valor armazenados no contêiner.
encontrar (chave) Procure o par chave-valor cuja chave é chave no contêiner do mapa. Se encontrado com sucesso, retorne um iterador bidirecional apontando para o par chave-valor; caso contrário, retorne o mesmo iterador que o método end(). Além disso, se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional do tipo const.
limite_inferior(chave) Retorna um iterador bidirecional apontando para o primeiro par chave-valor maior ou igual à chave no contêiner do mapa atual. Se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional do tipo const.
limite_superior (chave) Retorna um iterador apontando para o primeiro par chave-valor maior que a chave no contêiner do mapa atual. Se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional do tipo const.
intervalo_equal(chave) Este método retorna um objeto par (contém dois iteradores bidirecionais), onde os valores de retorno dos métodos pair.first e lower_bound() são equivalentes, e os valores de retorno dos métodos pair.second e upper_bound() são equivalentes. Ou seja, este método retornará um intervalo que contém o par chave-valor cuja chave é chave (o par chave-valor do contêiner do mapa é único, portanto, o intervalo contém no máximo um par chave-valor).
vazio() Retorna verdadeiro se o contêiner estiver vazio; caso contrário, falso.
tamanho() Retorna o número de pares de valores-chave armazenados no contêiner do mapa atual.
tamanho máximo() Devolve o número máximo de pares de valores-chave que o contentor do mapa pode conter.Sistemas operativos diferentes têm valores de retorno diferentes.
operador[] O contêiner do mapa sobrecarrega o operador []. Contanto que você conheça o valor da chave de um par chave-valor no contêiner do mapa, você pode obter diretamente o valor correspondente por meio da chave, assim como obter os elementos da matriz.
em (chave) Encontre o valor correspondente à chave-chave no contêiner do mapa; se não for encontrado, a função gerará uma exceção out_of_range.
inserir() Insira pares de valores-chave no contêiner do mapa.
apagar() Exclua o local especificado, o valor-chave (chave) especificado ou o par de valores-chave na área especificada do contêiner do mapa. Os capítulos subsequentes se concentrarão nesse método.
trocar() Troque os pares de valores-chave armazenados nos dois contêineres de mapas, o que significa que os tipos dos dois pares de valores-chave para a operação devem ser os mesmos.
claro() Limpe todos os pares de valores-chave no contêiner do mapa, mesmo que size() do contêiner do mapa seja 0.
lugar() Constrói um novo par de valores-chave na posição especificada no contêiner do mapa atual. Seu efeito é o mesmo da inserção de pares de valores-chave, porém mais eficiente.
emplace_hint() É essencialmente o mesmo que emplace()constrói um novo par chave-valor num contentor de mapa.A diferença é que o utilizador deve fornecer a este método um iterador indicando a localização onde o par chave-valor é gerado,e utilizá-lo. como o primeiro parâmetro do método.
contar (chave) No contêiner do mapa atual, encontre o número de pares chave-valor cuja chave é chave e retorne-o. Observe que, como o valor-chave de cada par de valores-chave no contêiner do mapa é único, o valor máximo de retorno desta função é 1.

Vários métodos de criação de contêiner de mapa C++

A classe de modelo do contêiner de mapa contém vários construtores, portanto, há muitas maneiras de criar um contêiner de mapa. A seguir são apresentados vários métodos comumente usados ​​para criar um contêiner de mapa.

  1. Um contêiner de mapa vazio pode ser criado chamando o construtor padrão da classe de contêiner de mapa, por exemplo:
std::map<std::string, int> myMap;

O contêiner myMap criado desta forma está inicialmente vazio, ou seja, nenhum par de valores-chave é armazenado. Como um contêiner de mapa vazio pode adicionar novos pares de valores-chave a qualquer momento, conforme necessário, é mais comum criar um contêiner de mapa vazio.

  1. Claro, ao criar o contêiner do mapa, ele também pode ser inicializado, por exemplo:
std::map<std::string, int> myMap{ {"C语言教程",10},{"STL教程",20} };

Assim, o contêiner myMap contém 2 pares de valores-chave em seu estado inicial.

Novamente, os pares de valores-chave armazenados no contêiner do mapa são essencialmente objetos de pares criados pelo modelo de classe de pares. Portanto, o programa a seguir também pode criar exatamente o mesmo contêiner myMap:

std::map<std::string, int> myMap{std::make_pair("C语言教程",10),std::make_pair("STL教程",20)};
  1. Além disso, em alguns cenários, um contêiner de mapas criado anteriormente pode ser usado para criar um novo contêiner de mapas. Por exemplo:
std::map<std::string, int> newMap(myMap);

Assim, chamando o construtor de cópia (replicação) do contêiner do mapa, um contêiner newMap exatamente igual ao myMap pode ser criado com sucesso.

No padrão C++11, um construtor de movimentação também é adicionado ao contêiner do mapa. O construtor move é chamado quando um objeto de mapa temporário é passado como parâmetro para o contêiner do mapa a ser inicializado. por exemplo:

// 创建一个会返回临时 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());

Observe que, ao chamar o construtor de cópia ou ao chamar o construtor de cópia, você deve garantir que os tipos dos dois contêineres sejam exatamente iguais.

  1. O modelo de classe de mapa também suporta a obtenção de pares de valores-chave na área especificada do contêiner de mapa estabelecido, criando e inicializando um novo contêiner de mapa. Por exemplo:
std::map<std::string, int> myMap{ {"C语言教程",10},{"STL教程",20} };
std::map<std::string, int> newMap(++myMap.begin(), myMap.end());

Aqui, ao chamar o iterador bidirecional do contêiner do mapa, percebe-se que ao criar o contêiner newMap, ele é inicializado como um contêiner contendo um par chave-valor {"STL Tutorial",20}.

  1. Obviamente, com base na criação de contêineres de mapas acima, podemos modificar manualmente as regras de classificação do contêiner de mapas. Por padrão, o contêiner do mapa invoca a regra std::less<T> para classificar todos os pares de valores-chave em ordem crescente de acordo com o tamanho da chave de cada par de valores-chave no contêiner. Portanto, as duas linhas a seguir para criar um contêiner de mapa são, na verdade, 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 o contêiner myMap gerado nos 2 métodos de criação acima, a ordem dos pares de valores-chave internos é a seguinte:

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

O programa a seguir modifica manualmente as regras de classificação do contêiner myMap para classificar em ordem decrescente:

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

Neste ponto, a ordem dos pares chave-valor dentro do contêiner myMap é:

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

Em alguns cenários específicos, também precisamos personalizar as regras de classificação do contêiner do mapa.

iterador

Seja o contêiner sequencial aprendido anteriormente ou o contêiner associativo, para implementar a operação de travessia é necessário utilizar o iterador deste tipo de contêiner. É claro que os contêineres de mapas não são exceção.

A biblioteca padrão C++ STL equipa o contêiner de mapas com um iterador bidirecional. Isso significa que os iteradores do contêiner de mapa só podem ser ++p、p++、--p、p--、*poperados e os iteradores só podem ser comparados usando ==o operador ou .!=

Vale ressaltar que, em comparação com os contêineres sequenciais, os contêineres de mapas fornecem mais métodos membros (conforme mostrado na tabela abaixo) e, ao chamá-los, podemos facilmente obter iteradores com significados especificados.

método membro Função
começar() Retorna um iterador bidirecional apontando para o primeiro (observe, o primeiro par de valores-chave classificado) no contêiner. Se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional do tipo const.
fim() Retorna um iterador bidirecional apontando para a posição após o último elemento do contêiner (observe que é o último que foi classificado), geralmente usado em conjunto com Begin(). Se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional do tipo const.
rbegin() Retorna um iterador bidirecional reverso apontando para o último elemento (nota, o último classificado). Se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional reverso do tipo const.
render() Retorna um iterador bidirecional reverso apontando para a posição anterior à posição do primeiro elemento (nota, o primeiro classificado). Se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional reverso do tipo const.
cbegin() Tem a mesma função que start(), mas com base nele é adicionado um atributo const, que não pode ser usado para modificar os pares chave-valor armazenados no contêiner.
um pouco() Tem a mesma função que end(), exceto que um atributo const é adicionado sobre ele, que não pode ser usado para modificar os pares chave-valor armazenados no contêiner.
crbegin() Tem a mesma função que rbegin(), mas com base nele é adicionado um atributo const, que não pode ser usado para modificar os pares chave-valor armazenados no contêiner.
crédito() Tem a mesma função que rend(), exceto que em sua base é adicionado um atributo const, que não pode ser usado para modificar os pares chave-valor armazenados no contêiner.
encontrar (chave) Procure o par chave-valor cuja chave é chave no contêiner do mapa. Se encontrado com sucesso, retorne um iterador bidirecional apontando para o par chave-valor; caso contrário, retorne o mesmo iterador que o método end(). Além disso, se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional do tipo const.
limite_inferior(chave) Retorna um iterador bidirecional apontando para o primeiro par chave-valor maior ou igual à chave no contêiner do mapa atual. Se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional do tipo const.
limite_superior (chave) Retorna um iterador apontando para o primeiro par chave-valor maior que a chave no contêiner do mapa atual. Se o contêiner do mapa for qualificado como const, esse método retornará um iterador bidirecional do tipo const.
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 容器自定义排序规则。

Acho que você gosta

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