1. Home
C++ Tutorial

Explore C++ Tutorials: Exploring the World of C++ Programming

Discover comprehensive C++ tutorials designed for beginners and advanced programmers alike. Enhance your coding skills with step-by-step guides and practical examples.

  • 77 Lessons
  • 15 Hours
right-top-arrow
32

Iterators in C++: A Complete Guide to Efficient Data Traversal

Updated on 27/09/2024419 Views

Iterators are essential in C++ programming. They support data traversal and we can think of them as generalized pointers that extend the concept of pointing to a specific memory location.

I like to think that iterators in C++ act as our trusty guides, leading us through the complex pathways of our collections. But what exactly are these iterators, and why are they so fundamental to the C++ experience? That’s exactly what we will learn in this guide.

In this tutorial, we will learn everything about iterators and explore their different types, the operations they support, and how to use them effectively with STL containers. I’ll help you use iterators with confidence, effortlessly being able to traverse and manipulate your data with the elegance and efficiency that C++ offers.

Let us learn more.

What are Iterators in C++?

Unlike raw pointers, which directly manipulate memory addresses, iterators in C++ provide a higher-level abstraction. They offer a consistent and standardized way to access and manipulate elements within various container types, such as vectors, lists, maps, and more.

This abstraction is where the true power of iterators in C++ lies. By decoupling the act of iteration from the specific implementation details of the underlying data structure, iterators unlock the door to generic programming. Iterators allow us to write algorithms that work seamlessly with a wide range of containers, without having to rewrite the code for each specific type.

The Standard Template Library (STL), the crown jewel of C++, owes much of its power and flexibility to iterators. STL algorithms like std::sort, std::find, and std::transform operate on ranges defined by iterators, making them incredibly versatile and reusable.

Types of Iterators in C++

C++ offers a rich taxonomy of iterators, each tailored to specific access patterns and operations. Understanding their unique characteristics empowers you to choose the right tool for the job, ensuring efficient and safe data manipulation. By choosing the right cpp iterator type for our specific task, we can harness the full power of the C++ STL and write efficient generic code.

Input Iterators in C++

Input iterators are the simplest and most restrictive type. They are read-only, single-pass iterators, meaning you can only move forward through the data once, and you can only access the elements, not modify them. These iterators in C++ are often used for reading data from input streams or in algorithms that don't require modifying the underlying data.

(Example) Reading integers from a file:

std::ifstream inputFile("data.txt");

std::istream_iterator<int> start(inputFile), end;

std::vector<int> numbers(start, end); // Read all integers into a vector

Forward Iterators in C++

Forward iterators in C++ build upon input and output iterators, allowing both reading and writing while still maintaining the single direction restriction. They enable multiple passes over the data, making them suitable for algorithms that modify elements or require multiple readings.

(Example) Modifying elements in a list:

std::ofstream outputFile("output.txt");

std::ostream_iterator<int> outputIterator(outputFile, " ");

std::vector<int> data = {1, 2, 3, 4, 5};

std::copy(data.begin(), data.end(), outputIterator); // Write vector to file

Bidirectional Iterators in C++

Bidirectional iterators enhance forward iterators with the ability to move backward as well as forward. This added flexibility makes them indispensable for algorithms that require traversing data in both directions, such as reversing a sequence or searching for elements in either direction.

(Example) Reversing a set:

std::set<int> numbers = {5, 2, 8, 1};

std::set<int>::reverse_iterator rit;

for (rit = numbers.rbegin(); rit != numbers.rend(); ++rit) {

std::cout << *rit << " "; // Output: 8 5 2 1

}

Random Access Iterators in C++

Random access iterators are the most powerful and versatile. These iterators in C++ offer constant-time access to any element within the container, much like accessing elements in an array using indices. This capability is essential for algorithms that rely on efficient random access, like sorting or binary search.

(Example) Sorting a vector:

std::vector<int> numbers = {5, 2, 8, 1};

std::sort(numbers.begin(), numbers.end());

// Sorts the vector in ascending order using random access iterators

Incrementing/Decrementing Iterators in C++

These iterators in C++ provide intuitive mechanisms for moving through your data structures:

  • ++iter (pre-increment): Advances the iterator to the next element and returns a reference to the new position. This is generally preferred for efficiency, as it avoids creating a temporary copy of the iterator.
  • iter++ (post-increment): Creates a copy of the iterator, advances the original iterator to the next element, and then returns the copy. Use this when you need to retain the original iterator position.

For bidirectional and random-access iterators, you can also use --iter and iter-- to move backward through the data.

Dereferencing Iterators in C++

The * operator, when applied to an iterator, reveals the element it points to.

Example (with C++ vector iterator):

std::vector<int> numbers = {1, 2, 3, 4, 5};

std::vector<int>::iterator it = numbers.begin();

