C++ Containers

Os containers (recipientes) da biblioteca padrão guardam coleções de valores: uma lista que cresce, um dicionário, um conjunto sem repetidos, uma pilha, uma fila. Esta página reúne os onze mais usados: qual cabeçalho incluir, quando usar cada um, quanto custa cada operação e os métodos principais, com exemplo e o resultado real (83 exemplos compilados e executados com o g++ 15.2, -std=c++23). Para aprender com calma, comece por O que é a STL e std::vector.

Como ler as tabelas
  • Cada container tem uma declaração inicial (no título da tabela). Cada exemplo começa dela, do zero.
  • Nos métodos que mudam o container, o resultado mostra como ele ficou, no formato do std::println("{}", c) C++23: [10, 20] para sequências, {10, 20} para conjuntos, {"ana": 9} para mapas.
  • Nos unordered_…, a ordem mostrada é a que saiu no laboratório; em outro compilador (até no editor do guia) pode ser outra.
  • Posições começam em 0. begin() é um iterador para o primeiro elemento; end(), para "depois do último".

Atalhos: Visão geral · Complexidade · Qual usar · vector · array · deque · list · map · unordered_map · set · unordered_set · stack · queue · priority_queue

Exemplo rápido

#include <map>
#include <print>
#include <set>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> compras{"pão", "leite", "café", "leite"};
    compras.push_back("ovos");
    std::println("lista: {} ({} itens)", compras, compras.size());

    std::set<std::string> semRepetir(compras.begin(), compras.end());
    std::println("sem repetir: {}", semRepetir);

    std::map<std::string, int> quantos;
    for (const auto& item : compras) {
        ++quantos[item];
    }
    std::println("quantos: {}", quantos);
}
$ g++ -std=c++23 -Wall -Wextra colecoes.cpp -o colecoes
$ ./colecoes
lista: ["pão", "leite", "café", "leite", "ovos"] (5 itens)
sem repetir: {"café", "leite", "ovos", "pão"}
quantos: {"café": 1, "leite": 2, "ovos": 1, "pão": 1}

O mesmo item, três containers: o vector guarda tudo na ordem em que entrou (com o "leite" repetido), o set guarda cada um uma vez, em ordem, e o map associa cada item a um número. O set e o map ordenam comparando bytes, e por isso "pão" (com ã) veio depois de "ovos".

Experimente: qualquer exemplo das tabelas cabe aqui. Troque os itens, acrescente um std::deque ou uma std::stack e mostre com std::println.

#include <map>
#include <print>
#include <set>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> compras{"pão", "leite", "café", "leite"};
    compras.push_back("ovos");
    std::println("lista: {} ({} itens)", compras, compras.size());

    std::set<std::string> semRepetir(compras.begin(), compras.end());
    std::println("sem repetir: {}", semRepetir);

    std::map<std::string, int> quantos;
    for (const auto& item : compras) {
        ++quantos[item];
    }
    std::println("quantos: {}", quantos);
}

Visão geral

ContainerCabeçalhoO que éPor posição [i]OrdemRepetidosAula
std::vector<T><vector>Lista que cresce, elementos lado a lado na memória.sima de inserçãosimvector
std::array<T, N><array>Array de tamanho fixo, conhecido na compilação.sima de inserçãosimstd::array
std::deque<T><deque>"Fila de duas pontas": cresce rápido nos dois lados.sima de inserçãosimlist e deque
std::list<T><list>Lista ligada: cada elemento aponta para o vizinho.nãoa de inserçãosimlist e deque
std::map<K, V><map>Dicionário chave → valor, ordenado pela chave.pela chavecrescente de chavechaves não (multimap sim)std::map
std::unordered_map<K, V><unordered_map>Dicionário com tabela de espalhamento (hash).pela chavenenhumachaves nãounordered_map
std::set<T><set>Conjunto ordenado, sem repetidos.nãocrescentenão (multiset sim)std::set
std::unordered_set<T><unordered_set>Conjunto com hash, sem repetidos.nãonenhumanãostd::set
std::stack<T><stack>Pilha: o último que entra é o primeiro que sai.não (só o topo)—simstack e queue
std::queue<T><queue>Fila: o primeiro que entra é o primeiro que sai.não (só as pontas)—simstack e queue
std::priority_queue<T><queue>Fila em que sai sempre o maior (ou o menor).não (só o topo)—simstack e queue

