Capítulo 9: La Biblioteca Estándar (STL)
9.1 Nivel Introductorio
Introducción a la STL
La Biblioteca Estándar de Plantillas (STL, por sus siglas en inglés) es un componente fundamental de C++ que proporciona una colección de clases y funciones genéricas. La STL facilita la programación al ofrecer contenedores, algoritmos e iteradores que pueden reutilizarse y adaptarse a diferentes tipos de datos.
Componentes principales de la STL
- Contenedores: Estructuras de datos que almacenan colecciones de objetos.
- Iteradores: Objetos que apuntan a elementos dentro de los contenedores y permiten recorrerlos.
- Algoritmos: Funciones genéricas que realizan operaciones sobre contenedores o rangos de elementos.
Contenedores básicos: vector, list
std::vector
El std::vector es un contenedor que representa un array dinámico. Permite almacenar elementos en un arreglo que puede cambiar de tamaño durante la ejecución del programa.
Características:
- Acceso aleatorio eficiente (
O(1)) a través de índices. - Inserción y eliminación de elementos al final en tiempo constante amortizado.
- Inserciones y eliminaciones en otras posiciones pueden ser costosas (
O(n)).
Declaración y uso:
#include <vector>
#include <iostream>
int main() {
std::vector<int> numeros;
// Agregar elementos
numeros.push_back(10);
numeros.push_back(20);
numeros.push_back(30);
// Acceso a elementos
std::cout << "Elemento en posición 1: " << numeros[1] << std::endl;
// Recorrido del vector
for (int num : numeros) {
std::cout << num << " ";
}
// Salida: 10 20 30
return 0;
}std::list
El std::list es una lista doblemente enlazada que permite inserciones y eliminaciones eficientes en cualquier posición.
Características:
- No tiene acceso aleatorio; se debe recorrer para acceder a elementos específicos.
- Inserciones y eliminaciones en cualquier posición en tiempo constante (
O(1)). - Mayor uso de memoria en comparación con
std::vector.
Declaración y uso:
#include <list>
#include <iostream>
int main() {
std::list<std::string> palabras;
// Agregar elementos
palabras.push_back("Hola");
palabras.push_back("Mundo");
palabras.push_front("¡");
// Recorrido de la lista
for (const std::string& palabra : palabras) {
std::cout << palabra << " ";
}
// Salida: ¡ Hola Mundo
return 0;
}9.2 Nivel Intermedio
Iteradores y algoritmos
Iteradores
Los iteradores son objetos que apuntan a elementos dentro de contenedores y permiten recorrerlos de manera uniforme.
Tipos de iteradores:
- Input Iterator: Lectura de datos secuencial.
- Output Iterator: Escritura de datos secuencial.
- Forward Iterator: Lectura/escritura secuencial en una dirección.
- Bidirectional Iterator: Lectura/escritura en ambas direcciones.
- Random Access Iterator: Acceso aleatorio eficiente.
Uso de iteradores:
#include <vector>
#include <iostream>
int main() {
std::vector<int> numeros = {1, 2, 3, 4, 5};
// Usando iteradores para recorrer el vector
for (std::vector<int>::iterator it = numeros.begin(); it != numeros.end(); ++it) {
std::cout << *it << " ";
}
// Salida: 1 2 3 4 5
return 0;
}Algoritmos
La STL proporciona una serie de algoritmos genéricos que funcionan con contenedores a través de iteradores.
Ejemplos de algoritmos:
std::sort: Ordena elementos en un rango.std::find: Busca un elemento en un rango.std::copy: Copia elementos de un rango a otro.std::for_each: Aplica una función a cada elemento de un rango.
Uso de algoritmos:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> numeros = {5, 2, 8, 1, 3};
// Ordenar el vector
std::sort(numeros.begin(), numeros.end());
// Buscar un elemento
auto it = std::find(numeros.begin(), numeros.end(), 3);
if (it != numeros.end()) {
std::cout << "Encontrado: " << *it << std::endl;
} else {
std::cout << "No encontrado." << std::endl;
}
// Aplicar una función a cada elemento
std::for_each(numeros.begin(), numeros.end(), [](int n) {
std::cout << n << " ";
});
// Salida: 1 2 3 5 8
return 0;
}Contenedores asociativos: map, set
std::map
Un std::map es un contenedor que almacena pares clave-valor ordenados por las claves. Las claves son únicas.
Declaración y uso:
#include <map>
#include <iostream>
int main() {
std::map<std::string, int> edades;
// Agregar elementos
edades["Ana"] = 28;
edades["Luis"] = 35;
edades["Carlos"] = 22;
// Acceder a elementos
std::cout << "Edad de Ana: " << edades["Ana"] << std::endl;
// Recorrer el mapa
for (const auto& par : edades) {
std::cout << par.first << " tiene " << par.second << " años." << std::endl;
}
return 0;
}std::set
Un std::set es un contenedor que almacena elementos únicos ordenados automáticamente.
Declaración y uso:
#include <set>
#include <iostream>
int main() {
std::set<int> numeros;
// Agregar elementos
numeros.insert(5);
numeros.insert(3);
numeros.insert(8);
numeros.insert(5); // No se agrega, ya existe
// Recorrer el set
for (int n : numeros) {
std::cout << n << " ";
}
// Salida: 3 5 8
return 0;
}Funciones útiles de la STL
std::pair y std::make_pair
Permiten crear pares de valores.
Ejemplo:
#include <utility>
#include <iostream>
int main() {
std::pair<std::string, int> persona;
persona = std::make_pair("María", 30);
std::cout << persona.first << " tiene " << persona.second << " años." << std::endl;
return 0;
}std::tuple y std::make_tuple
Permiten crear tuplas con más de dos elementos.
Ejemplo:
#include <tuple>
#include <iostream>
int main() {
std::tuple<std::string, int, char> datos;
datos = std::make_tuple("Carlos", 25, 'M');
std::cout << std::get<0>(datos) << ", Edad: " << std::get<1>(datos)
<< ", Sexo: " << std::get<2>(datos) << std::endl;
return 0;
}9.3 Nivel Avanzado
Adaptadores de contenedores: stack, queue, priority_queue
std::stack
Implementa una estructura tipo pila (LIFO: Last In, First Out).
Uso:
#include <stack>
#include <iostream>
int main() {
std::stack<int> pila;
// Agregar elementos
pila.push(10);
pila.push(20);
pila.push(30);
// Acceso y eliminación
while (!pila.empty()) {
std::cout << "Elemento: " << pila.top() << std::endl;
pila.pop();
}
return 0;
}std::queue
Implementa una estructura tipo cola (FIFO: First In, First Out).
Uso:
#include <queue>
#include <iostream>
int main() {
std::queue<std::string> cola;
// Agregar elementos
cola.push("Primer");
cola.push("Segundo");
cola.push("Tercero");
// Acceso y eliminación
while (!cola.empty()) {
std::cout << "Elemento: " << cola.front() << std::endl;
cola.pop();
}
return 0;
}std::priority_queue
Es una cola que ordena automáticamente los elementos según una prioridad.
Uso:
#include <queue>
#include <vector>
#include <functional>
#include <iostream>
int main() {
// Por defecto, es un max-heap
std::priority_queue<int> pqueue;
// Agregar elementos
pqueue.push(10);
pqueue.push(5);
pqueue.push(20);
// Acceso y eliminación
while (!pqueue.empty()) {
std::cout << "Elemento: " << pqueue.top() << std::endl;
pqueue.pop();
}
// Salida: 20 10 5
return 0;
}Programación genérica avanzada
Plantillas de clase
Las plantillas no solo se aplican a funciones, sino también a clases, permitiendo crear contenedores y estructuras de datos genéricas.
Ejemplo:
#include <iostream>
template <typename T>
class Caja {
private:
T contenido;
public:
Caja(T valor) : contenido(valor) {}
T obtenerContenido() {
return contenido;
}
};
int main() {
Caja<int> cajaEntero(100);
Caja<std::string> cajaString("Hola");
std::cout << "Caja entero: " << cajaEntero.obtenerContenido() << std::endl;
std::cout << "Caja string: " << cajaString.obtenerContenido() << std::endl;
return 0;
}Especialización de plantillas
Permite definir implementaciones específicas para ciertos tipos.
Ejemplo:
#include <iostream>
template <typename T>
class Operaciones {
public:
static T suma(T a, T b) {
return a + b;
}
};
// Especialización para tipo `char*` (cadenas estilo C)
template <>
class Operaciones<char*> {
public:
static std::string suma(char* a, char* b) {
return std::string(a) + std::string(b);
}
};
int main() {
int resultadoInt = Operaciones<int>::suma(5, 3);
std::cout << "Suma de enteros: " << resultadoInt << std::endl;
std::string resultadoStr = Operaciones<char*>::suma("Hola ", "Mundo");
std::cout << "Suma de cadenas: " << resultadoStr << std::endl;
return 0;
}Uso de std::function y std::bind
std::function
Es un contenedor para funciones y objetos función. Permite almacenar y pasar funciones como si fueran variables.
Ejemplo:
#include <functional>
#include <iostream>
int suma(int a, int b) {
return a + b;
}
int main() {
std::function<int(int, int)> funcionSuma = suma;
int resultado = funcionSuma(5, 7);
std::cout << "Resultado: " << resultado << std::endl;
return 0;
}std::bind
Permite crear funciones parciales o ligar ciertos parámetros a valores específicos.
Ejemplo:
#include <functional>
#include <iostream>
int potencia(int base, int exponente) {
int resultado = 1;
for (int i = 0; i < exponente; i++) {
resultado *= base;
}
return resultado;
}
int main() {
using namespace std::placeholders;
// Crear una función que siempre calcula el cuadrado
auto cuadrado = std::bind(potencia, _1, 2);
std::cout << "Cuadrado de 5: " << cuadrado(5) << std::endl; // Salida: 25
return 0;
}Aplicación en algoritmos
Ejemplo: Usar std::function en std::sort
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
int main() {
std::vector<int> numeros = {5, 2, 8, 3, 1};
// Función de comparación personalizada
std::function<bool(int, int)> comparar = [](int a, int b) {
return a > b;
};
// Ordenar utilizando la función de comparación
std::sort(numeros.begin(), numeros.end(), comparar);
for (int n : numeros) {
std::cout << n << " ";
}
// Salida: 8 5 3 2 1
return 0;
}