std::vector::reserve în C++

std::vector C++98

reserve() rezervă memorie pentru n elemente, capacity() spune cât loc este rezervat, iar shrink_to_fit() cere eliberarea locului nefolosit.

Sintaxă
void reserve(size_type n);
size_type capacity() const noexcept;
void shrink_to_fit();   // C++11

Ce face std::vector::reserve

Un vector are două mărimi: size(), numărul de elemente, și capacity(), numărul de elemente pentru care are deja memorie. Când size() ajunge la capacity(), următorul push_back alocă o zonă mai mare și mută totul acolo.

reserve(n) alocă dinainte loc pentru cel puțin n elemente, fără să creeze elemente. Dacă știi câte vor fi, un singur reserve evită toate realocările. Dacă n este mai mare decât capacitatea, are loc o realocare și toți iteratorii, pointerii și referințele devin invalide; după aceea, cât timp size() <= n, push_back nu mai invalidează elementele existente.

shrink_to_fit() cere reducerea capacității la size(). Este doar o cerere: standardul permite implementării să o ignore (libc++ și libstdc++ o respectă). Dacă micșorează, realocă, deci invalidează și ea iteratorii.

Parametri

Parametru Descriere
n Numărul minim de elemente pentru care să existe memorie.

Ce returnează

reserve și shrink_to_fit: nimic. capacity: numărul de elemente care încap fără realocare.

Exemple

Exemplul 1: reserve evită realocările

Numărăm de câte ori se schimbă capacitatea în timp ce adăugăm 1000 de elemente. Numărul fără reserve depinde de bibliotecă (aici libc++).

#include <print>
#include <vector>

int realocari(bool cu_reserve) {
    std::vector<int> v;
    if (cu_reserve) {
        v.reserve(1000);
    }
    int numar = 0;
    std::size_t cap = v.capacity();
    for (int i = 0; i < 1000; ++i) {
        v.push_back(i);
        if (v.capacity() != cap) {
            ++numar;
            cap = v.capacity();
        }
    }
    return numar;
}

int main() {
    std::println("Fără reserve: {} realocări", realocari(false));
    std::println("Cu reserve(1000): {} realocări", realocari(true));
}

Rezultat

Fără reserve: 11 realocări
Cu reserve(1000): 0 realocări

Exemplul 2: size, capacity și shrink_to_fit

#include <print>
#include <vector>

int main() {
    std::vector<int> v;
    v.reserve(100);
    std::println("după reserve:       size={} capacity={}", v.size(), v.capacity());
    for (int i = 0; i < 10; ++i) {
        v.push_back(i);
    }
    std::println("după 10 push_back:  size={} capacity={}", v.size(), v.capacity());
    v.shrink_to_fit();
    std::println("după shrink_to_fit: size={} capacity={}", v.size(), v.capacity());
}

Rezultat

după reserve:       size=0 capacity=100
după 10 push_back:  size=10 capacity=100
după shrink_to_fit: size=10 capacity=10

Exemplul 3: Referință care rămâne validă datorită reserve

Capacitatea rezervată ajunge pentru toate elementele, deci vectorul nu se realocă și referința primul rămâne validă. Fără reserve, același cod ar fi comportament nedefinit.

#include <print>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> nume;
    nume.reserve(10);
    nume.push_back("Ana");
    const std::string& primul = nume[0];
    for (int i = 0; i < 5; ++i) {
        nume.push_back("invitat");     // size ajunge la 6 <= 10: fără realocare
    }
    std::println("Primul: {}, total: {}, capacity: {}", primul, nume.size(), nume.capacity());
}

Rezultat

Primul: Ana, total: 6, capacity: 10

Greșeli frecvente

  • Confunzi reserve cu resize: după v.reserve(10), v[0] = 1 este comportament nedefinit, pentru că vectorul tot nu are elemente.
  • Apelezi reserve(v.size() + 1) înainte de fiecare push_back. Așa forțezi o realocare la fiecare pas și pierzi creșterea geometrică; apelează reserve o singură dată, cu numărul total.
  • Te bazezi pe o anumită valoare a capacității (de exemplu „se dublează”). Felul în care crește capacitatea depinde de bibliotecă (libc++ dublează, MSVC crește cu 50%).

Comenzi înrudite

  • std::vector::push_back: Adaugă un element la sfârșitul vectorului; vectorul crește cu o poziție.
  • std::vector::resize: Schimbă numărul de elemente: adaugă elemente noi la final sau le șterge pe cele în plus.
  • std::vector::size: size() spune câte elemente are vectorul; empty() spune dacă nu are niciunul.
  • std::vector::clear: Șterge toate elementele vectorului; dimensiunea devine 0, dar memoria rezervată rămâne.
  • std::unordered_map::reserve: reserve() pregătește tabela hash pentru n elemente; bucket_count() și load_factor() arată cât de încărcată este.