C++: Iteradores STL

Nas lições 34-35 aprendemos sobre contêineres e algoritmos STL.

Agora, vamos mergulhar na "cola" da STL — iteradores.

Compreender iteradores é a chave para verdadeiramente entender a filosofia de design da STL.


1. Visão Geral de Iteradores

(1) 1.1 O que são Iteradores?

Iteradores são um conceito central da STL que conectam contêineres e algoritmos.

Analogia do mundo real:


(2) 1.2 Operações Básicas de Iteradores

Todos os iteradores suportam as seguintes operações:

Operação Descrição Exemplo
*it Desreferenciar int x = *it;
it++ Avançar para o próximo elemento ++it;
it-- Voltar para o elemento anterior --it;
it1 == it2 Comparar igualdade if (it1 == it2)
it1 != it2 Comparar desigualdade while (it != end())


2. Categorias de Iteradores

(1) 2.1 Cinco Tipos de Iteradores

A STL define 5 tipos de iteradores, do mais fraco ao mais forte:

Tipo de Iterador Capacidades Contêiner Representativo
Iterador de entrada Somente leitura, unidirecional istream_iterator
Iterador de saída Somente escrita, unidirecional ostream_iterator
Iterador de avanço Leitura/escrita, unidirecional forward_list
Iterador bidirecional Leitura/escrita, bidirecional list, set, map
Iterador de acesso aleatório Leitura/escrita, acesso aleatório vector, deque, array

(2) 2.2 Comparação de Capacidades dos Iteradores

TEXT 📖 Somente leitura
Input iterator ← Weakest
 ↓
Forward iterator
 ↓
Bidirectional iterator
 ↓
Random-access iterator ← Strongest

Quanto mais forte a capacidade, mais operações são suportadas:

Operação Entrada Avanço Bidirecional Acesso Aleatório
Desreferenciar *
Avançar ++
Voltar --
Acesso aleatório ``
Aritmética + -

(3) 2.3 Exemplo: Iteradores para Diferentes Contêineres

CPP
#include <iostream>
#include <vector>
#include <list>
#include <forward_list>

int main() {
 std::vector<int> v = {1, 2, 3};
 std::list<int> l = {1, 2, 3};
 std::forward_list<int> fl = {1, 2, 3};
 
 // vector: random-access iterator
 auto it_v = v.begin();
 std::cout << it_v[2] << std::endl; // Random access works
 
 // list: bidirectional iterator
 auto it_l = l.begin();
 ++it_l; // Can advance
 --it_l; // Can move back
 // it_l[2]; // ❌ Error! list doesn't support random access
 
 return 0;
}

Saída:

TEXT 📖 Somente leitura
3


3. Invalidação de Iteradores

(1) 3.1 O que é Invalidação de Iteradores?

Invalidação de iteradores ocorre quando uma operação no contêiner faz com que a posição de um iterador se torne inválida.

Causas comuns:


(2) 3.2 Regras de Invalidação de Iteradores por Contêiner

Contêiner Operação Invalidação
vector push_back Pode invalidar (quando ocorre realocação)
vector erase Iteradores no e após o elemento excluído são invalidados
list push_back Não invalidado
list erase Apenas o iterador para o elemento excluído é invalidado
map/set erase Apenas o iterador para o elemento excluído é invalidado

(3) 3.3 Exemplo: Invalidação de Iterador do vector (Dificuldade ⭐⭐)

▶ Exemplo 1: Uso de Contêiner STL (Dificuldade ⭐)

CPP
#include <iostream>
#include <vector>

