Iterators are generalized pointer wrappers that allow you to traverse and manipulate elements inside standard containers. While they look like pointers, iterators must follow container safety rules. In this lesson, we will examine iterators and look at one of C++'s most dangerous undefined behavior bugs: iterator invalidation.
Let's look at iterator syntax and container modification traps.
Iterators: Pointer Wrappers
An iterator holds the address of an element inside a container and supports operators like ++ to advance, -- to step back, and to dereference. Every standard container provides begin() (pointing to the first element) and end() (pointing to the memory slot after* the last element):
Under the hood, a range based for loop (like for (int x : vec)) is translated by the compiler into an iterator loop identical to the code above.
The Critical Hazard: Iterator Invalidation
If you store an iterator pointing to an element in a container and then modify that container, the iterator can become invalidated. If you attempt to dereference or increment an invalidated iterator, your program will crash or corrupt memory.
Let's look at the classic vector invalidation trap: pushing elements to a vector while looping over it:
When the vector reallocates its capacity, it deletes the old heap memory block. The iterator it continues to hold the old address, becoming a dangling pointer. To prevent this, never modify the size of a container inside loops that rely on standard iterators, or use the iterator values returned by insertion methods to reset your position.