Contenedor vector de C++
En C++,vectores uno de los contenedores STL más utilizados.
vectorEn esencia es unarray dinámico con expansión automática, en comparación con los arrays tradicionales, vector no requiere gestionar la memoria manualmente y puede expandirse automáticamente según la cantidad de elementos.
Debido a que vector tiene:
- Almacenamiento en memoria contigua
- Acceso aleatorio rápido
- Ecosistema STL completo
- Amigable con la caché (Cache Friendly)
Por lo tanto, en el desarrollo moderno de C++, la mayoría de los escenarios de almacenamiento de datos secuenciales priorizan el uso de vector.
Características principales de vector
- Tamaño dinámico:vector puede expandirse y reducirse automáticamente.
- Memoria contigua:Los elementos se almacenan de forma contigua en la memoria, lo que permite una eficiencia de acceso muy alta.
- Acceso aleatorio:Permite acceder rápidamente a los elementos mediante índice, con una complejidad temporal de O(1).
- Gestión automática de memoria:No es necesario usar malloc/free manualmente.
- Compatibilidad con iteradores:Se puede usar convenientemente con los algoritmos de STL.
¿Por qué vector es muy rápido?
Los elementos de vector están dispuestos de forma contigua en la memoria:
[1][2][3][4][5]
Esta disposición es muy amigable con la caché de la CPU.
Cuando la CPU lee el primer elemento, suele precargar también los datos posteriores en la caché, por lo que recorrer un vector suele ser muy rápido.
Esta es también la razón por la que en muchos casos:
vector 比 list 更快
aunque list tenga una complejidad de eliminación teóricamente menor.
Usar vector
Antes de usar vector, es necesario incluir el archivo de cabecera:
#include <vector>
Crear un vector
Crear un vector vacío:
std::vector<int>vec;
Especificar el tamaño inicial:
std::vector<int>vec(5);
El código anterior crea 5 elementos con valor predeterminado 0.
Especificar el valor inicial:
std::vector<int>vec(5, 10);
Resultado de la creación:
[10, 10, 10, 10, 10]
Usar lista de inicialización:
std::vector<int>vec = {1, 2, 3, 4};
Añadir elementos
Usarpush_back()para añadir un elemento al final:
vec.push_back(100);
La complejidad temporal promedio de añadir un elemento al final de vector es:
O(1)
Acceder a elementos
Acceder usando índice:
int x = vec[0];
Usarat():
int y = vec.at(1);
Diferencia:
[]No verifica límites, es más rápido.at()Sí verifica límites, es más seguro.
Obtener el tamaño
vec.size();
Devuelve el número actual de elementos.
Diferencia entre size y capacity
Este es uno de los conceptos más importantes de vector.
std::vector<int>vec; vec.push_back(1); vec.push_back(2); std::cout << vec.size() << std::endl; std::cout << vec.capacity() << std::endl;
La salida puede ser:
2 4
- size:Número actual de elementos.
- capacity:Capacidad de memoria actualmente asignada.
capacity normalmente es mayor o igual que size.
Esto se debe a que vector reserva más espacio por adelantado para reducir expansiones frecuentes.
Mecanismo de expansión de vector
Cuando el espacio de vector es insuficiente, ocurre una expansión:
- Solicitar más memoria
- Copiar los elementos antiguos
- Liberar la memoria antigua
Este proceso tiene un costo elevado.
Por ejemplo:
std::vector<int>vec;
for (int i = 0; i < 1000000; i++) {
vec.push_back(i);
}
Pueden ocurrir múltiples reasignaciones de memoria.
reserve(): preasignar espacio
Para evitar expansiones frecuentes, se puede asignar memoria por adelantado:
std::vector<int>vec; vec.reserve(1000000);
Esto permite reducir:
- Reasignación de memoria
- Copia de elementos
- Pérdida de rendimiento
Esta es una técnica de optimización muy importante en el desarrollo de ingeniería.
Recorrer un vector
Usando índice
for (size_t i = 0; i < vec.size(); i++) {
std::cout << vec[i] << " ";
}
Usando iteradores
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
Bucle for basado en rango
for (int element : vec) {
std::cout << element << " ";
}
Eliminar elementos
Eliminar el tercer elemento:
vec.erase(vec.begin() + 2);
Nota:
Cuando vector elimina un elemento en el medio, los elementos posteriores se desplazan hacia adelante.
Por ejemplo:
1 2 3 4 5 删除 2 后: 1 3 4 5
donde:
3 4 5
todos deben moverse de posición.
Por lo tanto:
erase() 的时间复杂度为 O(n)
Escenarios en los que vector no es adecuado
- Inserciones frecuentes al inicio
- Eliminaciones frecuentes en el medio
- Expansión frecuente con objetos muy grandes
Por ejemplo:
vec.insert(vec.begin(), 100);
Esta es una operación lenta.
Invalidación de iteradores
Después de la expansión de vector, las direcciones de memoria originales pueden invalidarse.
Por ejemplo:
std::vector<int>vec = {1, 2, 3};
auto it = vec.begin();
vec.push_back(4);
std::cout << *it;
Aquíitpuede haberse invalidado.
Porque push_back() puede provocar una reasignación de memoria.
Es uno de los errores más comunes de vector.
Advertencias sobre clear()
vec.clear();
clear() solo vacía los elementos:
- size pasa a ser 0
- capacity puede conservarse
Es decir:
clear() 不一定释放内存
Si se desea liberar la memoria:
std::vector<int>().swap(vec);
o:
vec.shrink_to_fit();
push_back y emplace_back
En el C++ moderno se recomienda usar:
emplace_back()
Por ejemplo:
vec.push_back(Person("Tom", 20));
puede generar objetos temporales.
Mientras que:
vec.emplace_back("Tom", 20);
construye el objeto directamente en el interior de vector.
Normalmente es más eficiente.
Diferencias entre vector y array
| Característica | Array | vector |
|---|---|---|
| Tamaño fijo | Sí | no |
| Expansión automática | no | Sí |
| Memoria contigua | Sí | Sí |
| Acceso aleatorio | Rápido | Rápido |
| Compatibilidad con STL | Escasa | Completa |
| Acceso seguro | Ninguno | at() |
Complejidad de las operaciones comunes de vector
| Operación | Complejidad temporal |
|---|---|
| Acceso aleatorio | O(1) |
| push_back | O(1) promedio |
| Eliminación al final | O(1) |
| Inserción al inicio | O(n) |
| Eliminación en el medio | O(n) |
| clear | O(n) |
Ejemplo: gestión de calificaciones de estudiantes
Ejemplo
#include <iostream>
#include <vector>
#include <numeric>
int main() {
std::vector<int>scores = {85, 90, 78, 92};
scores.push_back(88);
std::cout << "Todas las calificaciones: ";
for (int score : scores) {
std::cout << score << " ";
}
std::cout << std::endl;
int total = std::accumulate(scores.begin(), scores.end(), 0);
double average = total * 1.0 / scores.size();
std::cout << "Calificación promedio: " << average << std::endl;
scores.erase(scores.begin() + 2);
std::cout << "Calificaciones después de eliminar: ";
for (int score : scores) {
std::cout << score << " ";
}
std::cout << std::endl;
return 0;
}
El resultado de salida es el siguiente:
所有成绩: 85 90 78 92 88 平均成绩: 86.6 删除后成绩: 85 90 92 88
Resumen
vector es una de las estructuras de datos más importantes y utilizadas en el C++ moderno.
Sus ventajas principales:
- Expansión dinámica
- Memoria contigua
- Acceso aleatorio rápido
- Amigable con la caché
- Ecosistema STL completo
Pero también hay que tener en cuenta:
- Costo de rendimiento de la expansión
- Baja eficiencia en la eliminación en el medio
- Problema de invalidación de iteradores
Para la gran mayoría de escenarios de almacenamiento secuencial:
vector 通常都是默认首选容器Otras extensiones