Cargando...

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


#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


#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.


#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


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


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


12) TL;DR

  • vector = memoria contigua, acceso O(1), push_back O(1) amortiguado.
  • reserve reduce reasignaciones; resize cambia el tamaño.
  • emplace_back construye objetos en sitio sin temporales.
  • Las reasignaciones invalidan iteradores.
  • data() permite integrar fácilmente APIs de C.
  • shrink_to_fit no garantiza reducción real; el “shrink idiom” suele funcionar mejor.
  • Para muchas operaciones en el medio, usa deque o list.
  • Todos los ejemplos funcionan en Visual Studio 2022 y GCC 11+.

Artículos relacionados