C++ STL: contenedores asociativos

Descripción general de los contenedores asociativos

Los contenedores secuenciales almacenan todos los tipos de datos básicos de C++, mientras que los contenedores asociativos son bastante diferentes. Al almacenar valores de elementos, este tipo de contenedor también proporcionará un valor adicional (también llamado "clave" para cada elemento, su esencia también es un tipo de datos básicos de C++ o un elemento de un tipo personalizado), su función es que en el proceso de uso de un contenedor asociativo, si se conoce el valor de la clave del elemento objetivo, el elemento objetivo se puede encontrar directamente a través de la clave sin tener que pasar por La forma de atravesar todo el contenedor.

Abandonar contenedores secuenciales y usar contenedores asociativos para almacenar elementos a menudo se debe a que los contenedores asociativos pueden encontrar, leer o eliminar rápidamente elementos almacenados, y este tipo de contenedor inserta elementos de manera más eficiente que los contenedores secuenciales.

Es decir, los elementos almacenados en el contenedor asociativo son todos "pares clave-valor" (<key,value>), que es la mayor diferencia con el contenedor secuencial. Además, los elementos almacenados en el contenedor de secuencia no están ordenados de forma predeterminada, mientras que los elementos almacenados en el contenedor asociativo están ordenados en orden ascendente de forma predeterminada según el tamaño del valor clave de cada elemento.

Estas características de los contenedores asociativos se atribuyen al hecho de que cuando la biblioteca estándar STL implementa este tipo de contenedor, la capa inferior utiliza la estructura de datos de "árbol rojo-negro" para organizar y almacenar cada par clave-valor.

tipo de contenedor asociativo

La biblioteca estándar C++ STL proporciona cuatro tipos de contenedores asociativos, a saber, mapa, conjunto, multimapa y multiconjunto, y sus respectivas características se muestran en la siguiente tabla:

nombre de contenedor asociativo características
mapa Definida en el archivo de encabezado <mapa>, la clave de cada elemento de los datos almacenados en este contenedor debe ser única (es decir, no se puede repetir) y el contenedor se ordenará en orden ascendente de forma predeterminada según el tamaño de cada uno. clave de elemento (llamar a std:: less<T>).
colocar Definido en el archivo de encabezado <set>, utilizando los datos almacenados en este contenedor, la clave y el valor de cada elemento son exactamente iguales y el valor de cada elemento no se puede repetir (se garantiza la unicidad de cada clave de elemento). El contenedor se ordena automáticamente en orden ascendente según el tamaño de la clave (en realidad, el valor del elemento) de cada elemento (llame a std::less<T>).
multimapa Definido en el archivo de encabezado <mapa>, la única diferencia con el contenedor de mapas es que las claves de los elementos almacenados en el contenedor de mapas múltiples se pueden repetir.
conjunto múltiple Definido en el archivo de encabezado <set>, la única diferencia con el contenedor de conjuntos es que el valor del elemento almacenado en el contenedor de conjuntos múltiples se puede repetir (una vez que se repite el valor, significa que la clave también se repite).

Además, C++ 11 ha agregado 4 nuevos contenedores hash, a saber, unordered_map, unordered_multimap y unordered_set, unordered_multiset. Estrictamente hablando, también son contenedores asociativos, pero el contenedor hash subyacente utiliza una tabla hash en lugar de un árbol rojo-negro.

Uso detallado de par

Sabemos que los contenedores asociativos almacenan datos en forma de "pares clave-valor", como por ejemplo:

<"C语言教程", "http://c.biancheng.net/c/">
<"Python教程", "http://c.biancheng.net/python/">
<"Java教程", "http://c.biancheng.net/java/">

Como se muestra arriba, cada fila representa un par clave-valor, donde el primer elemento actúa como clave y el segundo elemento actúa como valor.

Tenga en cuenta que, según las características de los datos almacenados en cada contenedor asociativo, el almacenamiento de contenedores asociativos de conjuntos y conjuntos múltiples solo se puede usar cuando las claves y los valores en cada par clave-valor son correspondientemente iguales; de lo contrario, los contenedores asociativos de mapas o mapas múltiples deben ser usado.

Teniendo en cuenta que el "par clave-valor" no es un tipo de datos ordinario, la biblioteca estándar STL de C++ proporciona una plantilla de clase de par, que se utiliza especialmente para convertir dos elementos ordinarios primero y segundo (que pueden ser tipos de datos básicos de C++, estructuras, y tipo definido por clase) se crea como un nuevo elemento <primero, segundo>. No es difícil ver por el formato de sus elementos que usar la plantilla de clase de par para crear elementos en forma de "pares clave-valor" es perfecto.

