std::vector em C++: Tamanho, Capacidade e Crescimento Seguro
Entenda como std::vector cresce em C++: a diferença entre tamanho e capacidade, quando usar reserve, por que resize é diferente e como evitar referências inválidas.

Nesta página
std::vector em C++: Tamanho, Capacidade e Crescimento Seguro
Você tem uma lista de notas, mas não sabe quantas haverá. Um array embutido de C++ tem um número fixo de elementos; std::vector pode crescer quando você acrescenta notas. Essa flexibilidade traz perguntas importantes: qual é a diferença entre tamanho e capacidade? reserve cria elementos? O que acontece com uma referência quando o vetor muda de área na memória?
Este guia continua Arrays em C++. Aquele artigo compara arrays embutidos, std::array e std::vector; aqui vamos trabalhar com o vetor em mais detalhes. Todas as saídas de exemplo foram conferidas com GCC 14.1 no Windows, usando C++20.

Adicione e leia elementos
Salve como main.cpp, compile com g++ -std=c++20 -Wall -Wextra main.cpp -o main e execute o programa gerado:
1#include <cstddef>
2#include <iostream>
3#include <vector>
4
5int main() {
6 std::vector<int> notas{90, 85, 77};
7 notas.push_back(92);
8
9 std::cout << "quantidade: " << notas.size() << '\n';
10 std::cout << "primeira: " << notas.front() << '\n';
11 std::cout << "ultima: " << notas.back() << '\n';
12
13 for (std::size_t i = 0; i < notas.size(); ++i) {
14 if (i != 0) std::cout << ' ';
15 std::cout << notas[i];
16 }
17 std::cout << '\n';
18}1quantidade: 4
2primeira: 90
3ultima: 92
490 85 77 92push_back acrescenta um elemento ao final. size() conta os elementos que realmente existem. Os índices começam em zero: notas[3] é a quarta nota. Só chame front() ou back() quando o vetor não estiver vazio.
Tamanho não é capacidade
Capacidade é a quantidade de elementos que cabem na área atual do vetor antes de ele precisar reservar mais memória. Ela é pelo menos igual ao tamanho, mas a capacidade extra não contém elementos prontos para uso.
1tamanho = 3, capacidade = 5
2
3índice: 0 1 2 3 4
4espaço: [ 10 ][ 20 ][ 30 ][ -- ][ -- ]
5 elementos atuais | espaço extraAs caixas nos índices 3 e 4 representam apenas espaço disponível. Elas ficam fora do tamanho do vetor: valores[3] é inválido até que um quarto elemento seja adicionado. Use push_back ou resize para criar esse elemento.
Meça o crescimento sem presumir uma regra fixa
Execute este programa para observar as mudanças de tamanho e capacidade:
1#include <iostream>
2#include <vector>
3
4int main() {
5 std::vector<int> valores;
6 valores.reserve(5);
7 std::cout << "inicio: tamanho=" << valores.size()
8 << " capacidade=" << valores.capacity() << '\n';
9
10 for (int valor = 1; valor <= 6; ++valor) {
11 const auto anterior = valores.capacity();
12 valores.push_back(valor * 10);
13 if (valores.capacity() != anterior) {
14 std::cout << "memoria cresceu no tamanho " << valores.size()
15 << ": capacidade=" << valores.capacity() << '\n';
16 }
17 }
18
19 std::cout << "fim: tamanho=" << valores.size()
20 << " capacidade=" << valores.capacity() << '\n';
21}Na instalação com GCC 14.1 usada neste artigo, a saída foi:
inicio: tamanho=0 capacidade=5
memoria cresceu no tamanho 6: capacidade=10
fim: tamanho=6 capacidade=10O padrão C++ garante que reserve(5) fornece capacidade de pelo menos cinco e não altera o tamanho. Ele não obriga todas as implementações a escolher capacidade 5 nem a crescer até 10. A observação útil é que as primeiras cinco adições cabem sem uma nova alocação; quando a capacidade era cinco, a sexta exige mais espaço. Esta é uma medição do crescimento em uma implementação, não um teste de velocidade.

O desenho começa com capacidade exatamente cinco; por isso, a terceira etapa exige realocação. Implementações reais podem escolher uma capacidade maior, então a nova aparece apenas como “pelo menos seis”. Guarde um índice e consulte o elemento novamente após o crescimento, em vez de usar uma referência para a área antiga.
reserve e resize têm funções diferentes
É fácil confundir os nomes:
| Chamada em um vetor vazio | Tamanho depois | O que mudou? |
|---|---|---|
valores.reserve(5) | 0 | Há espaço para pelo menos cinco elementos; nenhum foi criado |
valores.resize(5) | 5 | Cinco elementos int agora existem, inicializados com zero |

