C++ std::vector

Arrays e std::array têm tamanho fixo: você decide quantos elementos cabem quando escreve o programa. Mas quantos itens a Ana vai pôr na lista de compras hoje? Quantos produtos o cliente vai colocar no carrinho? Não dá para saber. O std::vector (vector = vetor) é uma lista que cresce e diminui enquanto o programa roda. Ele é o container padrão do C++: quando precisar de uma lista e não tiver um bom motivo para outra coisa, use std::vector.

Você vai aprender
  • Como criar um vetor vazio, com valores ou com N elementos.
  • Como acrescentar no fim (push_back) e tirar do fim (pop_back).
  • Como saber o tamanho (size), se está vazio (empty) e como esvaziar (clear).
  • Como acessar com [i], at(i), front() e back().
  • Como percorrer, inclusive o aviso de "sinais diferentes" com size().
  • Como inserir e apagar no meio com insert e erase (o básico de begin()).
  • A ideia de capacity e reserve.

Os programas foram compilados com o g++ 15 na máquina fictícia da Ana, na pasta ~/cpp, com g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa. Você já viu o std::vector de passagem no for de intervalo; agora é a vez dele.


A ideia: um caderno de lista de compras

O array é como uma cartela de ovos: tem 12 buracos e pronto. O std::vector é como uma lista num caderno:

  • começa vazia, ou com alguns itens;
  • você escreve um item novo no fim sempre que lembra de algo;
  • pode riscar o último, ou um do meio;
  • a qualquer momento, conta quantos itens há.

E, quando a página enche, o caderno "vira a folha" sozinho: o vetor arruma mais espaço automaticamente, sem você precisar se preocupar.


1. Criar um vetor

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vazio;
    std::vector<int> notas = {7, 9, 6};
    std::vector<int> zeros(5);
    std::vector<int> setes(3, 7);

    std::cout << "vazio: " << vazio.size() << " itens\n";
    std::cout << "notas: " << notas.size() << " itens\n";
    std::cout << "zeros: ";
    for (int x : zeros) std::cout << x << ' ';
    std::cout << "\nsetes: ";
    for (int x : setes) std::cout << x << ' ';
    std::cout << '\n';
}
#include <vector>
O cabeçalho do std::vector.
std::vector<int>
"Um vetor de int". Entre < > vai só o tipo dos elementos. Não há tamanho: ele muda durante a execução.
vazio;
Sem nada depois: um vetor vazio, com 0 elementos. Diferente de int x;, aqui não há lixo: um vetor sempre começa num estado válido.
notas = {7, 9, 6};
Com valores iniciais, entre chaves.
zeros(5)
Com parênteses: 5 elementos, todos 0.
setes(3, 7)
3 elementos, todos valendo 7.
$ g++ -std=c++23 -Wall -Wextra criar.cpp -o criar
$ ./criar
vazio: 0 itens
notas: 3 itens
zeros: 0 0 0 0 0
setes: 7 7 7 
Parênteses e chaves não são a mesma coisa

Com std::vector, (5) quer dizer "5 elementos" e {5} quer dizer "um elemento que vale 5":

#include <iostream>
#include <vector>

int main() {
    std::vector<int> a(5);
    std::vector<int> b{5};
    std::cout << "a(5): " << a.size() << " itens\n";
    std::cout << "b{5}: " << b.size() << " item, que vale " << b[0] << '\n';
}
$ g++ -std=c++23 -Wall -Wextra chaves.cpp -o chaves
$ ./chaves
a(5): 5 itens
b{5}: 1 item, que vale 5

Regra prática: chaves para listar os valores; parênteses para dizer quantos.


2. Acrescentar no fim: push_back

O jeito mais comum de usar um vetor é começar vazio e ir acrescentando. push_back(valor) ("empurrar para trás") coloca o valor no fim da lista, e o tamanho aumenta em 1:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> notas;
    std::cout << "Começo: " << notas.size() << " notas\n";

    notas.push_back(7);
    notas.push_back(9);
    notas.push_back(6);
    std::cout << "Depois de 3 push_back: " << notas.size() << " notas\n";

    for (int nota : notas) {
        std::cout << nota << ' ';
    }
    std::cout << '\n';
}
$ g++ -std=c++23 -Wall -Wextra push.cpp -o push
$ ./push
Começo: 0 notas
Depois de 3 push_back: 3 notas
7 9 6 

