Uso de std::vector en C++
Aprende a utilizar std::vector en C++ para trabajar con arreglos dinámicos y gestionar datos de forma eficiente.
std::vector es el contenedor secuencial más utilizado en C++.
Proporciona un arreglo dinámico almacenado en memoria contigua:
acceso aleatorio O(1), push_back amortiguado O(1) y, por lo general,
O(n) para inserciones y eliminaciones en el medio debido al desplazamiento de elementos.
Este artículo cubre la gestión de capacidad (size/capacity), el comportamiento de crecimiento,
reserve/resize, push_back vs emplace_back,
invalidación de iteradores, diseño de memoria (data()),
shrink_to_fit y consejos de rendimiento.
1) Uso básico
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v; // vacío
v.push_back(10);
v.push_back(20);
v.push_back(30);
cout << "size=" << v.size()
<< " capacity=" << v.capacity() << "\n";
cout << v[1] << " " << v.at(2) << "\n"; // [] sin control, at() con control
for (int x : v) cout << x << " ";
}
size es la cantidad de elementos; capacity es la cantidad de elementos que el vector puede almacenar
antes de necesitar una nueva reasignación de memoria.
2) Memoria contigua y data()
Un vector almacena sus elementos en memoria contigua, lo que permite usar su búfer con APIs escritas en C.
#include <cstring> // memcpy
#include <vector>
int main() {
vector<char> buf(8, 0);
const char* msg = "C++";
memcpy(buf.data(), msg, 3); // memoria contigua garantizada
}
Esto es ideal para E/S, SIMD, gráficos, buffers de red y para interoperabilidad con bibliotecas en C.
3) Estrategia de crecimiento, reserve y resize
reserve(n): aumenta la capacidad a al menosnsin modificar el tamaño.resize(n): ajusta el tamaño an(y aumenta capacidad si es necesario).
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v;
v.reserve(1000); // reduce reasignaciones
for (int i=0; i<1000; i++)
v.push_back(i);
v.resize(1200, -1); // agrega 200 elementos nuevos con valor -1
cout << v.size() << " " << v.capacity() << "\n";
}
El factor de crecimiento depende de la implementación (normalmente entre 1.5× y 2×).
Usar reserve mejora significativamente el rendimiento en inserciones masivas.
4) Inserciones y eliminaciones: push_back vs emplace_back, insert/erase
push_back(x): inserta copiando o moviendo un objeto.emplace_back(args...): construye el objeto directamente en su ubicación final.insertyeraserequieren desplazar elementos → O(n).
#include <string>
#include <vector>
using namespace std;
struct Book {
string title;
double price;
int pages;
Book(string t, double p, int s)
: title(t), price(p), pages(s) {}
};
int main() {
vector<Book> books;
books.emplace_back("C++ Primer", 180.0, 950);
books.push_back(Book{"Effective C++", 150.0, 320});
books.insert(books.begin(), {"Clean Code", 175.0, 464});
books.erase(books.begin() + 1);
}
emplace_back es preferible para objetos costosos de copiar.
Si necesitas muchas inserciones en el medio, considera deque o list.
5) Invalidación de iteradores
Las reasignaciones y los desplazamientos de elementos pueden invalidar iteradores, punteros y referencias.
- Reasignación: invalida todos los iteradores y referencias.
- insert/erase: invalida a partir de la posición modificada.
#include <vector>
using namespace std;
int main() {
vector<int> v{1,2,3};
auto it = v.begin();
v.push_back(4); // puede reasignar memoria
// 'it' ahora puede ser inválido
}
Usa reserve para evitar reasignaciones; vuelve a obtener iteradores tras operaciones críticas.
6) Consejos de rendimiento
- Usa
reservesi conoces el tamaño final aproximado. - Usa
emplace_backpara construcción directa. - Prefiere movimientos (move semantics) a copias.
- Inserta al final si es posible; el medio es costoso.
clear()solo reduce tamaño, no capacidad.shrink_to_fit()no está garantizado.
7) assign, swap, shrink_to_fit
#include <vector>
using namespace std;
int main() {
vector<int> v{1,2,3,4,5};
v.assign(3, 9); // {9, 9, 9}
vector<int>(v).swap(v); // “shrink idiom”
v.shrink_to_fit();
}
La técnica “shrink idiom” suele ser más eficaz que shrink_to_fit().
8) Ordenación, búsqueda y algoritmos
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> a{5,1,4,2,3};
sort(a.begin(), a.end()); // 1 2 3 4 5
bool found4 = binary_search(a.begin(), a.end(), 4);
auto pos = lower_bound(a.begin(), a.end(), 3);
cout << found4 << " @" << (pos - a.begin()) << "\n";
}
Con C++20, std::ranges permite pipelines más expresivos.
9) Temas avanzados: Allocators, vector<bool>, objetos pequeños
- Allocators personalizados: para pools de memoria, seguimiento, alineación.
- vector<bool>: representación compactada en bits; cuidado con referencias “proxy”.
- Small-object optimization: se aplica a
std::string, no avector.
10) Ejemplo: Lista de libros
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
struct Book {
string title;
double price;
int pages;
Book(string t, double p, int s)
: title(move(t)), price(p), pages(s) {}
};
int main() {
vector<Book> books;
books.reserve(4);
books.emplace_back("C++ Primer", 180.0, 950);
books.emplace_back("Effective C++", 150.0, 320);
books.emplace_back("Clean Code", 175.0, 464);
sort(books.begin(), books.end(),
[](const Book& a, const Book& b){
return a.price < b.price;
});
auto it = find_if(books.begin(), books.end(),
[](const Book& b){ return b.price >= 160.0; });
if (it != books.end())
cout << it->title << " " << it->price << "\n";
}
11) Errores comunes
- Usar vector para muchas inserciones en el medio → mejor
dequeolist. - Usar iteradores inválidos después de una reasignación.
- Hacer copias innecesarias en lugar de usar move/emplace.
- No usar
reservecuando se conoce el tamaño final. - Asumir que
vector<bool>funciona como un vector normal.
12) TL;DR
vector= memoria contigua, acceso O(1), push_back O(1) amortiguado.reservereduce reasignaciones;resizecambia el tamaño.emplace_backconstruye objetos en sitio sin temporales.- Las reasignaciones invalidan iteradores.
data()permite integrar fácilmente APIs de C.shrink_to_fitno garantiza reducción real; el “shrink idiom” suele funcionar mejor.- Para muchas operaciones en el medio, usa
dequeolist. - Todos los ejemplos funcionan en Visual Studio 2022 y GCC 11+.