Iterators

Iterators are special objects that can be used to iterate over (loop through) container objects.

Table of Contents

  1. Iterators
  2. Iterator Safety
  3. Iterators and Standard Collections
  4. Next and Prev Iterators
  5. Reverse Iterators
  6. Const Iterators
  7. Iterators as Function Arguments
  8. Further Reading

Iterators

Another way to traverse a collection is to use iterators, which are special objects that allow us to step through the elements of a container.

All standard containers come with a function that returns an iterator that points to the start of the collection:

  std::vector bagOfHolding{ "sword", "shield", "potion" };
  auto iterator{ bagOfHolding.begin() }; // Iterator that points to the first element.

With iterators we can:

  • Use the * operator to access the element the iterator points to.
  • Use addition or subtraction to advance or rewind the iterator.
  std::cout << *iterator << "\n"; // Use * to access the element. Outputs: sword
  iterator++; // Advance the iterator
  std::cout << *iterator << "\n"; // Outputs: shield
  iterator++; // Advance the iterator
  std::cout << *iterator << "\n"; // Outputs: potion
  iterator--; // Rewinds the iterator
  std::cout << *iterator << "\n"; // Outputs: shield

Iterator Safety

Nothing stops us from advancing an iterator beyond the end of a collection. This can cause our programs to crash:

  std::vector twoWords{ "ghostly", "grinner" };
  auto iterator{ twoWords.begin() }; // Iterator that points to the first element.

  std::cout << *iterator << "\n"; // Use * to access the element. Outputs: ghostly
  iterator++; // Advance the iterator
  std::cout << *iterator << "\n"; // Outputs: grinner
  iterator++; // Oh no! We've advanced the iterator beyond the end of the collection!
  std::cout << *iterator << "\n"; // Segmentation fault!

To guard against this problem we can use the .end() iterator:

  std::vector sentence{ "you", "eat", "bugs" };

  for (auto i{ sentence.begin() }; i != sentence.end(); ++i) {
    std::cout << *i << ' '; // Indirection to get value of current element
  }

🎵 Note:

The end iterator points to one position past the last element of the collection.

Iterators and Standard Collections

The above examples have used vectors, but iterators work for all C++ standard containers:

  std::string sentence{ "You eat bugs!" };
  for (auto i{ sentence.begin() }; i != sentence.end(); ++i) {
    std::cout << *i << "\n";
  }

Next and Prev Iterators

The <iterator> header provides std::next() and std::prev() functions that return the next or previous iterator:

  std::vector sentence{ "you", "eat", "bugs" };
  auto secondElement{ std::next(sentence.begin()) };
  auto lastElement{ std::prev(sentence.end()) };
  // These function also take a second argument for number of position to advance or rewind:
  auto alsoLastElement{ std::next(sentence.begin(), 2) };

âš¡ Warning:

These functions may return iterators pointing outside of our collection!

Reverse Iterators

There are iterators that let us walk through a collection in reverse:

  std::vector sentence{ "you", "eat", "bugs" };

  for (auto i{ sentence.rbegin() }; i != sentence.rend(); ++i) {
    std::cout << *i << ' '; // Indirection to get value of current element
  }

Const Iterators

In many cases when we are using an iterator we aren’t modifying the container contents, in those cases we should be using the const iterators:

  std::vector sentence{ "you", "eat", "bugs" };

  for (auto i{ sentence.cbegin() }; i != sentence.cend(); ++i) {
    std::cout << *i << ' '; // Indirection to get value of current element
  }

🎵 Note:

There are also const reverse iterators crbegin() and crend().

Iterators as Function Arguments

There are many standard functions that operate on a range of container elements using iterators. These function usually take two iterators as arguments, an iterator to the start of the range and an iterator to the end of the range.

Some examples of functions from the <algorithm> header:

#include <algorithm> // std::max_element, std::sort, std::reverse, std::find
#include <iostream>  // std::cout
#include <vector>    // std::vector

int main() {
  std::vector temperatures{ 7, -3, 6, 2, -5, 0, 4 };

  std::cout << "The highest temperature is " 
            << *std::max_element(temperatures.begin(), temperatures.end()) 
            << ".\n";
  
  // Sort the vector in ascending order.
  std::sort(temperatures.begin(), temperatures.end()); 
  // Reverse the order of the vector.
  std::reverse(temperatures.begin(), temperatures.end()); 
  // Find an iterator to the number 0. 
  auto it{std::find(temperatures.begin(), temperatures.end(), 0)};
  // it will be .end() if zero wasn't found.
  if (it != temperatures.end()) {
    // Insert 1 right before 0.
    temperatures.insert(it, 1); 
  }
  
  for(auto temperature : temperatures) {
    std::cout << temperature << " ";
  }
}

Further Reading