stack, queue e priority_queue são adaptadores: por dentro usam um deque (ou um vector, no caso da priority_queue) e só mostram as operações permitidas. Por isso não têm begin()/end() e não dá para percorrê-los com for. Desde o C++23 existem também std::flat_map e std::flat_set (<flat_map>, <flat_set>), mapas e conjuntos ordenados guardados num vetor; o g++ 15 já os tem.

Complexidade

Quanto o tempo de cada operação cresce quando o container cresce (n = número de elementos). O(1): constante, não depende do tamanho. O(log n): cresce muito devagar (um milhão de elementos ≈ 20 passos). O(n): proporcional ao tamanho. "Amortizado": quase sempre O(1); de vez em quando, uma operação cara (o vector realocando) que, dividida por todas, some.

ContainerAcesso [i]Inserir/remover no fimNo começoNo meioBuscar um valor
vectorO(1)O(1) amortizadoO(n)O(n)O(n); O(log n) se ordenado (binary_search)
arrayO(1)tamanho fixo: não insere nem removeO(n)
dequeO(1)O(1)O(1)O(n)O(n)
list—O(1)O(1)O(1) com o iterador em mãosO(n)
map, setpela chave: O(log n)inserir e remover: O(log n), sempre no lugar certo da ordemO(log n)
unordered_map, unordered_setpela chave: O(1) em médiainserir e remover: O(1) em média (O(n) no pior caso)O(1) em média
stack, queue—push, pop, top/front: O(1)—
priority_queue—top: O(1); push e pop: O(log n)—

Qual usar

Você precisa de…Use
Uma lista de coisas, na ordem, que cresce. (Na dúvida, é esta.)std::vector
Um número fixo de elementos, sabido ao escrever o código (os 12 meses, as 3 notas).std::array
Achar um valor pela chave (nome → telefone) e listar em ordem.std::map
Achar pela chave o mais rápido possível, sem ligar para a ordem.std::unordered_map
Saber se algo já apareceu; tirar repetidos.std::set (ordenado) ou std::unordered_set
Desfazer (Ctrl+Z), conferir parênteses, voltar pelo caminho.std::stack
Atender na ordem de chegada (fila do caixa, tarefas).std::queue
Atender sempre o mais urgente primeiro.std::priority_queue
Inserir e tirar muito nas duas pontas, com acesso por posição.std::deque
Inserir e remover no meio sem invalidar os outros iteradores.std::list (raramente: na prática o vector costuma ser mais rápido até nisso, por causa da memória contígua)

std::vector · #include <vector> · Aula

Declaração inicial dos exemplos: std::vector<int> v{10, 20, 30};

MétodoO que fazExemploResultado
size() · empty()Quantos elementos; se está vazio.v.size()
v.empty()
3
false
operator[](i) · at(i)Elemento na posição i. [] não confere (fora do tamanho é comportamento indefinido); at confere e lança std::out_of_range.v[1]
v.at(5)
20
lança std::out_of_range
what(): vector::_M_range_check: __n (which is 5) >= this->size() (which is 3)
front() · back()Primeiro e último.v.front()
v.back()
10
30
push_back(x) · emplace_back(args…) C++11Acrescenta no fim. emplace_back constrói o elemento ali mesmo, com os argumentos do construtor.v.push_back(40)
v.emplace_back(50)
v = [10, 20, 30, 40]
v = [10, 20, 30, 50]
pop_back()Tira o último.v.pop_back()v = [10, 20]
insert(posição, x)Insere antes da posição (um iterador). Empurra todos os seguintes: O(n).v.insert(v.begin() + 1, 15)
v.insert(v.begin(), 5)
v = [10, 15, 20, 30]
v = [5, 10, 20, 30]
erase(posição) · erase(início, fim)Remove um elemento ou um trecho [início, fim).v.erase(v.begin())
v.erase(v.begin(), v.begin() + 2)
v = [20, 30]
v = [30]
std::erase(v, x) · std::erase_if(v, f) C++20Remove todos os iguais a x (ou que satisfazem f). Devolve quantos removeu.std::erase(v, 20)
std::erase_if(v, [](int x) { return x > 15; })
v = [10, 30]
v = [10]
clear() · resize(n)Esvazia; muda o tamanho (novos elementos valem 0).v.clear()
v.resize(5)
v = []
v = [10, 20, 30, 0, 0]
reserve(n) · capacity()Reserva espaço; quanto cabe sem realocar.v.reserve(100); v.capacity()100
std::ranges::contains(v, x) C++23Tem x? Busca linear, O(n). De <algorithm>.std::ranges::contains(v, 20)true
std::ranges::sort(v) C++20Ordena (de <algorithm>); com std::greater{}, do maior para o menor.std::ranges::sort(v, std::greater{})v = [30, 20, 10]