Tenga en cuenta que la plantilla de clase de par está definida en el archivo de encabezado <utilidad>, por lo que este archivo de encabezado debe importarse antes de usar esta plantilla de clase.

  1. Antes del estándar C++11, se proporcionaban los siguientes tres constructores en la plantilla de clase de par:
// 1) 默认构造函数,即创建空的 pair 对象
pair();
// 2) 直接使用 2 个元素初始化成 pair 对象
pair (const first_type& a, const second_type& b);
// 3) 拷贝(复制)构造函数,即借助另一个 pair 对象,创建新的 pair 对象
template<class U, class V> pair (const pair<U,V>& pr);

En el estándar C++ 11, sobre la base de la introducción de referencias rvalue, se agregan los dos constructores siguientes a la plantilla de clase de par:

// 4) 移动构造函数
template<class U, class V> pair (pair<U,V>&& pr);
// 5) 使用右值引用参数,创建 pair 对象
template<class U, class V> pair (U&& a, V&& b);

Además, la plantilla de clase de par en el estándar C++ 11 también agrega el siguiente constructor: pair (piecewise_construct_t pwc, tuple<Args1...> first_args, tuple<Args2...> second_args);, pero esta forma de construir una plantilla de clase de par rara vez se usa.

El siguiente programa demuestra los métodos anteriores para crear objetos de par:

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

int main() {
    // 调用构造函数 1,也就是默认构造函数
    pair <string, double> pair1;
    // 调用第 2 种构造函数
    pair <string, string> pair2("STL教程","http://c.biancheng.net/stl/");  
    // 调用拷贝构造函数
    pair <string, string> pair3(pair2);
    // 调用移动构造函数
    pair <string, string> pair4(make_pair("C++教程", "http://c.biancheng.net/cplus/"));
    // 调用第 5 种构造函数
    pair <string, string> 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;
}

La salida del programa es:

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/

Cuando el programa anterior crea el objeto pair4, llama a la función make_pair(), que también la proporciona el archivo de encabezado <utility>, y su función es generar un objeto pair. Por lo tanto, cuando pasamos el valor de retorno de la función make_pair() (que es un objeto temporal) como parámetro al constructor pair(), llama al constructor de movimiento en lugar del constructor de copia.

Sobre la base del programa anterior, C++ 11 también nos permite asignar valores manualmente al objeto par1, como por ejemplo:

pair1.first = "Java教程";
pair1.second = "http://c.biancheng.net/java/";
cout << "new pair1: " << pair1.first << " " << pair1.second << endl;

El resultado de la ejecución es:

new pair1: Java教程 http://c.biancheng.net/java/

Al mismo tiempo, el proceso de creación del objeto pair4 en el programa anterior también se puede escribir de la siguiente forma, que es completamente equivalente:

pair <string, string> pair4 = make_pair("C++教程", "http://c.biancheng.net/cplus/");
cout << "pair4: " << pair4.first << " " << pair4.second << endl;
  1. <utility>Además de proporcionar métodos para crear objetos de par en el archivo de encabezado, <、<=、>、>=、==、!=estos 6 operadores están sobrecargados para objetos de par. Las reglas de operación son: para comparar dos objetos de par, primero compare el tamaño del par.primer elemento, si Si son iguales, continúe comparando el tamaño del par.segundo elemento.

Tenga en cuenta que para los dos pares de objetos que se comparan, los tipos de clave y valor correspondientes son los mismos; de lo contrario, no habrá comparabilidad y el compilador le indicará que no hay ningún operador coincidente, es decir, no se puede encontrar ningún operador sobrecargado adecuado.

Por ejemplo:

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

int main() {
    pair <string, int> pair1("STL教程", 20);
    pair <string, int> pair2("C++教程", 20);
    pair <string, int> 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;
}

El resultado de la ejecución del programa es:

pair != pair2
pair2 != pair3
  1. Finalmente, cabe señalar que la plantilla de clase de par también proporciona una función miembro swap (), que puede intercambiar los pares clave-valor de dos objetos de par. La premisa para una operación exitosa es que los tipos de clave y valor de los dos Los objetos del par deben ser iguales. Por ejemplo:
#include <iostream>
#include <utility>      // pair
#include <string>       // string
using namespace std;

int main() {
    pair <string, int> pair1("pair", 10);                   
    pair <string, int> pair2("pair2", 20);
    // 交换 pair1 和 pair2 的键值对
    pair1.swap(pair2);
    cout << "pair1: " << pair1.first << " " << pair1.second << endl;
    cout << "pair2: " << pair2.first << " " << pair2.second << endl;
    return 0;
}

El resultado de la ejecución del programa es:

pair1: pair2 20
pair2: pair 10

Supongo que te gusta

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