.size() responde quantos elementos há agora. Os elementos ficam na ordem em que foram acrescentados.

A lista de compras da Ana

Juntando push_back com o while (true) e o break da aula break e continue, e o getline (que lê uma linha inteira, com espaços), sai um programa de verdade: a Ana digita os itens, um por linha, e uma linha vazia termina a lista.

#include <iostream>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> lista;
    std::string item;

    std::cout << "Digite os itens (linha vazia para terminar):\n";
    while (true) {
        std::cout << "Item: ";
        std::getline(std::cin, item);
        if (item.empty()) {
            break;
        }
        lista.push_back(item);
    }

    std::cout << "\nLista de compras (" << lista.size() << " itens):\n";
    for (std::size_t i = 0; i < lista.size(); i++) {
        std::cout << i + 1 << ". " << lista[i] << '\n';
    }
}
$ g++ -std=c++23 -Wall -Wextra lista.cpp -o lista
$ ./lista
Digite os itens (linha vazia para terminar):
Item: arroz
Item: feijão
Item: café com leite
Item: detergente
Item:

Lista de compras (4 itens):
1. arroz
2. feijão
3. café com leite
4. detergente
  • std::vector<std::string>: um vetor de textos. Qualquer tipo serve.
  • item.empty() é verdadeiro quando a linha veio vazia (só Enter): é a saída do laço.
  • Antes de rodar, o programa não sabe quantos itens haverá. Com um array, você teria que chutar um tamanho máximo; com o vetor, tanto faz se forem 2 ou 2000.
  • O laço final usa índice para numerar. O tipo std::size_t é o tipo que o .size() devolve; a seção 5 explica por quê.

Missão: o vetor lista começa vazio. Acrescente "arroz", "feijão" e "café" com push_back. O resto do programa mostra o tamanho e os itens.

#include <iostream>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> lista;
    // acrescente os itens aqui

    std::cout << lista.size() << " itens\n";
    for (const auto& item : lista) {
        std::cout << "- " << item << '\n';
    }
}
#include <iostream>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> lista;
    lista.push_back("arroz");
    lista.push_back("feijão");
    lista.push_back("café");
    std::cout << lista.size() << " itens\n";
    for (const auto& item : lista) {
        std::cout << "- " << item << '\n';
    }
}

3. Acessar: [i], at(i), front(), back()

O acesso é igual ao do std::array: [i] sem garantia de conferência, .at(i) sempre conferido. Os índices vão de 0 a size() − 1:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> notas = {7, 9, 6};
    notas[1] = 10;
    std::cout << "notas[1]: " << notas[1] << '\n';
    std::cout << "notas.at(2): " << notas.at(2) << '\n';
    std::cout << "notas.at(5): " << notas.at(5) << '\n';
}
$ g++ -std=c++23 -Wall -Wextra acesso.cpp -o acesso
$ ./acesso
notas[1]: 10
notas.at(2): 6
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 5) >= this->size() (which is 3)
Abortado                   (imagem do núcleo gravada) ./acesso
$ echo $?
134

A mensagem diz: na conferência de intervalo do vetor (_M_range_check), o índice pedido (5) é maior ou igual ao tamanho (3). Como no std::array, a exceção pode ser tratada com try/catch (veja std::array e Exceções).

No editor do guia, que usa o Clang com a biblioteca libc++, a mesma exceção aparece como libc++abi: terminating due to uncaught exception of type std::out_of_range: vector.

[i] não cria elementos

Num vetor vazio, notas[0] = 7; não acrescenta nada: a posição 0 não existe, e escrever nela é comportamento indefinido. O g++ 15 sem otimização pega o erro e aborta (veja Quando dá errado); com -O2, ninguém confere, e no editor do guia também não: lá, o programa parece funcionar, o que é o pior tipo de erro. Para acrescentar, sempre push_back.

As pontas e tirar do fim

.front() é o primeiro elemento; .back(), o último. .pop_back() ("tirar de trás") remove o último, como tirar o prato de cima de uma pilha:

