[转载]C++中的迭代器

Iterators in C++: An Ultimate Guide to Iterators

from@ Iterators in C++: An Ultimate Guide to Iterators (simplilearn.com)

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;

}

Want a Top Software Development Job? Start Here!

Full Stack Development-MEANExplore Program

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.

Want a Top Software Development Job? Start Here!

Full Stack Development-MEANExplore Program

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";

}

Want a Top Software Development Job? Start Here!

Full Stack Development-MEANExplore Program

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;

}

Iterator_in_C_Plus_Plus_5

Want a Top Software Development Job? Start Here!

Full Stack Development-MEANExplore Program

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;

}

Iterator_in_C_Plus_Plus_6

Want a Top Software Development Job? Start Here!

Full Stack Development-MEANExplore Program

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;

}

Iterator_in_C_Plus_Plus_7

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

Want a Top Software Development Job? Start Here!

Full Stack Development-MEANExplore Program

Want a Top Software Development Job? Start Here!

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.

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:

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 Post Graduate Program in 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!