std::cout << *it << std::endl; // Output: 1

If the iterator is mutable (i.e., not a const_iterator), you can even modify the element directly:

*it = 10; // Changes the first element of the vector to 10

Comparing Iterators in C++

Iterators also provide the means to compare their positions relative to each other:

  • iter1 == iter2: Checks if two iterators point to the same element. This is often used in loops to determine when the end of a container is reached.

Example:

for (auto it = numbers.begin(); it != numbers.end(); ++it) { ... }

  • iter1 < iter2, iter1 > iter2, etc.: These comparison operators check the relative order of iterators, which is particularly useful with random-access iterators that support indexing.

Example:

std::vector<int>::iterator it1 = numbers.begin();

std::vector<int>::iterator it2 = numbers.begin() + 2;

if (it1 < it2) { ... } // it1 points to an earlier element than it2

Iterator Operations: Using Iterators in C++ with STL Containers

Now that you have a solid understanding of cpp iterator types and basic operations, let us explore how to harness their power within the vast landscape of the Standard Template Library (STL) containers.

begin() and end()

Every STL container, whether it's a vector, list, map, or set, offers two essential member functions: begin() and end(). These functions act as the entry and exit points for traversing your data using iterators.

  • begin(): This function returns an iterator pointing to the first element in the container. It's your starting point for any traversal.
  • end(): This function returns an iterator pointing past the last element in the container. Think of it as a marker indicating the end of the valid data range. You should never dereference the end() iterator, as it doesn't point to an actual element.

Traversing with for Loops

Let's see how to use begin() and end() in a for loop to iterate over a vector:

std::vector<int> numbers = {1, 2, 3, 4, 5};

for (std::vector<int>::iterator it = numbers.begin(); it != numbers.end(); ++it) {

std::cout << *it << " "; // Output: 1 2 3 4 5

}

In this loop, it starts at numbers.begin() and moves forward with each iteration until it reaches numbers.end(). During each iteration, we dereference it to access and print the current element.

Range-Based for Loops

For added convenience, C++11 introduced range-based for loops, which simplify iteration even further:

for (const int& num : numbers) {

std::cout << num << " "; // Output: 1 2 3 4 5

}

Behind the scenes, the range-based for loop also uses iterators, but it automatically handles their initialization, increment, and comparison with end().

STL Algorithms and Iterators

Iterators truly shine when used with the vast array of algorithms provided by the STL. These algorithms, such as std::find, std::sort, and std::transform, operate on ranges defined by iterators, making them incredibly flexible and applicable to various containers.

(Example) Finding an element in a vector:

std::vector<int>::iterator it = std::find(numbers.begin(), numbers.end(), 3);

if (it != numbers.end()) {

std::cout << "Found 3 at position: " << std::distance(numbers.begin(), it) << std::endl;

}

Container-Specific Member Functions

Many STL containers offer additional member functions that return iterators, providing specialized ways to interact with their elements. By leveraging these functions, you can efficiently manipulate the contents of your containers while maintaining the integrity of their underlying data structures.

For example:

  • insert(iterator, value): Inserts an element before the specified iterator.
  • erase(iterator): Removes the element pointed to by the iterator.

C++ Iterator Example Program

Here is an example of a C++ list iterator program (with std::list)that you can run yourself.

Code:

#include <iostream>

#include <list>

#include <algorithm>

int main() {

// Create a list of integers

std::list<int> numbers = {7, 5, 16, 8, 25, 13};

// Display original list

std::cout << "Original list: ";

for (int num : numbers) {

std::cout << num << " ";

}

std::cout << std::endl;

// 1. Iterating and modifying elements

for (std::list<int>::iterator it = numbers.begin(); it != numbers.end(); ++it) {

*it += 1; // Increment each element by 1

}

// 2. Finding an element (using std::find)

std::list<int>::iterator it = std::find(numbers.begin(), numbers.end(), 17);

if (it != numbers.end()) {

std::cout << "Found 17 in the list at position: "

<< std::distance(numbers.begin(), it) << std::endl;

} else {

std::cout << "17 not found in the list." << std::endl;

}

// 3. Inserting an element (using insert)

it = numbers.begin();

std::advance(it, 2); // Move iterator to the third position

numbers.insert(it, 99);

// 4. Deleting an element (using erase)

it = std::find(numbers.begin(), numbers.end(), 26);

if (it != numbers.end()) {

numbers.erase(it);

}

// 5. Iterating in reverse (using reverse_iterator)

std::cout << "Reversed list: ";

for (std::list<int>::reverse_iterator rit = numbers.rbegin(); rit != numbers.rend(); ++rit) {

std::cout << *rit << " ";

}

std::cout << std::endl;

return 0;

}

You now know how to C++ create iterator. If you wish to learn how to code in C++, you can check out upGrad’s software engineering courses.

