C++ vectors and iterator invalidation

Grow a dynamic array and use checked access.

C++
#include <iostream>
#include <vector>
#include <cassert>
int main() {
    std::vector<int> values{2, 4};
    values.push_back(6);
    values.at(1) = 5;
    for (int value : values) std::cout << value << '\n';
    assert(values.size() == 3);
}
Output
2
5
6

Explanation

A vector stores elements contiguously and grows as needed. push_back appends; at performs bounds checking and throws out_of_range for an invalid index.

Growth can reallocate the underlying storage, invalidating pointers, references, and iterators into it. This program reads elements only after appending.

Explore the dry run
Guided walkthroughReady
1#include <iostream>
2#include <vector>
3#include <cassert>
4int main() {
5 std::vector<int> values{2, 4};
6 values.push_back(6);
7 values.at(1) = 5;
8 for (int value : values) std::cout << value << '\n';
9 assert(values.size() == 3);
10}

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
Construct two elements.values = [2, 4]
—
Append, potentially reallocating storage.values = [2, 4, 6]
—
Replace the second element through checked access.values = [2, 5, 6]
—
Visit and print all three elements.values = [2, 5, 6]
2
5
6

Cost and common mistakes

O(1) indexed access; amortized O(1) append, O(n) if append reallocates; O(n) traversal and storage.

  • operator[] does not provide the bounds check of at.
  • Do not retain an iterator across an operation that invalidates it.

Try it yourself

What does values.at(3) do after these operations?

Reveal answer and reasoning

It throws std::out_of_range because valid indices are 0, 1, and 2.

Further reading