#include <iostream>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> pilha = {"prato azul", "prato verde", "prato branco"};
    std::cout << "Primeiro: " << pilha.front() << '\n';
    std::cout << "Último: " << pilha.back() << '\n';

    pilha.pop_back();
    std::cout << "Depois do pop_back, último: " << pilha.back()
              << " (" << pilha.size() << " itens)\n";
}
$ g++ -std=c++23 -Wall -Wextra pontas.cpp -o pontas
$ ./pontas
Primeiro: prato azul
Último: prato branco
Depois do pop_back, último: prato verde (2 itens)

Repare que pop_back() não devolve o elemento removido: se você precisa dele, leia com back() antes. E nunca chame pop_back(), front() ou back() num vetor vazio; confira com empty() antes.


4. Vazio ou não: empty e clear

#include <iostream>
#include <vector>

int main() {
    std::vector<int> carrinho = {3, 1, 2};
    std::cout << std::boolalpha;
    std::cout << "Vazio? " << carrinho.empty() << " (" << carrinho.size() << " itens)\n";
    carrinho.clear();
    std::cout << "Vazio? " << carrinho.empty() << " (" << carrinho.size() << " itens)\n";
    if (carrinho.empty()) {
        std::cout << "O carrinho está vazio.\n";
    }
}
$ g++ -std=c++23 -Wall -Wextra limpar.cpp -o limpar
$ ./limpar
Vazio? false (3 itens)
Vazio? true (0 itens)
O carrinho está vazio.
  • .empty() pergunta "está vazio?" e devolve true ou false. É a forma recomendada (mais clara que size() == 0).
  • .clear() apaga todos os elementos. O vetor continua existindo, pronto para receber outros.

Missão: a entrada traz as notas 7 9 6 8 e um 0 no fim. Leia num laço, guardando cada nota no vetor com push_back até vir o 0. Depois, se o vetor não estiver vazio, mostre quantas notas há e a maior: 4 notas, maior: 9.

#include <iostream>
#include <vector>

int main() {
    std::vector<int> notas;
    // leia as notas até o 0

    if (!notas.empty()) {
        int maior = notas.front();
        for (int n : notas) {
            if (n > maior) {
                maior = n;
            }
        }
        std::cout << notas.size() << " notas, maior: " << maior << '\n';
    }
}
#include <iostream>
#include <vector>

int main() {
    std::vector<int> notas;
    while (true) {
        int n = 0;
        std::cin >> n;
        if (n == 0) {
            break;
        }
        notas.push_back(n);
    }
    if (!notas.empty()) {
        int maior = notas.front();
        for (int n : notas) {
            if (n > maior) {
                maior = n;
            }
        }
        std::cout << notas.size() << " notas, maior: " << maior << '\n';
    }
}

5. Percorrer

Tudo o que você viu nas aulas de laços vale para o vetor:

  • Sem índice (o mais comum): for (const auto& item : lista) para ler, for (auto& item : lista) para alterar (veja for de intervalo).
  • Com índice: for (std::size_t i = 0; i < lista.size(); i++), quando você precisa da posição.