Advanced CPP Iterator Techniques: Fine-Tuning Iterations

As you become more adept at wielding iterators, you will discover a range of advanced techniques that offer even greater flexibility and control over how you interact with data. Let us explore some of these techniques to elevate your iterator game.

Const Iterators

In certain scenarios, you might want to ensure that the data within a container remains unchanged during iteration. This is where const iterators come to the rescue. They act as guardians, preventing any modifications to the elements they point to.

To obtain a const iterator, you can use the cbegin() and cend() member functions of STL containers:

std::vector<int> numbers = {1, 2, 3, 4, 5};

for (std::vector<int>::const_iterator it = numbers.cbegin(); it != numbers.cend(); ++it) {

// *it = 10; // This would be an error, as it tries to modify the element

std::cout << *it << " "; // Output: 1 2 3 4 5

}

In this example, attempting to modify the elements through the it iterator would result in a compilation error, ensuring the integrity of the data.

Reverse Iterators

Sometimes, you need to traverse your data in reverse order. Reverse iterators provide a convenient way to do this. Reverse iterators in C++ offer the same operations as regular iterators, but they move backward through the container.

You can obtain reverse iterators using rbegin() and rend() member functions:

std::list<int> numbers = {1, 2, 3, 4, 5};

for (std::list<int>::reverse_iterator rit = numbers.rbegin(); rit != numbers.rend(); ++rit) {

std::cout << *rit << " "; // Output: 5 4 3 2 1

}

Note that rbegin() points to the last element, and rend() points before the first element. Incrementing a reverse iterator actually moves it backward through the container.

Iterator Adapters

Iterator adapters are special iterators that modify the behavior of existing iterators to suit specific tasks. They act as intermediaries, providing a customized interface for interacting with data.

  • std::back_inserter(container): This adapter converts any operation that would normally overwrite an element into an insertion at the back of the container. This is useful when you want to append elements during iteration.

Example:

std::vector<int> numbers = {1, 2, 3};

std::fill_n(std::back_inserter(numbers), 2, 0); // Appends two zeros to the vector

  • std::front_inserter(container): Similar to std::back_inserter, but inserts elements at the front.
  • std::ostream_iterator(ostream, delimiter): Used to write elements to an output stream, with an optional delimiter between elements.

Final Tips

By understanding iterator operations, we unlock the ability to navigate and manipulate data structures in C++ with unparalleled flexibility and control. Whether we are iterating over a list, searching for a specific element, or transforming data in place, iterators provide the tools we need to achieve our goals with elegance and efficiency.

If you wish to learn programming languages such as C++, you can check out upGrad’s computer science programs such as the Master’s in Computer Science Program.

Frequently Asked Questions

  1. What is an iterator in C++?

An iterator is an object in C++ that acts as a generalized pointer, allowing traversal and manipulation of elements within containers like vectors and lists.

  1. What are the types of iterators in C++?

There are five main types: input, output, forward, bidirectional, and random access iterators, each offering varying levels of functionality and access capabilities.

  1. How do iterators differ from pointers in C++?

While both point to elements, iterators provide a higher-level abstraction, working with various containers and supporting operations like incrementing and dereferencing.

  1. How do you use iterators in C++?

Iterators in C++ are obtained using member functions like begin(), (start) and end() (one past the end), and then used in loops or algorithms to access and modify container elements.

  1. What is the difference between begin() and end() iterators?

begin() returns an iterator pointing to the first element, while end() returns an iterator pointing one position past the last element, acting as a stopping point.

  1. Can you modify elements using iterators in C++?

Yes, if the iterator is not const, we can modify the element it points to using the dereference operator (*).

  1. What are the advantages of using iterators?

Iterators provide a consistent way to work with different container types, enabling generic algorithms and abstracting the underlying data structure for flexibility.

  1. Are iterators in C++ pointers?

Not exactly. Iterators are objects that behave like pointers, but they can have more complex internal structures and offer additional functionalities beyond simple memory addressing.

Kechit Goyal

Kechit Goyal

Team Player and a Leader with a demonstrated history of working in startups. Strong engineering professional with a Bachelor of Technology (BTech…Read More

Need Guidance? We're Here to Help!
form image
+91
*
By clicking, I accept theT&Cand
Privacy Policy
image
Join 10M+ Learners & Transform Your Career
Learn on a personalised AI-powered platform that offers best-in-class content, live sessions & mentorship from leading industry experts.
right-top-arrowleft-top-arrow

upGrad Learner Support

Talk to our experts. We’re available 24/7.

text

Indian Nationals

1800 210 2020

text

Foreign Nationals

+918045604032

Disclaimer

upGrad does not grant credit; credits are granted, accepted or transferred at the sole discretion of the relevant educational institution offering the diploma or degree. We advise you to enquire further regarding the suitability of this program for your academic, professional requirements and job prospects before enr...