What Are Iterators in C++?
Iterators are one of the four pillars of the Standard Template Library or STL in C++. An iterator is used to point to the memory address of the STL container classes. For better understanding, you can relate them with a pointer, to some extent.
Iterators act as a bridge that connects algorithms to STL containers and allows the modifications of the data present inside the container. They allow you to iterate over the container, access and assign the values, and run different operators over them, to get the desired result.
Iterators in C++ are classified into 5 major categories based on their functionality. The following 5 iterators are explored in-depth, further in this article:
- Input iterator
- Output iterator
- Forward iterator
- Bidirectional iterator
- Random access iterator
Use of Iterators in C++
An iterator in C++ serves the following major purposes:
- The primary objective of an iterator is to access the STL container elements and perform certain operations on them.
- The internal structure of a container does not matter, since the iterators provide common usage for all of them.
- Iterator algorithms are not dependent on the container type.
- An iterator can be used to iterate over the container elements. It can also provide access to those elements to modify their values.
- Iterators follow a generic approach for STL container classes. This way, the programmers don’t need to learn about different iterators for different containers.
Syntax of Defining Iterators
<Container_Type> :: iterator;
<Container_Type> :: const_iterator;
Parameters used in the above syntax:
- Container_Type: This parameter is the type of container for which the iterator is declared.
All STL containers do not support all 5 types of iterators. For instance, random-access iterators are supported by the container “vector”, whereas bidirectional iterators are supported by the container “list”.
The following table contains STL containers and the iterators that are supported by them in C++:
STL CONTAINER |
ITERATOR SUPPORTED |
Vector |
Random-Access |
List |
Bidirectional |
Dequeue |
Random-Access |
Map |
Bidirectional |
Multimap |
Bidirectional |
Set |
Bidirectional |
Multiset |
Bidirectional |
Stack |
Does not support any iterator |
Queue |
Does not support any iterator |
Priority-Queue |
Does not support any iterator |
Difference Between Iterators and Pointers
Although pointers and iterators seem alike at first glance, there are many significant differences between them which you need to understand.
Pointers:
A pointer is a variable that stores the memory address of the variable it is pointing to. Like a normal variable, a pointer also has a data type that is the same as the data type of the variable whose memory it is storing. The pointer helps to provide large information to the functions by just passing the memory address of the object.
Syntax to declare a pointer in C++:
data_type* pointer_name
The following program illustrates the concept of pointers in C++:
#include <iostream>
using namespace std;
int main()
{
// initialize a variable myVar
int myVar = 10;
// initialize a pointer ptr
// pointing to myVar
int* ptr = &myVar;
// printing myVar will display 10
cout << “Value stored in myVar: ” << myVar;
cout << “\n”;
// printing *ptr will also display 10
cout << “Dereferenced pointer pointing to myVar: ” << *ptr;
cout << “\n”;
// update the value of myVar
myVar++;
// now *ptr will print 11, since myVar has been updated
cout << “Dereferenced pointer pointing to myVar after updating myVar: “;
cout << *ptr;
cout << “\n\n”;
return 0;
}
Iterators:
Iterators are used to point to the memory addresses of the STL containers. Apart from that, an iterator is also used to iterate over the data structures. It can access or assign values that a pointer is unable to do.
Pointers in C++ can also point to functions, whereas the iterators just serve the purpose of performing operations on the STL containers.
Syntax to declare an iterator in C++:
type_container :: iterator itr_name
The following program illustrates the concept of iterators in C++:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// initialize a vector
vector<int> v1 = { 10, 20, 30, 40 };
// declare an iterator
vector<int>::iterator itr;
// access vector elements without iterator
cout << “Traversing without iterator : “;
for (int j = 0; j < 4; ++j) {
cout << v1[j] << ” “;
}
cout << “\n”;
// access vector elements using an iterator
cout << “Traversing using iterator “;
for (itr = v1.begin(); itr != v1.end(); ++itr) {
cout << *itr << ” “;
}
cout << “\n\n”;
// insert an element into the vector
v1.push_back(50);
// access vector elements without iterator
cout << “Traversing without iterator : “;
for (int j = 0; j < 5; ++j) {
cout << v1[j] << ” “;
}
cout << “\n”;
// access vector elements using an iterator
cout << “Traversing using iterator “;
for (itr = v1.begin(); itr != v1.end(); ++itr) {
cout << *itr << ” “;
}
cout << “\n\n”;
return 0;
}
Categories of Iterators
As earlier discussed, there are 5 main types of iterators in C++ STL. In this section, you are going to see all these 5 iterators in detail. These 5 iterators are:
1. Input Iterators in C++
The input iterator is the simplest and least used iterator among the five main iterators of C++. It sequentially uses this iterator for input operations. In other words, you can say that it is used to read the values from the container. It is a one-way iterator. Once you have read a value, you are only allowed to increment the iterator. You can not decrement the input iterator in any way.
Salient Features
The input iterator in C++ has the following salient features:
- Equality and Inequality operator: You can compare the equality of two input iterators. Two iterators are said to be equal if both of them are pointing towards the same location. Otherwise, they are considered unequal.
In the expression shown below, consider it1 and it2 as two input iterators:
it1 == it2 //Using equality operator
it1 != it2 //Using inequality operator
- Usability: Input iterators are one-way iterators. This means that you can use them to iterate only in one direction at a time. These iterators work on a “single-pass algorithm”.
- Dereferencing: Dereferencing is used to access the data whose address is being pointed to by a pointer. The asterisk (*) is used alongside the pointer variable name for dereferencing a pointer variable.
In the expression shown below, consider it as an input iterator:
*it // Dereferencing it using the asterisk(*)
- Incrementable: Since input iterators are one-way iterators, you can increment them in the forward direction. These iterators can be incremented either in a pre-increment manner or post-increment manner.
In the expression shown below, consider it as an input iterator:
it++ // Using post increment operator
++it // Using pre increment operator
- Swappable: The values of the two input iterators that are pointing to different positions can be easily swapped or exchanged with each other.
Limitations
The following are some of the major limitations of the input iterators in C++:
- Input iterators are read-only. They can only read the data of the location to which the pointer points, they can not be used to assign the values.
- As mentioned earlier, these iterators are unidirectional. They can only move in a forward direction i.e., they can only be incremented. You can not decrement them.
- You can not use these iterators in a multi-pass algorithm where you have to move in both directions inside a container.
- Except for equality and inequality operators, you can not implement any other relational operator on these iterators.
- Like relational operators, you cannot implement arithmetic operators on the input iterators.
The following example will illustrate the input iterator in C++:
#include<bits/stdc++.h>
using namespace std;
int main()
{
// initialize a vector
vector<int> v{1, 2, 3, 4, 5};
// declare iterators
vector<int>::iterator it1, it2, temp;
// inititalize the iterators
it1 = v.begin(); // will point to the first element, i.e. 1
it2 = v.end() – 1; // will point to the last element, i.e. 5
// dereference and print iterators before swapping them
cout << “Before Swapping” << endl;
cout << “Dereferenced iterator 1: ” << *it1 << ” ” ;
cout << “\n”;
cout << “Dereferenced iterator 2: ” << *it2;
cout << “\n\n”;
// swap the iterators
temp = it1;
it1 = it2;
it2 = temp;
// dereference and print iterators after swapping them
cout << “Before Swapping” << endl;
cout << “Dereferenced iterator 1: ” << *it1 << ” ” ;
cout << “\n”;
cout << “Dereferenced iterator 2: ” << *it2;
cout << “\n\n”;
return 0;
}
2. Output Iterators in C++
Output iterators serve exactly the opposite purpose as the input iterators. This iterator is sequentially used for output operations. In other words, you can say that it is used to assign the values. But it can not access the values. It is complementary to the input iterators where you can access the values, but can not assign them. Like the input iterator, it is a one-way iterator. Once you have assigned a value, you are only allowed to increment the iterator, and you can not decrement the output iterator in any way.
Salient Features
The output iterator in C++ has the following salient features:
- Equality and Inequality operator: Just like the input iterators, you can compare the equality of two output iterators. Two iterators are said to be equal if both of them are pointing towards the same location. Otherwise, they are considered unequal.
In the expression shown below, consider it1 and it2 as two output iterators:
it1 == it2 // Using equality operator
it1 != it2 // Using inequality operator
- Usability: Output iterators also work with single-pass algorithms where you can visit an element only once at most. You can assign the value only once.
- Dereferencing: You can dereference an output iterator as an lvalue to get the position for storing the value. The method of dereferencing is the same as for the input iterators.
In the expression shown below, consider it as an output iterator:
*it // Dereferencing it using the asterisk(*)
- Incrementable: Just like input iterators, output iterators are one-way iterators too, and you can increment them in the forward direction. These iterators can be incremented either in a pre-increment manner or post-increment manner.
In the expression shown below, consider it as an output iterator:
it++ // Using post increment operator
++it // Using pre increment operator
- Swappable: The value of the two output iterators that are pointing to different positions can be easily swapped or exchanged with each other.
Limitations
The following are some of the major limitations of the output iterators in C++:
- You can not access the values using output iterators. These iterators can only be used to assign the values.
- As mentioned earlier, these iterators are unidirectional. They only move in a forward direction i.e., they can only be incremented. You can not decrement them.
- You can not use these iterators in a multi-pass algorithm where you have to move in both directions inside a container.
- Just like in input iterators, except for equality and inequality operators, you can not implement any other relational operator on output iterators.
- Like relational operators, arithmetic operators can not be implemented on the output iterators.
The following example illustrates the output iterator in C++:
#include<bits/stdc++.h>
using namespace std;
int main () {
// initialize 2 vectors
vector<int> v1, v2;
// add elements into the vectors
for (int i = 1; i <= 10; i++) {
v1.push_back(i);
v2.push_back(i + 2);
}
// initialize an iterator itr pointing to
// the first element in vector v1
vector<int>::iterator itr = v1.begin();
// copy elements of v2 vector to v1 vector at
// the beginning
copy (v2.begin(), v2.end(), inserter(v1, itr));
// print the elements of the vector v1
cout<<“Elements of vector v1 after copying elements of v2 are :”<< endl;
for ( itr = v1.begin(); itr!= v1.end(); ++itr )
{
cout << ” ” << *itr;
}
cout << “\n\n”;
}
3. Forward Iterators in C++
Forward iterators serve the purpose of both the input and output iterators. That’s why these iterators are said to be the combination of input and output operators. You can access the values (functionality of input iterators) as well as assign the values (functionality of output iterators). As the name suggests, these iterators are one-way iterators. You can only traverse in the forward direction. Also, Bidirectional and Random Access iterators are considered valid forward iterators.
Salient Features
The forward iterator in C++ has the following salient features:
- Equality and Inequality operator: Just like the other two iterators mentioned above, you can compare the equality of two output iterators. Two iterators are said to be equal if both of them are pointing towards the same location. Otherwise, they are considered unequal.
In the expression shown below, consider it1 and it2 as two forward iterators:
it1 == it2 // Using equality operator
it1 != it2 // Using inequality operator
- Usability: Forward iterator is the only iterator category that is used by every STL container class. It is the only iterator among the 5 types of iterators that provides the functionality of the simplest kind of loop through a container.
- Dereferencing: Since input iterators are dereferenced as an rvalue and output iterators are dereferenced as an lvalue, and forward iterators are the combination of these iterators, you can use both rvalue and lvalue.
- Incrementable: Forward iterators unidirectional iterators and you can increment them in the forward direction. These iterators can be incremented either in a pre-increment manner or post-increment manner.
In the expression shown below, consider it as a forward iterator:
it++ // Using post increment operator
++it // Using pre increment operator
- Swappable: The value of the two forward iterators that are pointing to different positions can be easily swapped or exchanged with each other.
Limitations
The following are some of the major limitations of the forward iterators in C++:
- The offset dereference operator ([]) is not supported by the forward iterators. So you can not use the offset operator to dereference a forward iterator.
- As mentioned earlier, these iterators are unidirectional. They only move in a forward direction i.e., they can only be incremented. You can not decrement them.
- Just like in input and output iterators, except for equality and inequality operators, you can not implement any other relational operator on the forward iterators.
- Like relational operators, arithmetic operators can not be implemented on the forward iterators.
The following example will illustrate the forward iterator in C++:
#include<bits/stdc++.h>
using namespace std;
template<class FIterator>
void forwardIterator(FIterator start, FIterator end)
{
while(start != end)
{
// dereference the iterator
// to print its value
cout << *start << ” “;
start++;
}
}
int main()
{
// declare a vector
vector<int> v1;
// add elements to the vector v1
for(int i = 1; i <= 10; i++)
{
v1.push_back(i + 2);
}
// call the forward Iterator function
// pass the vector v1
forwardIterator(v1.begin(),v1.end());
cout << “\n\n”;
return 0;
}
4. Bidirectional Iterators in C++
Bidirectional iterators can iterate in either direction. They are considered as the forward iterators with two decrement operators because they offer the same functionality as the forward iterators, except that they can move in both directions. The containers like list, set, and multimap supports bidirectional iterators. The random access iterators are also considered valid bidirectional iterators.
Salient Features
The bidirectional iterator in C++ has the following salient features:
- Equality and Inequality operator: Two bidirectional iterators can be compared as to whether they are equal or not. Two iterators are said to be equal if both of them are pointing towards the same location. Otherwise, they are considered unequal.
In the expression shown below, consider it1 and it2 as two bidirectional iterators:
it1 == it2 //Using equality operator
it1 != it2 //Using inequality operator
- Usability: Forward iterators are used for multi-pass algorithms (the algorithms that may need the read and write operations multiple times). Therefore, bidirectional operators can also be used for multi-pass algorithms.
- Dereferencing: Since input iterators are dereferenced as rvalue and output iterators are dereferenced as an lvalue and forward iterators are the combination of these iterators, you can use both rvalue and lvalue. So, bidirectional iterators can also be used for the same purpose.
- Incrementable/Decrementable: Bidirectional iterators are valid forward iterators and forward iterators are unidirectional. But bidirectional iterators can move in either way. You can increment your iterator pointer as well as decrement it. That is why they are named bidirectional.
In the expression shown below, consider it as a bidirectional iterator:
//********Incrementing the iterator********//
it++ // Using post increment operator
++it // Using pre increment operator
//********Decrementing the iterator********//
it– // Using post decrement operator
–it // Using pre decrement operator
- Swappable: The value of the two bidirectional iterators that are pointing to different positions can be easily swapped or exchanged with each other.
Limitations
The following are some of the major limitations of the bidirectional iterators in C++:
- The offset dereference operator ([]) is not supported by the bidirectional iterators. You can not use the offset operator to dereference a bidirectional iterator.
- Just like in the forward iterators, except for equality and inequality operators, you can not implement any other relational operator on the bidirectional iterators.
- Like relational operators, arithmetic operators can not be implemented on the bidirectional iterators.
The following example will illustrate the bidirectional iterator in C++:
#include<bits/stdc++.h>
using namespace std;
int main()
{
// initialize a vector
vector<int> v1{10, 20, 30, 40, 50, 60};
// initialize an iterator i1
vector<int> ::iterator i1;
// initialize a reverse iterator rITr
vector<int> :: reverse_iterator rItr;
// dereference and print i1
// to print values in original order
cout << “Values in original order: ” << “\n”;
for(i1 = v1.begin(); i1 != v1.end(); i1++)
{
cout << *i1 << ” “;
}
cout << “\n\n”;
// dereference and print rItr
// to print values in reverse order
cout << “Values in reverse order: ” << “\n”;
for(rItr = v1.rbegin(); rItr != v1.rend(); rItr++)
{
cout << *rItr << ” “;
}
cout << “\n\n”;
return 0;
}
5. Random-Access Iterators in C++
Random Access iterators are considered to be the most completed iterators among all the five iterators. These iterators are also stated as bidirectional iterators with random access.
Salient Features
The random-access iterator in C++ has the following salient features:
- Equality and Inequality operator: Two random access iterators can be compared to see whether they are equal or not. Two iterators are said to be equal if both of them are pointing towards the same location. Otherwise, they are considered unequal.
In the expression shown below, consider it1 and it2 as two random-access iterators:
it1 == it2 //Using equality operator
it1 != it2 //Using inequality operator
- Usability: Just like bidirectional iterators, random access iterators can also be used in multi-pass algorithms.
- Dereferencing: Bidirectional iterators are dereferenced as rvalue as well as an lvalue. Therefore, random access iterators can also be used for the same purpose.
- Incremental/Decremental: Random access iterators can be incremented as well as decremented, as they are multi-directional.
In the expression shown below, consider it as a random access iterator:
//********Incrementing the iterator********//
it++ // Using post increment operator
++it // Using pre increment operator
//********Decrementing the iterator********//
it– // Using post decrement operator
–it // Using pre decrement operator
- Swappable: The value of the two random-access iterators that are pointing to different positions can be easily swapped or exchanged with each other.
- Relational Operators: Unlike other iterators, random access supports all the relational operators.
- Arithmetic Operators: Just like relational operators, arithmetic operators can be implemented on the random access iterators.
- Offset dereference operator: The offset dereference operator ([]) is supported by the random access iterators. You can use the offset operator to dereference a random-access iterator.
The following example illustrates the random-access iterator in C++:
#include<bits/stdc++.h>
using namespace std;
int main()
{
// initialize a vector
vector<int>vec1 = {10, 20, 30, 40, 50, 60};
// declare iterator i1
vector<int>::iterator i1;
// declare iterator i2
vector<int>::iterator i2;
// initialize i1 and point it to
// the first element of the vector
i1 = vec1.begin();
// initialize i2 and point it to
// the last element of the vector
i2 = vec1.end();
// compare i1 and i2 using the
// relational operator
if ( i1 < i2)
{
cout << “Iterator i2 is greater than iterator i1.”;
}
// computing the distance using
// the arithmetic operator
int difference = i2 – i1;
cout << “\nThe difference between i1 and i2 = ” << difference;
cout << “\n\n”;
return 0;
}
Operations of Iterators
1. begin():
The begin() function returns a pointer pointing to the first element of the container. This pointer can point in either direction of the container and hence it is bidirectional.
Syntax
begin()
2. end():
The end() method returns a pointer pointing to the element that comes after the last element of the container. This element is not real, it is a virtual element that contains the address of the last element.
Syntax
end()
The following program illustrates the begin() and end() operations of iterators:
#include<iostream>
#include<iterator>
#include<vector>
using namespace std;
int main()
{
// initialize a vector
vector<int> myVector = { 100, 200, 300, 400, 500 };
// declare an iterator
vector<int>::iterator itr;
// traverse the vector using the begin() and end()
cout << “The vector contains these elements: “;
// print the elements of the vector
for (itr = myVector.begin(); itr < myVector.end(); itr++) {
cout << *itr << ” “;
}
cout << “\n\n”;
return 0;
}
3. advance():
The advance() method is used to increment the iterator from its current position. It takes an integer as its argument. The advance() method increments the pointer up to that integer position.
Syntax
advance(iterator i ,int distance)
The following program illustrates the advance() operation of iterators:
#include<iostream>
#include<iterator>
#include<vector>
using namespace std;
int main()
{
// initialize a vector
vector<int> myVect = { 100, 200, 300, 400, 500 };
// declare an iterator
vector<int>::iterator itr;
// initialize the iterator
itr = myVect.begin();
// print the original value to
// which the iterator is pointing
cout << “The iterator originally points to: “;
cout << *itr << ” “;
cout << “\n\n”;
// update the iterator by incrementing it
// by a distance of 2
// using advance()
advance(itr,2); // iterator now points to 300
// print the current value to which the
// iterator is pointing
cout << “The iterator now points to : “;
cout << *itr << ” “;
cout << “\n\n”;
return 0;
}
4. next():
The next() method gives an iterator in return that points to the element you get after incrementing the iterator pointer from the current element.
Syntax
next(iterator i ,int n)
5. prev():
The prev() method is just the opposite of the next() method. It returns an iterator pointer that points to the element that you get after decrementing the iterator from the current element.
Syntax
prev(iterator i, int n)
The following program illustrates the next() and the prev() operation of iterators:
#include<iostream>
#include<iterator>
#include<vector>
using namespace std;
int main()
{
// initialize a vector
vector<int> myVect = { 100, 200, 300, 400, 500 };
// declare iterators for the vector
vector<int>::iterator i1 = myVect.begin();
vector<int>::iterator i2 = myVect.end();
// store the pointer returned by next()
// after incrementing i1 by 3
auto nextptr = next(i1, 3);
// store the pointer returned by prev()
// after decrementing i2 by 3
auto prevptr = prev(i2, 3);
// Displaying iterator position
// print the value of the new iterator returned by next()
cout << “The new pointer after using next() now points to : “;
cout << “\n”;
cout << *nextptr << ” “;
cout << “\n\n”;
// Displaying iterator position
// print the value of the new iterator returned by prev()
cout << “The new pointer after using prev() now points to : “;
cout << “\n”;
cout << *prevptr << ” “;
cout << “\n\n”;
return 0;
}
6. inserter():
The inserter() method is a special type of iterator method. It is used to insert elements at a specified position in a container. If there is already an element at the specified position, then it can also overwrite the elements in the container to insert the new elements.
Syntax
inserter (Container & x, Iterator it)
The following program illustrates the inserter() operation of iterators:
#include<bits/stdc++.h>
using namespace std;
int main () {
// initialize 2 vectors
vector<int> myVect1, myVect2;
// add elements into the vectors
for (int i = 1; i <= 5; i++) {
myVect1.push_back(i);
myVect2.push_back(i + 4);
}
// initialize an iterator itr pointing to
// the first element in vector myVect1
vector<int>::iterator itr = myVect1.begin();
// copy elements of myVect2 vector to myVect1 vector at
// the beginning
copy (myVect2.begin(), myVect2.end(), inserter(myVect1, itr));
// print the elements of the vector myVect1
cout<<“Elements of vector myVect1 after copying elements of myVect2 are :”<< endl;
for ( itr = myVect1.begin(); itr!= myVect1.end(); ++itr )
{
cout << ” ” << *itr;
}
cout << “\n\n”;
}
Benefits of Iterators
Iterators prove to be highly beneficial in certain fields. Some of these benefits are listed below:
- Convenience in programming: Iterators provide you with ease and convenience while writing the code. For instance, while using iterators, you do not have to worry about the size of the container. You can easily iterate through the container and get the last element using the end() method. You can even alter the container elements without shifting or doing all the labor.
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// initialize a vector
vector<char> v1 = {‘a’, ‘b’, ‘c’, ‘d’ };
// declare an iterator
vector<char>::iterator itr;
// access vector elements without iterator
cout << “Traversing without iterator : “;
for (int j = 0; j < 4; ++j) {
cout << v1[j] << ” “;
}
cout << “\n”;
// access vector elements using an iterator
cout << “Traversing using iterator “;
for (itr = v1.begin(); itr != v1.end(); ++itr) {
cout << *itr << ” “;
}
cout << “\n\n”;
// insert an element into the vector
v1.push_back(‘e’);
// access vector elements without iterator
cout << “Traversing without iterator : “;
for (int j = 0; j < 5; ++j) {
cout << v1[j] << ” “;
}
cout << “\n”;
// access vector elements using an iterator
cout << “Traversing using iterator “;
for (itr = v1.begin(); itr != v1.end(); ++itr) {
cout << *itr << ” “;
}
cout << “\n\n”;
return 0;
}
In the code mentioned above, you did not store or calculate the initial size of the container. You simply iterated over the container with the help of an iterator. Then you added an element to the container. This time, you did not calculate the size either.
You simply iterated over the container. In both cases, you used the same loop and therefore the iterator eased out your work.
- Code Reusability: Now, consider the above code. If you want to change the vector into a list without changing the code, this would not be possible without an iterator. But since you are using an iterator here, you can just change the declaration of the vector into a list and it will work fine. In this way, you can reuse your code.
- Dynamic Processing: With the help of iterators, you can dynamically allocate as well as delete the memory. This dynamic processing of containers prevents the wastage of memory. And you can easily alter the size of the containers according to your needs and requirements.
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// initialize a vector
vector<int> v = { 10, 20, 30 };
// declare an iterator
vector<int>::iterator i;
// add elements into vector using the iterator
for (i = v.begin(); i != v.end(); ++i) {
if (i == v.begin()) {
// add 100 at the beginning
i = v.insert(i, 100);
}
}
// vect -> 100 10 20 30
// delete an element using the iterator
for (i = v.begin(); i != v.end(); ++i) {
if (i == v.begin() + 1) {
// delete 2nd element, i.e. at index 1
i = v.erase(i);
}
}
cout << “Vector after performing all operations: “;
cout << “\n”;
// vect -> 100 20 30
// Traversing the vector using the iterator
for (i = v.begin(); i != v.end(); ++i) {
cout << *i << ” “;
}
cout << “\n\n”;
return 0;
}
In the code mentioned above, it is clearly shown how you can dynamically add and remove elements from a container with the help of iterators. However, if you try to achieve this dynamic processing of containers without iterators, it would be very laborious, as you have to shift the elements every time.
Disadvantages of Iterators
In the above section, you saw how beneficial iterators are. But there are some drawbacks of using iterators too. Some of these disadvantages are mentioned below:
- Iterators do not allow you to handle two data structures simultaneously, especially when the data of the first data structure decides your location in the other one.
- You can not go back while iterating through a container, due to the working process of iterators.
- The structure of the STL container cannot be updated while traversing it if you are using an iterator to iterate over it.
Providers of Iterators
The following table represents iterators along with their provider containers.
ITERATOR |
PROVIDER |
Input Iterator |
istream |
Output Iterator |
ostream |
Forward iterator |
– |
Bidirectional iterator |
List, set, map, multimap, multiset |
Random access iterator |
Vector, array, deque |
Iterators and Their Characteristics
Iterators have the characteristics to access, read, write, and iterate over the container elements. However, not all iterators possess all characteristics. The following table represents the iterators along with the characteristics possessed by them.
ITERATORS |
CHARACTERISTICS |
|||
ACCESS |
READ |
WRITE |
ITERATE |
|
Input |
-> |
= *i |
++ |
|
Output |
*i= |
++ |
||
Forward |
-> |
= *i |
*i= |
++ |
Bidirectional |
= *i |
*i= |
++, – – |
|
Random Access |
->, [ ] |
= *i |
*i= |
++, – – |
Difference Between Random Access Iterator and Other Iterators
A Random Access iterator differs from the other iterators significantly and also overcomes some limitations faced with other iterators. The key differences between the random access iterator and the other iterators are discussed below:
OTHER ITERATORS |
RANDOM ACCESS ITERATORS |
|
Input |
Input iterators are only accessible. They can only read the data of the location to which the pointer points, they can not be used to assign the values. |
Random Access iterators can be used to assign the values. |
Output |
You can not access the values using output iterators. These iterators can only be used to assign the values. |
You can access the values of the container using this iterator. |
Forward |
Forward iterators are unidirectional. They only move in a forward direction i.e., they can only be incremented. You cannot decrement them. |
These iterators are bidirectional. You can move forward as well as backward using these iterators. |
Bidirectional |
The offset dereference operator ([]) is not supported by the bidirectional iterators. You can not use the offset operator to dereference a bidirectional iterator. |
These iterators support the use of the offset dereferences operator for dereferencing the variables. |
Except for equality and inequality operators, you can not implement any other relational operators on the bidirectional iterators. |
You can implement every relational operator on random access iterators. |
|
Like relational operators, arithmetic operators can not be implemented on the bidirectional iterators. |
Unlike bidirectional operators, you can implement relational operators on these iterators. |
Advance your career as a MEAN stack developer with the Full Stack Web Developer – MEAN Stack Master’s Program. Enroll now!
Wrapping Up!
In this article, you have learned a lot about Iterators in C++. This article talked about different categories of iterators as well as their use cases. You then dived deep into the advantages as well as disadvantages of the iterators. You also explored how each iterator is beneficial in its own way.
To better understand the entire C++ programming language, you can go through our guide on C++ Programming for Beginners.
Don’t just stop here. To learn Full-stack Development and to give yourself a chance to work for top tech giants, check out our course on Full Stack Web Development. In this course, you will learn the complete end-to-end technologies/skills that will help you to set your foot into professional web development. These include Java, DevOps, Agility, HTML, AWS, etc.
Also, check out the complete list of free online courses by Simplilearn.
If you have any questions for us, please mention them in the comments section and we will have our experts answer them for you.
Happy Learning!