As cinco caixas mostram espaço possível à esquerda e elementos existentes à direita. As duas chamadas podem reservar memória, mas só resize torna esses índices válidos. Se um push_back posterior passar da capacidade e causar realocação, obtenha de novo ponteiros, referências ou iteradores a partir do vetor.
Use reserve quando pretende acrescentar aproximadamente essa quantidade de elementos e quer evitar algumas realocações. Use resize quando precisa dos elementos agora, talvez para preenchê-los por índice. Chamar reserve a cada push_back geralmente atrapalha: deixe o vetor cuidar do crescimento, a menos que tenha uma estimativa útil.
1std::vector<int> valores;
2valores.reserve(5); // tamanho ainda é 0: valores[0] é inválido
3valores.push_back(42); // tamanho agora é 1: valores[0] existe
4valores.resize(3); // tamanho agora é 3: valores[1] e valores[2] são 0O crescimento pode invalidar referências e iteradores
O vetor guarda seus elementos lado a lado na memória. Quando precisa de uma área maior, ele pode mover ou copiar os elementos. Se houver realocação, ponteiros, referências e iteradores para os elementos ficam inválidos. Ler por meio de um deles depois disso tem comportamento indefinido. Não tente provar o problema acessando um ponteiro pendurado: um programa que parece funcionar ainda pode estar errado.
Guarde um índice se precisar reencontrar um elemento depois de acrescentar valores e então acesse-o novamente:
1#include <iostream>
2#include <vector>
3
4int main() {
5 std::vector<int> valores{10};
6 const std::size_t indiceInicial = 0;
7
8 for (int valor = 20; valor <= 100; valor += 10) {
9 valores.push_back(valor);
10 }
11
12 std::cout << "primeiro: " << valores.at(indiceInicial) << '\n';
13 std::cout << "quantidade: " << valores.size() << '\n';
14}primeiro: 10
quantidade: 10at() verifica o índice e lança std::out_of_range se ele estiver fora do tamanho atual; operator[] não faz essa verificação. O índice ainda precisa apontar para o elemento desejado no vetor atual: inserir ou remover antes dele pode mudar sua posição. reserve pode reduzir realocações, mas não garante que toda alteração futura manterá referências válidas.
Escolha a ferramenta certa
- Use
std::vectorquando a quantidade de elementos pode mudar, especialmente ao acrescentar no final. - Use
std::arrayquando o tamanho é fixo e conhecido na compilação. - Use um array embutido quando precisa conversar com código que exige esse formato ou está aprendendo como arrays e ponteiros funcionam.
Ler elementos de std::vector não é inerentemente lento: assim como em um array, eles ficam lado a lado. O crescimento pode exigir alocação e movimentação, então não confunda o custo de crescer com o custo de acessar. Para a comparação completa e as medições, volte a Arrays em C++.
Experimente
Mude o programa de crescimento para chamar reserve(8) e acrescentar seis valores. Preveja o tamanho final e a capacidade mínima antes de rodar. Depois troque reserve(8) por resize(8) e preveja os oito primeiros valores antes do laço.
Resposta: Depois de reserve(8) e seis adições, o tamanho é 6 e a capacidade é pelo menos 8. Depois de resize(8), o tamanho já é 8 e seus oito elementos int são zero; acrescentar mais seis eleva o tamanho a 14. A capacidade exata depende da implementação.
Para saber mais
Leia a seguir

Como Tratar Erros em Go: Um Guia Prático
Aprenda a tratar erros em Go com exemplos executáveis: retorne um erro, confira o resultado, acrescente contexto com %w e reconheça um arquivo ausente com errors.Is.

O Que É a Linguagem de Programação Rust? Um Guia Simples da Linguagem Rápida Que Não Deixa Você Travar
Um guia simples e sem jargões sobre o Rust, a linguagem tão rápida quanto o C que impede famílias inteiras de erros de memória antes de o programa rodar. Entenda a ideia de posse, por que gigantes da tecnologia o adotam e onde ele é usado.

O Que É a Linguagem de Programação Go? Um Guia Simples da Linguagem da Nuvem
Um guia simples e sem jargões sobre o Go, a linguagem simples e rápida que o Google criou para a nuvem. Entenda o que são goroutines, por que um programa Go é um arquivo só e como o Go passou a mover o Docker, o Kubernetes e boa parte da internet.
Comentários
Seja gentil e específico. Comentários com links de contas novas aparecem depois da análise de um moderador.