Por que std::size_t e não int? Porque .size() devolve um número sem sinal, e o g++ avisa quando você compara os dois:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> notas = {7, 9, 6};
    for (int i = 0; i < notas.size(); i++) {
        std::cout << notas[i] << ' ';
    }
    std::cout << '\n';
}
$ g++ -std=c++23 -Wall -Wextra sinal.cpp -o sinal
sinal.cpp: In function ‘int main()’:
sinal.cpp:6:23: warning: comparison of integer expressions of different signedness: ‘int’ and ‘std::vector<int>::size_type’ {aka ‘long unsigned int’} [-Wsign-compare]
    6 |     for (int i = 0; i < notas.size(); i++) {
      |                     ~~^~~~~~~~~~~~~~

"Comparação de inteiros com sinais diferentes: int e o size_type do vetor (que é long unsigned int)". O programa funciona, mas o aviso é bem-vindo: misturar com e sem sinal dá resultados absurdos com números negativos (por exemplo, i < notas.size() - 1 num vetor vazio, veja Inteiros). As saídas: std::size_t i, ou, se não precisa do índice, o for de intervalo.


6. Inserir e apagar no meio: insert e erase

push_back e pop_back trabalham no fim. Para mexer no meio, existem insert (inserir) e erase (apagar). Eles não recebem um índice, e sim um iterador: um "marcador de posição" dentro do vetor. Por enquanto, basta saber dois:

  • lista.begin(): marca o primeiro elemento. lista.begin() + 2 marca o elemento de índice 2.
  • lista.end(): marca a posição depois do último (não é um elemento!). lista.end() - 1 marca o último.
#include <iostream>
#include <string>
#include <vector>

void mostrar(const std::vector<std::string>& lista) {
    for (const auto& item : lista) {
        std::cout << item << " | ";
    }
    std::cout << '\n';
}

int main() {
    std::vector<std::string> lista = {"arroz", "feijão", "café"};
    mostrar(lista);

    lista.insert(lista.begin(), "pão");
    mostrar(lista);

    lista.insert(lista.begin() + 2, "leite");
    mostrar(lista);

    lista.erase(lista.begin() + 1);
    mostrar(lista);

    lista.erase(lista.end() - 1);
    mostrar(lista);
}
$ g++ -std=c++23 -Wall -Wextra inserir.cpp -o inserir
$ ./inserir
arroz | feijão | café |
pão | arroz | feijão | café |
pão | arroz | leite | feijão | café |
pão | leite | feijão | café |
pão | leite | feijão | 
insert(lista.begin(), "pão")
Insere "pão" antes da posição marcada: no começo. Todos os outros andam uma casa para a frente.
insert(lista.begin() + 2, "leite")
Insere antes do índice 2: o "leite" vira o novo índice 2.
erase(lista.begin() + 1)
Apaga o elemento de índice 1 (o "arroz", naquele momento). Os seguintes andam uma casa para trás.
erase(lista.end() - 1)
Apaga o último (o mesmo que pop_back()).

A função mostrar existe só para não repetir o laço cinco vezes; const std::vector<std::string>& quer dizer "leia este vetor sem copiá-lo" (veja Funções e Por Valor × Por Referência). Os iteradores têm aula própria: Iteradores.

Inserir e apagar no meio dá trabalho ao computador

Para abrir espaço no começo de um vetor de 1 milhão de itens, o computador precisa mover 1 milhão de itens uma casa para a frente. No fim (push_back/pop_back), não há nada para mover. Por isso o vetor é ótimo para acrescentar e tirar do fim, e menos bom para mexer no começo. Se o seu programa vive inserindo no começo, existem outros containers (list e deque).


7. Por dentro: capacity e reserve

Como o vetor "cresce"? Ele reserva um bloco de memória com espaço para alguns elementos. Quando o bloco enche, ele pede um bloco maior (no g++, o dobro), copia tudo para lá e libera o antigo. .size() diz quantos elementos existem; .capacity() ("capacidade") diz para quantos há espaço reservado:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v;
    for (int i = 1; i <= 10; i++) {
        v.push_back(i);
        std::cout << "size " << v.size() << ", capacity " << v.capacity() << '\n';
    }

    std::vector<int> w;
    w.reserve(100);
    std::cout << "Depois do reserve(100): size " << w.size()
              << ", capacity " << w.capacity() << '\n';
}
$ g++ -std=c++23 -Wall -Wextra capacidade.cpp -o capacidade
$ ./capacidade
size 1, capacity 1
size 2, capacity 2
size 3, capacity 4
size 4, capacity 4
size 5, capacity 8
size 6, capacity 8
size 7, capacity 8
size 8, capacity 8
size 9, capacity 16
size 10, capacity 16
Depois do reserve(100): size 0, capacity 100

A capacidade pulou 1 → 2 → 4 → 8 → 16: cada pulo é uma "mudança de casa". Como os pulos dobram, eles ficam raros conforme o vetor cresce, e o push_back continua rápido em média. Se você já sabe que vão vir uns 100 elementos, .reserve(100) reserva o espaço de uma vez: o tamanho continua 0, mas os próximos 100 push_back não precisam mudar de casa. É só uma otimização; programas pequenos não precisam dela. (Os números exatos da capacidade dependem da biblioteca; estes são os do g++.)


O container padrão