std::array · #include <array> · Aula

Declaração inicial dos exemplos: std::array<int, 3> a{10, 20, 30};

MétodoO que fazExemploResultado
size() C++11O tamanho, fixo, faz parte do tipo (<int, 3>).a.size()3
operator[](i) · at(i) C++11Como no vector.a[2]
a.at(3)
30
lança std::out_of_range
what(): array::at: __n (which is 3) >= _Nm (which is 3)
front() · back() C++11Primeiro e último.a.back()30
fill(x) C++11Põe x em todas as posições.a.fill(0)a = [0, 0, 0]
==, < C++11Compara elemento a elemento.a == std::array<int, 3>{10, 20, 30}true

std::deque · #include <deque> · Aula

Declaração inicial dos exemplos: std::deque<int> d{10, 20, 30};

MétodoO que fazExemploResultado
push_front(x) · push_back(x)Acrescenta no começo ou no fim, ambos O(1).d.push_front(5)
d.push_back(40)
d = [5, 10, 20, 30]
d = [10, 20, 30, 40]
pop_front() · pop_back()Tira do começo ou do fim.d.pop_front()d = [20, 30]
operator[](i) · at(i)Acesso por posição, O(1).d[1]20
insert · erase · size · front · backComo no vector.d.insert(d.begin() + 1, 15)d = [10, 15, 20, 30]

std::list · #include <list> · Aula

Declaração inicial dos exemplos: std::list<int> l{30, 10, 20};

MétodoO que fazExemploResultado
push_front(x) · push_back(x) · pop_front() · pop_back()Pontas, O(1).l.push_front(5)
l.pop_back()
l = [5, 30, 10, 20]
l = [30, 10]
insert(posição, x)Insere em O(1) se você já tem o iterador. Não há []: para chegar à posição, ande com std::next (O(n)).l.insert(std::next(l.begin()), 15)l = [30, 15, 10, 20]
remove(x) · remove_if(f)Remove todos os iguais a x.l.remove(10)l = [30, 20]
sort() · reverse() · unique()Métodos próprios (o std::sort não funciona em list). unique tira repetidos vizinhos.l.sort()
l.reverse()
l = [10, 20, 30]
l = [20, 10, 30]
front() · back() · size()Primeiro, último, tamanho.l.front()
l.size()
30
3

std::map · #include <map> · Aula

Declaração inicial dos exemplos: std::map<std::string, int> m{{"bia", 7}, {"ana", 9}};

MétodoO que fazExemploResultado
operator[](chave)Acessa o valor da chave. Se a chave não existe, cria (com valor 0) e devolve.m["caio"] = 8
m["ana"] = 10
m["zé"]; m.size()
m = {"ana": 9, "bia": 7, "caio": 8}
m = {"ana": 10, "bia": 7}
3
at(chave) C++11Acessa sem criar: chave inexistente lança std::out_of_range.m.at("bia")
m.at("zé")
7
lança std::out_of_range
what(): map::at
contains(chave) C++20A chave existe?m.contains("ana")
m.contains("zé")
true
false
find(chave)Iterador para o par (->first = chave, ->second = valor), ou end() se não achar.m.find("bia")->second
m.find("zé") == m.end()
7
true
count(chave)0 ou 1 (no multimap, quantos).m.count("ana")1
insert({chave, valor})Insere só se a chave não existe. Devolve um par (iterador, inseriu?).m.insert({"ana", 0}).second
m.insert({"caio", 8})
false
m = {"ana": 9, "bia": 7, "caio": 8}
insert_or_assign(chave, valor) C++17Insere ou substitui.m.insert_or_assign("ana", 0)m = {"ana": 0, "bia": 7}
erase(chave)Remove pela chave; devolve quantos removeu.m.erase("ana")m = {"bia": 7}
for (auto& [chave, valor] : m) C++17Percorre em ordem crescente de chave.m.begin()->first"ana"

std::unordered_map · #include <unordered_map> · Aula

Declaração inicial dos exemplos: std::unordered_map<std::string, int> u{{"bia", 7}, {"ana", 9}};

MétodoO que fazExemploResultado
operator[] · at · contains · find · count · insert · erase C++11Os mesmos métodos do map, com o mesmo comportamento (o [] também cria). A diferença: busca O(1) em média e nenhuma ordem.u["caio"] = 8
u.at("ana")
u.contains("zé")
u = {"caio": 8, "ana": 9, "bia": 7}
9
false
bucket_count() · load_factor() C++11Quantas “gavetas” a tabela de espalhamento tem e quão cheias estão, em média.u.bucket_count()13

