Iterators in C++ are objects that provide a way to access elements of containers (like arrays, vectors, lists, etc.) sequentially without exposing the underlying representation of the container. They act as a bridge between algorithms and containers, allowing for generic programming.
This example demonstrates how to use iterators with various STL containers.
#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <algorithm>
template<typename Container>
void printContainer(const Container& c, const std::string& name) {
std::cout << name << ": ";
for (auto it = c.begin(); it != c.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
}
int main() {
// Vector
std::vector<int> vec = {1, 2, 3, 4, 5};
printContainer(vec, "Vector");
// Modify vector using iterator
for (auto it = vec.begin(); it != vec.end(); ++it) {
*it *= 2;
}
printContainer(vec, "Modified Vector");
// List
std::list<int> lst = {10, 20, 30, 40, 50};
printContainer(lst, "List");
// Set
std::set<int> st = {5, 2, 8, 1, 9};
printContainer(st, "Set");
// Using std::find with iterator
auto it = std::find(vec.begin(), vec.end(), 6);
if (it != vec.end()) {
std::cout << "Found 6 at position: " << std::distance(vec.begin(), it) << std::endl;
}
// Reverse iterator
std::cout << "Vector in reverse: ";
for (auto rit = vec.rbegin(); rit != vec.rend(); ++rit) {
std::cout << *rit << " ";
}
std::cout << std::endl;
return 0;
}
printContainer
function demonstrates how to use iterators generically to traverse any container.std::find
algorithm with iterators.rbegin()
and rend()
) are demonstrated, showing how to traverse a container in reverse order.This example implements a custom iterator for an in-order traversal of a binary tree, demonstrating how to create your own iterators for custom data structures.
#include <iostream>
#include <stack>
#include <iterator>
template<typename T>
class BinaryTree {
private:
struct Node {
T data;
Node* left;
Node* right;
Node(const T& val) : data(val), left(nullptr), right(nullptr) {}
};
Node* root;
public:
BinaryTree() : root(nullptr) {}
void insert(const T& value) {
root = insertRec(root, value);
}
class Iterator {
private:
std::stack<Node*> stack;
void pushLeft(Node* node) {
while (node) {
stack.push(node);
node = node->left;
}
}
public:
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
Iterator(Node* root) {
pushLeft(root);
}
Iterator& operator++() {
Node* node = stack.top();
stack.pop();
pushLeft(node->right);
return *this;
}
Iterator operator++(int) {
Iterator tmp = *this;
++(*this);
return tmp;
}
bool operator==(const Iterator& other) const {
return (stack.empty() && other.stack.empty()) ||
(!stack.empty() && !other.stack.empty() && stack.top() == other.stack.top());
}
bool operator!=(const Iterator& other) const {
return !(*this == other);
}
T& operator*() {
return stack.top()->data;
}
};
Iterator begin() {
return Iterator(root);
}
Iterator end() {
return Iterator(nullptr);
}
private:
Node* insertRec(Node* node, const T& value) {
if (node == nullptr) {
return new Node(value);
}
if (value < node->data) {
node->left = insertRec(node->left, value);
} else if (value > node->data) {
node->right = insertRec(node->right, value);
}
return node;
}
};
int main() {
BinaryTree<int> tree;
tree.insert(5);
tree.insert(3);
tree.insert(7);
tree.insert(1);
tree.insert(9);
std::cout << "In-order traversal: ";
for (const auto& value : tree) {
std::cout << value << " ";
}
std::cout << std::endl;
// Using the iterator with STL algorithms
auto minElement = std::min_element(tree.begin(), tree.end());
auto maxElement = std::max_element(tree.begin(), tree.end());
std::cout << "Min element: " << *minElement << std::endl;
std::cout << "Max element: " << *maxElement << std::endl;
return 0;
}
Iterator
class is defined within the BinaryTree
class and provides the necessary typedefs and operations required for a forward iterator.begin()
and end()
methods of the BinaryTree
class return appropriate iterators.main()
function, we demonstrate how this custom iterator can be used in a range-based for loop and with STL algorithms like std::min_element
and std::max_element
.Iterator Categories: Understand the different iterator categories (Input, Output, Forward, Bidirectional, Random Access) and their capabilities.
Iterator Invalidation: Be aware of operations that can invalidate iterators, such as resizing a vector or modifying a container while iterating.
Const Iterators: Use const_iterator
when you don't need to modify the elements being iterated over.
Stream Iterators: Familiarize yourself with stream iterators (istream_iterator
, ostream_iterator
) for I/O operations.
Iterator Adapters: Learn about iterator adapters like reverse_iterator
, back_insert_iterator
, etc.
Iterators are a fundamental concept in C++ STL, providing a uniform way to access elements in containers and enabling generic programming. They are crucial for writing efficient and reusable code, especially in scientific computing and AI applications where data manipulation is common.
In this guide, we explored two main aspects of iterators:
These examples highlight the power and versatility of iterators in C++. The first example demonstrates how iterators provide a consistent interface for working with different container types, while the second shows how custom iterators can be implemented to work seamlessly with STL algorithms.
Iterators are particularly relevant to your interests in scientific computing and artificial intelligence. In scientific computing, they allow for efficient traversal and manipulation of large datasets stored in various container types. In AI applications, iterators can be used to implement custom data structures for machine learning models or to efficiently process input data.
Understanding and effectively using iterators can significantly enhance your ability to write generic, efficient, and maintainable C++ code, which is crucial in both scientific computing and AI development.
Previous Page | Course Schedule | Course Content