C++ Iterators


Introduction

In C++, iterators are objects that allow you to traverse through the elements of a container (such as arrays, vectors, lists, maps, etc.) in a generic way. They provide a unified interface to access elements in different types of containers, enabling you to write code that works with any container type without having to explicitly know the underlying structure.

Iterators are a core part of the C++ Standard Template Library (STL) and play a crucial role in making C++ containers flexible and reusable.


Types of Iterators

There are several types of iterators in C++, which offer different levels of functionality:

  1. Input Iterator: Allows reading the container's elements in a one-way, forward direction.
  2. Output Iterator: Allows writing to a container in a one-way, forward direction.
  3. Forward Iterator: Allows both reading and writing in a one-way direction, like input iterators but with the ability to modify values.
  4. Bidirectional Iterator: Allows reading and writing elements in both forward and reverse directions.
  5. Random Access Iterator: Allows reading, writing, and direct access to any element in the container. Provides constant-time complexity for accessing any element, just like array indices.

Declaring and Initializing Iterators

To declare an iterator, use the syntax:

iterator_type<Container>::iterator iterator_name;

Where:

  • iterator_type<Container> is the specific iterator type for the container you're working with, such as std::vector<int>::iterator for a vector of integers.
  • iterator_name is the name of your iterator.

Example: Using Iterators with std::vector

Let's look at an example of how to use an iterator with a std::vector container.

Code Example 1: Basic Iteration with a Vector

#include <iostream>
#include <vector>

int main() {
    // Declare and initialize a vector
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // Declare an iterator for the vector
    std::vector<int>::iterator it;

    // Iterate through the vector using the iterator
    for (it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";  // Dereferencing iterator to access element
    }

    return 0;
}

Output:

1 2 3 4 5

In this example:

  • vec.begin() returns an iterator to the first element.
  • vec.end() returns an iterator that points one position past the last element.
  • We dereference the iterator with *it to access the element it points to.

Iterator Operations

Iterators support several operations that allow you to traverse and manipulate container elements:

1. Dereferencing the Iterator

You can dereference an iterator to access the element it points to:

*it  // Dereferences the iterator to access the value

2. Incrementing and Decrementing

You can move the iterator to the next or previous element by using the increment (++) or decrement (--) operators:

++it  // Move iterator forward (next element)
--it  // Move iterator backward (previous element)

3. Comparing Iterators

You can compare iterators to check if one iterator is equal to or different from another, typically used with begin() and end():

it == vec.end()  // Check if iterator has reached the end
it != vec.end()  // Check if iterator has not yet reached the end

4. Random Access Iterators

For random access iterators (like those for std::vector and std::deque), you can directly access elements using arithmetic operations like + or -.

*(it + 2)  // Access the third element from the current position

Iterators with Other Containers

C++ iterators work with various types of containers. Here's a look at how to use iterators with different container types:

1. Using Iterators with std::list

Unlike std::vector, std::list is a doubly linked list and provides bidirectional iterators.

#include <iostream>
#include <list>

int main() {
    std::list<int> lst = {10, 20, 30, 40, 50};
    std::list<int>::iterator it;

    // Iterating through the list using iterator
    for (it = lst.begin(); it != lst.end(); ++it) {
        std::cout << *it << " ";
    }

    return 0;
}

Output:

10 20 30 40 50

2. Using Iterators with std::map

A std::map is a sorted associative container, and its iterators allow you to access both the key and the associated value.

#include <iostream>
#include <map>

int main() {
    std::map<int, std::string> map = {{1, "One"}, {2, "Two"}, {3, "Three"}};

    // Iterate through the map using iterators
    for (std::map<int, std::string>::iterator it = map.begin(); it != map.end(); ++it) {
        std::cout << it->first << ": " << it->second << std::endl;
    }

    return 0;
}

Output:

1: One
2: Two
3: Three

In this example, it->first accesses the key, and it->second accesses the value.

3. Using Iterators with std::set

std::set also supports iterators, and iterating through it will give you the elements in sorted order.

#include <iostream>
#include <set>

int main() {
    std::set<int> s = {5, 2, 3, 8, 1};

    // Using iterators to iterate through set (elements will be in sorted order)
    for (std::set<int>::iterator it = s.begin(); it != s.end(); ++it) {
        std::cout << *it << " ";
    }

    return 0;
}

Output:

1 2 3 5 8

Advanced Iterator Usage

1. Reverse Iterators

To traverse a container in reverse order, use reverse iterators. You can get a reverse iterator using rbegin() and rend().

#include <iostream>
#include <vector>

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

    // Use reverse iterators to print in reverse order
    for (std::vector<int>::reverse_iterator rit = vec.rbegin(); rit != vec.rend(); ++rit) {
        std::cout << *rit << " ";
    }

    return 0;
}

Output:

5 4 3 2 1

2. Iterator-to-Iterator Range

You can also use iterators to work with subranges of containers, for example, copying or modifying part of a container.

#include <iostream>
#include <vector>
#include <algorithm>

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

    // Copy a range of elements from vec to subVec
    std::copy(vec.begin(), vec.begin() + 3, subVec.begin());

    // Print subVec
    for (int i : subVec) {
        std::cout << i << " ";
    }

    return 0;
}

Output:

1 2 3