SituaçãoUse
Uma lista qualquer, de tamanho que muda (o caso comum)std::vector
Quantidade fixa, conhecida ao escrever o programastd::array
Uma tabela de linhas e colunas de tamanho variávelstd::vector<std::vector<T>> (veja Arrays Multidimensionais)
Achar um valor por uma chave (nome → telefone)std::map
Muitas inserções no começo e no fimstd::deque

Na dúvida, comece com std::vector: ele é simples, rápido e é o que a maioria dos programas C++ usa. A lista completa de funções do vetor (e dos outros containers) está em Referência: Containers, e o assunto continua em O que é a STL e em Algoritmos, onde você vai ordenar e procurar em vetores com uma linha.


Quando dá errado

"vector::_M_range_check: __n (which is 5) >= this->size() (which is 3)"

O que aconteceu: um .at(5) num vetor de 3 elementos lançou std::out_of_range, e o programa foi encerrado (código 134).

Como consertar: confira de onde vem o índice; índices válidos vão de 0 a size() − 1. Se o índice pode estar errado, teste antes (if (i < v.size())) ou trate a exceção.

"Assertion '__n < this->size()' failed" (escrever num vetor vazio com [ ])
#include <iostream>
#include <vector>

int main() {
    std::vector<int> notas;
    notas[0] = 7;
    std::cout << notas[0] << '\n';
}
$ g++ -std=c++23 -Wall -Wextra colchete.cpp -o colchete
$ ./colchete
/usr/include/c++/15/bits/stl_vector.h:1263: constexpr std::vector< <template-parameter-1-1>, <template-parameter-1-2> >::reference std::vector< <template-parameter-1-1>, <template-parameter-1-2> >::operator[](size_type) [with _Tp = int; _Alloc = std::allocator<int>; reference = int&; size_type = long unsigned int]: Assertion '__n < this->size()' failed.
Abortado                   (imagem do núcleo gravada) ./colchete
$ echo $?
134

O que significa: no meio do texto comprido, o que importa é o fim: operator[] (o [ ]) e Assertion '__n < this->size()' failed, "a conferência de que o índice é menor que o tamanho falhou". O vetor está vazio; não existe posição 0. Essa conferência é do g++ 15 sem otimização; com -O2, o programa seguiria escrevendo em memória que não é dele.

Como consertar: para acrescentar, notas.push_back(7);. Se queria um vetor com posições prontas, crie com tamanho: std::vector<int> notas(5);.

"Assertion '!this->empty()' failed" (pop_back num vetor vazio)
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v;
    v.pop_back();
    std::cout << "Fim\n";
}
$ g++ -std=c++23 -Wall -Wextra popvazio.cpp -o popvazio
$ ./popvazio
/usr/include/c++/15/bits/stl_vector.h:1459: constexpr void std::vector< <template-parameter-1-1>, <template-parameter-1-2> >::pop_back() [with _Tp = int; _Alloc = std::allocator<int>]: Assertion '!this->empty()' failed.
Abortado                   (imagem do núcleo gravada) ./popvazio
$ echo $?
134

O que significa: "a conferência de que o vetor não está vazio falhou", dentro do pop_back(). Não há o que tirar. O mesmo vale para front() e back() num vetor vazio. Com -O2, é comportamento indefinido sem aviso.

Como consertar: if (!v.empty()) { v.pop_back(); }.

"no matching function for call to ‘push_back(const char [5])’"
#include <vector>

int main() {
    std::vector<int> notas;
    notas.push_back("sete");
}
$ g++ -std=c++23 -Wall -Wextra tipo.cpp -o tipo
tipo.cpp: In function ‘int main()’:
tipo.cpp:5:20: error: no matching function for call to ‘push_back(const char [5])’
    5 |     notas.push_back("sete");
      |     ~~~~~~~~~~~~~~~^~~~~~~~
tipo.cpp:5:20: note: there are 2 candidates     « (as notas sobre os 2 candidatos foram cortadas)
tipo.cpp:5:21: error: invalid conversion from ‘const char*’ to ‘std::vector<int>::value_type’ {aka ‘int’} [-fpermissive]
    5 |     notas.push_back("sete");
      |                     ^~~~~~
      |                     |
      |                     const char*

