El Standard Template Library (STL) de C++ proporciona una colección robusta de estructuras de datos y algoritmos. Dentro de los contenedores, los vector son arreglos dinámicos que ofrecen acceso aleatorio y manejo flexible de elementos.
Características Clave de vector
- Acceso Rápido: Como los arreglos tradicionales, los
vectorpermiten el acceso a elementos mediante índices en tiempo constante (O(1)) debido a su naturaleza de memoria contigua. - Redimensionamiento Automático: Cuando un
vectorse llena, automáticamente asigna un bloque de memoria más grande y copia los elementos existentes. Este proceso de crecimiento, a menudo duplicando la capacidad, puede ser costoso. - Eficiencia en Operaciones de Extremo: Añadir o eliminar elementos al final de un
vectores generalmente una operación rápida (O(1)), asumiendo que no se requiere una realocación. Las inserciones o eliminaciones en el medio o al principio son más lentas (O(n)) ya que requieren desplazar elementos.
Ejemplo de Uso
#include <vector>
#include <iostream>
#include <algorithm> // Necesario para std::for_each
// Usando el espacio de nombres std para conveniencia
using namespace std;
int main() {
// --- Inicialización ---
vector<int> numeros; // Vector vacío
vector<int> valores(5, 8); // Vector con 5 elementos, todos inicializados a 8
vector<int> lista = {10, 20, 30}; // Inicialización con lista de C++11
// --- Acceso a Elementos ---
// Acceso directo (sin verificación de límites)
int primerElemento = lista[0];
// Acceso con verificación de límites (lanza excepción si el índice es inválido)
int segundoElemento = lista.at(1);
int primerValor = valores.front(); // Obtiene el primer elemento
int ultimoValor = valores.back(); // Obtiene el último elemento
cout << "Primer elemento de lista: " << primerElemento << endl;
cout << "Segundo elemento de lista: " << segundoElemento << endl;
cout << "Primer valor de valores: " << primerValor << endl;
cout << "Último valor de valores: " << ultimoValor << endl;
// --- Modificación ---
lista.push_back(40); // Añade 40 al final
lista.pop_back(); // Elimina el último elemento (40)
// Inserta el valor 25 en la posición después del primer elemento
lista.insert(lista.begin() + 1, 25);
// Elimina el elemento en la posición del iterador
lista.erase(lista.begin());
lista.clear(); // Elimina todos los elementos
// --- Gestión de Capacidad ---
bool estaVacio = lista.empty(); // Verifica si el vector está vacío
size_t tamanoActual = valores.size(); // Número actual de elementos
size_t capacidadActual = valores.capacity(); // Capacidad total de memoria asignada
cout << "¿Está lista vacío? " << (estaVacio ? "Sí" : "No") << endl;
cout << "Tamaño actual de valores: " << tamanoActual << endl;
cout << "Capacidad actual de valores: " << capacidadActual << endl;
// Reserva espacio para al menos 100 elementos para evitar realocaciones frecuentes
valores.reserve(100);
// Cambia el número de elementos a 10. Si es mayor que el tamaño actual, los nuevos se inicializan a 0.
valores.resize(10);
// --- Recorrido de Elementos ---
// Usando iteradores
cout << "Recorriendo 'valores' con iteradores: ";
for (auto it = valores.begin(); it != valores.end(); ++it) {
cout << *it << " ";
}
cout << endl;
// Usando bucle for basado en rango (C++11 y posterior)
cout << "Recorriendo 'valores' con for basado en rango: ";
for (int val : valores) {
cout << val << " ";
}
cout << endl;
return 0;
}
Consideraciones Importantes
- Seguridad de Índices: El operador
[]no realiza comprobaciones de límites, lo que puede llevar a comportamientos indefinidos si se accede a un índice inválido. Utiliceat()para un acceso seguro que lance una excepciónstd::out_of_rangeen caso de error. - Validez de Iteradores: Las operaciones de inserción y eliminación (excepto las de cola) pueden invalidar los iteradores. En particular, si ocurre una realocación de memoria, todos los iteradores existentes se vuelven inválidos.
- Optimización de Rendimiento: Para escenarios que requieren inserciones o eliminaciones frecuentes en el medio de la secuencia, considere usar
std::listostd::deque, que están optimizados para estas operaciones.
Funcionamiento Interno y Optimización
- Gestión de Memoria: Internamente, un
vectorusa un bloque contiguo de memoria para almacenar sus elementos. Mantiene punteros o índices para gestionar el inicio, el fin de los datos utilizados y el final de la memoria asignada. - Estrategia de Crecimiento: Cuando se necesita más espacio, el
vectorasigna un nuevo bloque de memoria, generalmente el doble del tamaño anterior, y copia los elementos. Para minimizar el impacto de este proceso, se recomienda usarreserve()si se tiene una idea del tamaño final o de los picos de uso. - Requisitos del Tipo de Elemento: Los tipos de datos almacenados en un
vectordeben ser *copiables* y *asignables*. Esto significa que deben tener constructores de copia y operadores de asignación definidos adecuadamente, especialmente para clases que manejan recursos propios (necesitando una copia profunda).
Recomendaciones de Uso
-
Pre-asignación de Capacidad: Si conoce aproximadamente cuántos elementos almacenará su
vector, utilicereserve(n)al principio para pre-asignar memoria y evitar múltiples realocaciones costosas. -
Elección del Contenedor Adecuado: Si su caso de uso implica muchas inserciones/eliminaciones en posiciones arbitrarias, un
std::listostd::dequepodría ser más apropiado que unvector. -
Liberación de Memoria: El método
clear()solo elimina los elementos pero no libera la memoria previamente asignada. Si necesita liberar la memoria de unvector(por ejemplo, para reducir el consumo de memoria), puede usar la técnica de intercambio con unvectortemporal vacío: ```cpp// Suponiendo que 'miVector' contiene datos y desea liberar su memoria std::vector().swap(miVector); // miVector ahora está vacío y su memoria ha sido liberada