Repare que "caio" apareceu antes dos outros: num unordered_map a ordem é a das "gavetas" internas, e pode mudar quando a tabela cresce.

std::set · #include <set> · Aula

Declaração inicial dos exemplos: std::set<int> s{30, 10, 20};

MétodoO que fazExemploResultado
insert(x)Insere, se ainda não existe. Mantém em ordem. Devolve (iterador, inseriu?).s.insert(15)
s.insert(10).second
s = {10, 15, 20, 30}
false
contains(x) · count(x) C++20Existe?s.contains(20)
s.count(99)
true
0
erase(x)Remove.s.erase(10)s = {20, 30}
begin() · rbegin()O menor e o maior (ordenado).*s.begin()
*s.rbegin()
10
30
lower_bound(x) · upper_bound(x)Primeiro elemento ≥ x; primeiro > x.*s.lower_bound(15)
*s.upper_bound(20)
20
30

std::unordered_set · #include <unordered_set> · Aula

Declaração inicial dos exemplos: std::unordered_set<int> us{30, 10, 20};

MétodoO que fazExemploResultado
insert · contains · count · erase · size C++11Como o set, sem ordem, com busca O(1) em média.us.insert(15)
us.contains(10)
us.erase(30)
us = {15, 20, 10, 30}
true
us = {20, 10}

std::stack · #include <stack> · Aula

Declaração inicial dos exemplos: std::stack<int> p; p.push(10); p.push(20); p.push(30);

MétodoO que fazExemploResultado
push(x) · emplace(args…)Empilha no topo.p.push(40)p = [10, 20, 30, 40]
top()Olha o topo (o último que entrou), sem tirar.p.top()30
pop()Tira o topo. Não devolve nada: leia com top() antes.p.pop()p = [10, 20]
size() · empty()Tamanho; vazia?p.size()3

O println mostra a pilha de baixo para cima: o último número é o topo.

std::queue · #include <queue> · Aula

Declaração inicial dos exemplos: std::queue<int> f; f.push(10); f.push(20); f.push(30);

MétodoO que fazExemploResultado
push(x)Entra no fim da fila.f.push(40)f = [10, 20, 30, 40]
front() · back()O primeiro (o próximo a sair) e o último.f.front()
f.back()
10
30
pop()Tira o da frente (não devolve).f.pop()f = [20, 30]
size() · empty()Tamanho; vazia?f.empty()false

O println mostra a fila da frente para o fim: o primeiro número é o próximo a sair.

std::priority_queue · #include <queue> · Aula

Declaração inicial dos exemplos: std::priority_queue<int> pq; pq.push(20); pq.push(50); pq.push(10);

MétodoO que fazExemploResultado
top()O maior elemento (por padrão).pq.top()50
push(x) · pop()Insere; tira o maior. Ambos O(log n).pq.pop(); pq.top()
pq.push(30); pq.top()
20
50
priority_queue<T, vector<T>, greater<T>>Fila de prioridade do menor.std::priority_queue<int, std::vector<int>, std::greater<int>> menor; menor.push(20); menor.push(50); menor.push(10);
menor.top()

10

Só o topo é acessível. Para ver todos em ordem, tire um por um com top() e pop().

Iteradores invalidados

Depois de um push_back, insert ou erase num vector, iteradores, ponteiros e referências para os elementos podem ficar inválidos (o vetor pode ter mudado de lugar na memória). Usá-los é comportamento indefinido. Não guarde &v[0] ou v.begin() e depois acrescente elementos. map, set e list não têm esse problema (só o elemento apagado fica inválido).

Missão: a Ana lançou as notas da turma num vector. Mostre as notas diferentes, em ordem crescente, separadas por espaço, e quantas são. Dica: um container desta página faz as duas coisas sozinho, e ele pode ser criado direto do vetor: C diferentes(notas.begin(), notas.end());.

7 9 10
3 notas diferentes
#include <iostream>
#include <vector>

int main() {
    std::vector<int> notas{7, 9, 7, 10, 9, 7};
    // seu código aqui
}
#include <iostream>
#include <set>
#include <vector>

int main() {
    std::vector<int> notas{7, 9, 7, 10, 9, 7};
    std::set<int> diferentes(notas.begin(), notas.end());
    for (int n : diferentes) {
        std::cout << n << ' ';
    }
    std::cout << '\n' << diferentes.size() << " notas diferentes\n";
}