O que significa: "nenhuma função push_back aceita um texto". O vetor é de int (value_type, o tipo dos elementos, é int), e "sete" é um texto. O compilador lista as versões de push_back que existem e explica por que nenhuma serve: não dá para converter texto em int.

Como consertar: acrescente um valor do tipo certo (notas.push_back(7);) ou, se a lista é de textos, declare std::vector<std::string>.

"comparison of integer expressions of different signedness"

O que aconteceu: um int comparado com .size(), que é sem sinal (seção 5).

Como consertar: std::size_t i no laço, ou for de intervalo.

O vetor tem 1 elemento em vez de 5 (ou o contrário)

O que aconteceu: std::vector<int> v{5}; cria um elemento que vale 5; std::vector<int> v(5); cria 5 zeros.

Como consertar: chaves para listar valores, parênteses para dizer quantos.

Acrescentar ou apagar enquanto percorre

O que acontece: fazer push_back, insert ou erase no mesmo vetor dentro de um for de intervalo que o percorre é comportamento indefinido: se o vetor "mudar de casa" (seção 7), o laço continua olhando a casa antiga.

Como evitar: junte as mudanças num segundo vetor e aplique depois do laço. Formas seguras de apagar vários itens estão em Algoritmos.


Referência

FormaO que faz
std::vector<T> v;Vetor vazio (#include <vector>).
std::vector<T> v = {a, b, c};Com valores.
std::vector<T> v(n); / v(n, x)n elementos zerados / valendo x.
v.push_back(x)Acrescenta x no fim.
v.pop_back()Remove o último (vetor não pode estar vazio).
v.size()Quantos elementos há (sem sinal).
v.empty()true se não há elementos.
v.clear()Remove todos.
v[i] / v.at(i)Elemento i: sem conferência garantida / conferido (std::out_of_range).
v.front() / v.back()Primeiro / último.
v.insert(v.begin() + i, x)Insere x antes do índice i.
v.erase(v.begin() + i)Apaga o elemento de índice i.
v.capacity() / v.reserve(n)Espaço reservado / reservar espaço para n.
b = a;, a == bCopia / compara elemento por elemento (como no std::array).

Referência completa: Containers.


Exercícios

1. Acrescentar

Complete para acrescentar 42 no fim de v:

v.;

2. Está vazio?

Complete o teste para mostrar a mensagem só se o carrinho estiver vazio:

if (carrinho.) { std::cout << "Nada no carrinho\n"; }

3. Apagar o primeiro

Complete para apagar o primeiro elemento de lista:

lista.erase();

4. Quanto mede?

Depois de std::vector<int> v = {1, 2, 3}; v.push_back(4); v.pop_back(); v.pop_back();, quanto vale v.size()?

5. O que std::vector<int> v(3); cria?

6. Num vetor vazio, v[0] = 5;…

7. Para uma lista cujo tamanho só se sabe quando o programa roda, o container recomendado é…

Missão final: riscar um item da lista

Missão: o "sabão" entrou por engano na lista de compras, na posição de índice 1. Apague-o com erase e lista.begin(). A lista deve ficar arroz, feijão, café, nessa ordem.

#include <iostream>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> lista = {"arroz", "sabão", "feijão", "café"};
    // apague o sabão aqui

    for (const auto& item : lista) {
        std::cout << "- " << item << '\n';
    }
}
#include <iostream>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> lista = {"arroz", "sabão", "feijão", "café"};
    lista.erase(lista.begin() + 1);
    for (const auto& item : lista) {
        std::cout << "- " << item << '\n';
    }
}

Resumo

  • std::vector<T> é uma lista que cresce e diminui durante a execução; é o container padrão do C++.
  • Crie vazio, com {valores}, ou com (quantidade); chaves e parênteses são diferentes.
  • push_back acrescenta no fim; pop_back tira do fim; insert e erase mexem no meio, com begin() + i.
  • size() conta, empty() pergunta se está vazio, clear() esvazia.
  • [i] não tem conferência garantida; at(i) lança std::out_of_range. [i] nunca acrescenta elementos.
  • Percorra com for (const auto& x : v); com índice, use std::size_t para evitar o aviso de sinais.
  • capacity é o espaço reservado; reserve(n) reserva antes. Referência completa: Containers.