DMarlon Studio

Lesson 13 · 13 of 20

Iterators · C++

Walk through C++ containers with iterators: begin, end, rbegin, next, distance and erase on a std::list, with a route and a playlist.

C++ · Iterators in DMarlon Studio: the example's code and its output in the terminal

An iterator points to an element in a container: *it reads it, ++it moves to the next one and it != route.end() checks whether you have reached the end. Note that end() is not the last element but the position after it. Iterators are what the range for uses under the hood and what every algorithm takes.

std::next(it) returns the following iterator without moving it; here it tells whether more cities are left, so -> only goes between them. With `rbegin()` and `rend()` the same code walks backwards: the trip home.

std::vector iterators are random access: they support arithmetic such as route.begin() + 2. std::distance counts the steps between two iterators, and stop->size() calls a method on the element pointed to, just as with a pointer.

In a `std::list` (a doubly linked list), inserting and erasing in the middle is cheap, but erasing invalidates the iterator of the erased element. That is why erase(it) returns the next one and the loop only moves on with ++it when it does not erase. insert puts "Jingle" before the given position, the second one. `cbegin()` and `cend()` give const iterators, which only allow reading.

Code · main.cpp

#include <iostream>#include <iterator>#include <list>#include <string>#include <vector>int main() {    // An iterator points to an element: *it reads it and ++it moves to the next.    std::vector<std::string> route = {"Madrid", "Zaragoza", "Lleida", "Barcelona"};    for (auto it = route.begin(); it != route.end(); ++it)        std::cout << *it << (std::next(it) != route.end() ? " -> " : "\n");    // Reverse iterators walk backwards: the trip home.    for (auto it = route.rbegin(); it != route.rend(); ++it)        std::cout << *it << (std::next(it) != route.rend() ? " -> " : "\n");    // Vector iterators allow arithmetic; distance counts the steps.    auto stop = route.begin() + 2;    std::cout << "Third stop: " << *stop << ", " << std::distance(route.begin(), stop)              << " steps from the start, " << stop->size() << " letters\n";    // In a list, erasing and inserting through an iterator is cheap.    std::list<std::string> playlist = {"Intro", "Ad", "Song A", "Ad", "Song B"};    for (auto it = playlist.begin(); it != playlist.end();) {        if (*it == "Ad") it = playlist.erase(it);      // erase returns the next one        else ++it;    }    playlist.insert(std::next(playlist.begin()), "Jingle");   // before the 2nd track    for (auto it = playlist.cbegin(); it != playlist.cend(); ++it)   // const: read only        std::cout << "[" << *it << "]";    std::cout << "\n";}

Output · c++ -std=c++17 main.cpp -o main && ./main

Madrid -> Zaragoza -> Lleida -> Barcelona
Barcelona -> Lleida -> Zaragoza -> Madrid
Third stop: Lleida, 2 steps from the start, 6 letters
[Intro][Jingle][Song A][Song B]

Key points

  • *it reads the element, ++it moves on and end() lies past the last one.
  • rbegin() and rend() walk backwards.
  • std::next, std::distance and begin() + n move and measure.
  • erase(it) returns the next iterator: use it when erasing inside a loop.
  • cbegin() and cend() give read-only iterators.

Exercise

Use std::find to get an iterator to "Song A" in the playlist and insert "Encore" right after it (hint: std::next). Print the final playlist.