int main() {
 std::vector<int> v = {1, 2, 3, 4, 5};
 
 auto it = v.begin();
 std::cout << "*it = " << *it << std::endl; // Output: 1
 
 // push_back may cause reallocation, invalidating iterators
 v.push_back(6);
 
 // ❌ Dangerous! it may be invalidated
 // std::cout << "*it = " << *it << std::endl; // Undefined behavior
 
 // ✅ Correct approach: re-acquire the iterator
 it = v.begin();
 std::cout << "*it = " << *it << std::endl; // Output: 1
 
 return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
*it = 1
*it = 1

(4) 3.4 Removendo Elementos com Segurança

Abordagem errada:

TEXT 📖 Somente leitura
for (auto it = v.begin(); it != v.end(); ++it) {
### ▶ Exemplo 2: Aplicação de Recurso do C++ Moderno (Dificuldade ⭐)

 if (*it % 2 == 0) {
 v.erase(it); // ❌ it is invalidated!
 }
}

Abordagem correta:

CPP
for (auto it = v.begin(); it != v.end(); ) {
 if (*it % 2 == 0) {
 it = v.erase(it); // ✅ erase returns the next valid iterator
 } else {
 ++it;
 }
}

Saída:

TEXT 📖 Somente leitura
(program output)


4. Iteradores Reversos

(1) 4.1 O que são Iteradores Reversos?

Iteradores reversos percorrem um contêiner do final ao início.

Exemplo: Saída reversa (Dificuldade ⭐)

CPP
#include <iostream>
#include <vector>

int main() {
 std::vector<int> v = {1, 2, 3, 4, 5};
 
 // Using reverse iterator
 for (auto it = v.rbegin(); it != v.rend(); ++it) {
 std::cout << *it << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

Resultado:

TEXT 📖 Somente leitura
5 4 3 2 1 


5. Iteradores de Inserção

(1) 5.1 O que são Iteradores de Inserção?

Iteradores de inserção são iteradores de saída usados para inserir elementos em um contêiner.

Três tipos de iteradores de inserção:

Iterador Função Exemplo
back_inserter Inserir no final std::back_inserter(v)
front_inserter Inserir no início std::front_inserter(l)
inserter Inserir em posição especificada std::inserter(v, v.begin())

(2) 5.2 Exemplo: Copiando com back_inserter (Dificuldade ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

int main() {
 std::vector<int> src = {1, 2, 3, 4, 5};
 std::vector<int> dst; // Empty container
 
 // ❌ Wrong! dst doesn't have enough space
 // std::copy(src.begin(), src.end(), dst.begin());
 
 // ✅ Correct! Use back_inserter for automatic expansion
 std::copy(src.begin(), src.end(), std::back_inserter(dst));
 
 std::cout << "Copy result: ";
 for (int x : dst) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

Saída:

TEXT 📖 Somente leitura
1 2 3 4 5


6. Iteradores de Fluxo

(1) 6.1 Iteradores de Fluxo de Entrada

Função: Ler dados de um fluxo de entrada.

Exemplo: Lendo da entrada padrão (Dificuldade ⭐⭐)

TEXT 📖 Somente leitura
#include <iostream>
#include <vector>
#include <iterator>

int main() {
 std::vector<int> v;
 
 std::cout << "Enter some numbers (Ctrl+Z to end):" << std::endl;
 
 // Read from standard input
 std::copy(std::istream_iteratorint(std::cin),
 std::istream_iteratorint(),
 std::back_inserter(v));
 
 std::cout << "You entered: ";
 for (int x : v) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

(2) 6.2 Iteradores de Fluxo de Saída

Função: Escrever dados em um fluxo de saída.

Exemplo: Saída para arquivo (Dificuldade ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <iterator>
#include <fstream>

int main() {
 std::vector<int> v = {1, 2, 3, 4, 5};
 
 // Output to standard output
 std::copy(v.begin(), v.end(),
 std::ostream_iteratorint(std::cout, " "));
 std::cout << std::endl;
 
 // Output to file
 std::ofstream file("output.txt");
 std::copy(v.begin(), v.end(),
 std::ostream_iteratorint(file, "\n"));
 file.close();
 
 return 0;
}

❓ Perguntas Frequentes

P: Por que list não suporta acesso aleatório? R: list é uma lista encadeada — elementos não são contíguos na memória, então você não pode acessá-los diretamente por índice.


P: Como evitar invalidação de iteradores? R: 1. Readquira iteradores após cada operação no contêiner 2. Use o valor de retorno de erase para atualizar iteradores 3. Prefira usar algoritmos em vez de manipulação manual de iteradores


P: O que é const_iterator? R: const_iterator é um iterador somente leitura que não pode modificar valores de elementos.

TEXT 📖 Somente leitura
std::vector<int> v = {1, 2, 3};
std::vector<int>::const_iterator it = v.cbegin();
// *it = 10; // ❌ Error! Cannot modify

▶ Exemplo 3: Percorrendo com Iterador (Dificuldade ⭐)

CPP
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};

    for (auto it = v.begin(); it != v.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
1 2 3 4 5
💡 Dica: Iteradores funcionam como ponteiros — use * para desreferenciar, ++ para mover, e != para comparar posições.


Tópico Pontos-chave
Categorias de iteradores Entrada → Avanço → Bidirecional → Acesso Aleatório
Invalidação de iteradores vector pode invalidar, list não
Iteradores reversos rbegin()/rend()
Iteradores de inserção back_inserter etc.
Iteradores de fluxo Conectam STL e E/S

📖 Resumo

📝 Exercícios

  1. Básico (Dificuldade ⭐): Use iteradores para percorrer vector<int> e exibir todos os elementos. Faça tanto com begin/end quanto com for baseado em intervalo.

  2. Intermediário (Dificuldade ⭐⭐): Use iteradores reversos rbegin/rend para percorrer um vector de forma reversa e exibir. Observe a diferença em relação ao percurso para frente.

  3. Desafio (Dificuldade ⭐⭐⭐): Implemente um iterador personalizado que encapsula um intervalo de inteiros (por exemplo, 1 a 10), suportando operadores ++ e *.



Próxima lição: Objetos de Função STL (#38)

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%