C++: Fundamentos de Contêineres STL
Nas lições anteriores, aprendemos sobre templates — fazendo com que funções e classes suportem qualquer tipo.
Mas em projetos reais, você não precisa escrever seus próprios contêineres (como arrays dinâmicos ou listas encadeadas).
A Biblioteca de Templates Padrão (STL) do C++ já fornece contêineres prontos — basta usá-los diretamente!
1. O que é a STL?
A STL (Standard Template Library) é parte da biblioteca padrão do C++, contendo:
| Componente | Finalidade | Exemplos |
|---|---|---|
| Contêineres | Armazenar dados | std::vector, std::array |
| Iteradores | Percorrer contêineres | begin(), end() |
| Algoritmos | Manipular dados | std::sort, std::find |
| Objetos de função | Lógica de comparação personalizada | std::less, std::greater |
💡 Ponto-chave: A STL é implementada com templates, então suporta qualquer tipo.
2. vector (Mais Usado)
(1) 2.1 O que é vector?
std::vector é um array dinâmico — seu tamanho pode crescer automaticamente.
| Comparação | Array Comum | vector |
|---|---|---|
| Tamanho | Fixo | Dinâmico |
| Memória | Stack ou heap | Heap |
| Acessar elementos | arr[i] |
vec[i] ou vec.at(i) |
| Recomendação | ⭐⭐ | ⭐⭐⭐⭐⭐ |
▶ Exemplo 1: Uso Básico de vector (Dificuldade ⭐)
#include <iostream>
#include <vector>
int main() {
// Create a vector storing ints
std::vector<int> vec = {1, 2, 3, 4, 5};
// Access elements
std::cout << "First element: " << vec[0] << std::endl;
std::cout << "Second element: " << vec.at(1) << std::endl;
// Modify elements
vec[0] = 100;
// Get size
std::cout << "Size: " << vec.size() << std::endl;
return 0;
}
Saída:
First element: 1
Second element: 2
Size: 5
💡 Dica: vec.at(i) realiza verificação de limites e lança uma exceção se fora dos limites; vec[i] não verifica, e é mais eficiente.
(2) 2.2 Adicionando e Removendo Elementos
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec;
// Add elements to the end
vec.push_back(10);
vec.push_back(20);
vec.push_back(30);
// Remove the last element
vec.pop_back();
// Insert element at a specified position
vec.insert(vec.begin() + 1, 15); // Insert 15 at the second position
// Remove element at a specified position
vec.erase(vec.begin() + 1); // Remove the second element
return 0;
}
(3) 2.3 Percorrendo um vector
Método 1: Percorrimento por subscrito (mais comum)
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (size_t i = 0; i < vec.size(); i++) {
std::cout << vec[i] << " ";
}
std::cout << std::endl;
return 0;
}
Método 2: for baseado em intervalo (C++11, recomendado)
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (int x : vec) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
Método 3: Iteradores (abordado mais tarde)
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
💡 Recomendação: Prefira for baseado em intervalo (C++11) — é o mais conciso.
3. array (Array de Tamanho Fixo)
(1) 3.1 O que é array?
std::array é um array de tamanho fixo, mas mais seguro que um array comum.
| Comparação | Array Comum | array |
|---|---|---|
| Tamanho | Você precisa saber | Obtenha com size() |
| Decompõe para ponteiro? | Sim | Não |
| Recomendação | ⭐⭐ | ⭐⭐⭐⭐ |
▶ Exemplo 2: Uso Básico de array (Dificuldade ⭐)
#include <iostream>
#include <array>
int main() {
// Create an array storing 5 ints
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// Access elements
std::cout << "First element: " << arr[0] << std::endl;
// Get size
std::cout << "Size: " << arr.size() << std::endl;
return 0;
}
Saída:
First element: 1
Size: 5
💡 Ponto-chave: O tamanho de std::array é determinado em tempo de compilação e não pode ser alterado.
4. deque (Fila de Duas Pontas)
(1) 4.1 O que é deque?
std::deque é uma fila de duas pontas — você pode adicionar/remover elementos rapidamente em ambas as extremidades.
| Operação | vector | deque |
|---|---|---|
| Adicionar no final | O(1) | O(1) |
| Adicionar no início | O(n) | O(1) |
| Acesso aleatório | O(1) | O(1) |
▶ Exemplo 3: Uso Básico de deque (Dificuldade ⭐⭐)
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq;
// Add at end
dq.push_back(10);
dq.push_back(20);
// Add at front
dq.push_front(5);
dq.push_front(1);
// dq is now: 1, 5, 10, 20
for (int x : dq) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
Saída:
1 5 10 20
5. list (Lista Duplamente Encadeada)
(1) 5.1 O que é list?
std::list é uma lista duplamente encadeada — cada elemento armazena os endereços do elemento anterior e do próximo.
| Comparação | vector | list |
|---|---|---|
| Acesso aleatório | O(1) | ❌ Não suportado |
| Inserir/deletar no meio | O(n) | O(1) |
| Uso de memória | Pequeno | Grande (dois ponteiros extras por elemento) |
▶ Exemplo 4: Uso Básico de list (Dificuldade ⭐⭐)
#include <iostream>
#include <list>
int main() {
std::list<int> lst = {1, 2, 3, 4, 5};
// Add at front
lst.push_front(0);
// Add at end
lst.push_back(6);
// Remove elements with value 3
lst.remove(3);
for (int x : lst) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
Saída:
0 1 2 4 5 6
6. forward_list (Lista Simplesmente Encadeada)
(1) 6.1 O que é forward_list?
std::forward_list é uma lista simplesmente encadeada — cada elemento armazena apenas o endereço do próximo elemento.
| Comparação | list | forward_list |
|---|---|---|
| Uso de memória | Maior | Menor |
| Pode percorrer para trás? | ✅ | ❌ |
| Recomendação | ⭐⭐⭐⭐ | ⭐⭐ (para cenários específicos) |
💡 Conselho: A menos que você tenha certeza de que só precisa percorrer para frente, use std::list.
7. Guia de Seleção de Contêineres
| Cenário | Contêiner Recomendado |
|---|---|
| Precisa de tamanho dinâmico | std::vector |
| Tamanho fixo, deseja compatibilidade com C | std::array |
| Precisa de adição/remoção rápida no início | std::deque |
| Inserção/remoção frequente no meio | std::list |
| Só precisa percorrer para frente e economizar memória | std::forward_list |
💡 Regra de ouro: Prefira std::vector a menos que tenha um motivo claro para usar outro.
8. Prática: Array Dinâmico com vector (Dificuldade ⭐⭐)
#include <iostream>
#include <vector>
#include <string>
struct Student {
std::string name;
int age;
double score;
};
int main() {
std::vector<Student> students;
// Add students
students.push_back({"Alice", 20, 92.5});
students.push_back({"Bob", 21, 88.0});
students.push_back({"Charlie", 19, 95.0});
// Traverse and output
for (const auto& s : students) {
std::cout << "Name: " << s.name
<< ", Age: " << s.age
<< ", Score: " << s.score << std::endl;
}
return 0;
}
❓ Perguntas Frequentes
P: Qual a diferença entre vector e array? R:> -
vectortem tamanho dinâmico, armazenado no heap > -arraytem tamanho fixo, armazenado na stack > > Conselho de seleção: Se o tamanho é incerto, usevector; se o tamanho é fixo e pequeno, usearray. P: Por que o for baseado em intervalo é recomendado para percorrer vector? R: Porque é mais conciso e menos propenso a erros nas condições do loop. > > // Traditional for (easy to get conditions wrong) > for (size_t i = 0; i < vec.size(); i++) { ... } > > // Range-based for (concise, less error-prone) > for (int x : vec) { ... } > P: Qual é mais rápido — list ou vector? R: Depende do cenário: > - Se você precisa de acesso aleatório (vec[100]),vectoré mais rápido > - Se você precisa de inserção/remoção frequente no meio,listé mais rápido
P: Qual é a coisa mais importante sobre contêineres STL? R: Entenda os conceitos centrais primeiro, depois reforce-os através de exemplos práticos.
📖 Resumo
- Contêineres STL são estruturas de dados prontas fornecidas pela biblioteca padrão do C++
- vector é o mais usado (array dinâmico)
- array é para cenários de tamanho fixo
- deque suporta operações rápidas em ambas as extremidades
- list é uma lista duplamente encadeada, boa para inserção/remoção frequente
- Prefira vector a menos que tenha um motivo claro para usar outro
📝 Exercícios
-
Básico (Dificuldade ⭐): Use
std::vector<int>para armazenar 5 inteiros, percorra e exiba-os. -
Intermediário (Dificuldade ⭐⭐): Use
std::vector<std::string>para armazenar 3 strings, permita que o usuário as insira, depois exiba. -
Desafio (Dificuldade ⭐⭐⭐): Use
std::dequepara implementar "detecção de palíndromo": -
Compare de ambas as extremidades em direção ao meio; se todos os caracteres correspondentes forem iguais, é um palíndromo
-
Exemplo:
"racecar"é um palíndromo,"hello"não é
- Categorias de contêineres STL: sequenciais (vector/list/deque) e associativos (set/map)
- vector array dinâmico: adição/remoção rápida no final, suporta acesso aleatório
- list lista duplamente encadeada: inserção rápida em qualquer posição
- map contêiner chave-valor: ordenado por chave, busca O(log n)
- set contêiner de conjunto: elementos únicos, automaticamente ordenados
9. 🚀 Próximos Passos
Agora que você aprendeu os fundamentos de contêineres STL, a seguir vamos estudar Algoritmos STL (lição 35) — usando os algoritmos fornecidos pela biblioteca padrão para manipular contêineres, para que você não precise escrever sua própria ordenação, busca...