class: center, middle # CMPE 30: Lecture 12 Mutation and Iteration --- # A vector has size 3 and capacity 8. How many more push_backs before it reallocates? 1. 3 1. 5 1. 7 1. 8 1. Ben got this wrong --- # Learning Objectives - Use `insert` and `erase` to modify a vector at any position - Preallocate with `reserve` and shrink with `shrink_to_fit` - Iterate with range-based `for` using `const auto&` and `auto&` - Use iterators and `.begin()` / `.end()` - Recognize iterator invalidation after insert/erase - Call `
` functions (`sort`, `count`, `find_if`, `min_element`) on iterator ranges --- # Insert and Erase .lc[ ```cpp std::vector
lista = {"Creep", "No Rain", "Linger"}; lista.insert( lista.begin() + 1, "Possum Kingdom"); lista.erase(lista.begin()); ``` ] .rc[ - Position is an **iterator** --- `begin() + n` means "index n" - `insert` places the new element **before** the given position - `erase` removes one (or a range) ] --- # Why Insert/Erase Are Slower - Inserting in the middle shifts every following element over - Erasing shifts every following element back - The more elements after the position, the more work - Prefer `push_back`/`pop_back` when you can **Trap:** insert/erase **invalidate** iterators, pointers, and references. Get fresh ones afterward. --- # reserve and shrink_to_fit .lc[ ```cpp std::vector
v; v.reserve(1000); // v.size() == 0 // v.capacity() >= 1000 // later: v.shrink_to_fit(); ``` ] .rc[ - `reserve(n)` preallocates - Avoid repeated reallocations when you know the size - `shrink_to_fit` is a **non-binding** request --- the implementation may ignore it ] --- # Range-Based for Loop .lc[ ```cpp std::vector
songs = {"Wannabe", "A Little Respect"}; for (const auto& song : songs) { std::cout << song << "\n"; } ``` ] .rc[ - `auto` --- let the compiler deduce the type - `&` --- reference, no copy - `const` --- promise not to modify ] --- # Modifying Elements .lc[ ```cpp std::vector
values = {1, 2, 3, 4, 5}; for (auto& v : values) { v *= 10; } // {10, 20, 30, 40, 50} ``` ] .rc[ - Drop `const` when you need to modify - Keep the `&` to avoid copies **Tip:** Prefer `const auto&` to read. Use `auto&` to modify. **Avoid** plain `auto` for anything larger than a primitive. ] --- # Iterators .lc[ ```cpp std::vector
canciones = {"Wannabe", "A Little Respect"}; for (auto it = canciones.begin(); it != canciones.end(); ++it) { std::cout << *it << "\n"; } ``` ] .rc[ - `.begin()` --- iterator to the first element - `.end()` --- iterator **one past** the last - `*it` dereferences (like a pointer) - `++it` advances ] --- # The Half-Open Interval .lc[ ```cpp for (auto it = v.begin(); it != v.end(); ++it) { // use *it } ``` ] .rc[ **Wut:** `.end()` points **past** the last element, not at it. - Valid range is `[begin, end)` - Makes loops cleaner and avoids off-by-one errors - Common C++ convention you will see everywhere ] --- # auto with Iterators .lc[ ```cpp // without auto std::vector
::iterator it = canciones.begin(); // with auto auto it = canciones.begin(); ``` ] .rc[ - `auto` shines with iterators - Iterator types are long and obvious from context - One of the best uses of `auto` ] --- # Why Use Iterators Directly? - Standard library algorithms require them (`std::sort`, `std::find`, ...) - You can iterate backward (`rbegin`/`rend`) - More control --- like erasing elements while iterating (done carefully) - Containers can be built from iterator ranges --- `std::vector
mid(all.begin() + 1, all.begin() + 4);` - Range-based `for` is a convenience wrapper over iterators --- # Standard Algorithms - `
` is full of functions that work on `[begin, end)` iterator ranges - `std::sort`, `std::find`, `std::count`, `std::min_element`, `std::max_element`, ... - Less code, fewer bugs, usually faster than a hand-rolled loop - Free `std::begin(c)` / `std::end(c)` work on any container **and** raw arrays --- # Algorithms in Action .lc[ ```cpp std::vector
rankings = {7, 2, 9, 4, 2, 8, 2, 5}; std::sort(rankings.begin(), rankings.end()); // {2, 2, 2, 4, 5, 7, 8, 9} auto twos = std::count( rankings.begin(), rankings.end(), 2); auto lo = *std::min_element( rankings.begin(), rankings.end()); ``` ] .rc[ - `sort` rearranges in place - `count` returns how many equal 2 (here: 3) - `min_element` returns an **iterator** to the smallest --- `*` to get the value (here: 2) ] --- # Lambdas and find_if .lc[ ```cpp // a lambda: a tiny unnamed function auto big = std::find_if( rankings.begin(), rankings.end(), [](int n) { return n > 5; }); if (big != rankings.end()) { std::cout << *big << "\n"; // 7 } ``` ] .rc[ - `[](int n) { return n > 5; }` --- capture list, parameter, body - The algorithm calls it on each element **Wut:** `std::find`/`find_if` return `end()` when nothing matches --- check before dereferencing. ] --- # Try It .lc[ ```cpp std::vector
nums = {1, 2, 3, 4, 5}; for (auto& n : nums) { n *= 2; } for (const auto& n : nums) { std::cout << n << " "; } std::cout << "\n"; ``` ] .rc[ - Predict the output, then run it - Rewrite the loops with explicit iterators ] --- # What does this print? .lc[ ```cpp std::vector
v = {10, 20, 30, 40, 50}; v.insert(v.begin() + 2, 25); v.erase(v.begin()); for (const auto& n : v) std::cout << n << " "; ``` ] .rc[ 1. `10 20 25 30 40 50` 1. `20 25 30 40 50` 1. `20 25 30 40` 1. `10 25 30 40 50` 1. Ben got this wrong ] --- # Where is the bug? .lc[ ```cpp std::vector
scores = {95, 87, 91}; for (int i = 0; i <= scores.size(); i++) { std::cout << scores[i] << "\n"; } ``` ] .rc[ 1. `scores` cannot be iterated 1. `[]` does not work on `std::vector` 1. `<=` should be `<` --- reads one past the end 1. `i++` should be `++i` 1. Ben got this wrong ] --- # Why is for (auto x : vec) wrong for vector
? 1. It does not compile 1. It **copies** every string on each iteration 1. It modifies the original vector 1. It only works for `vector
` 1. Ben got this wrong --- # Key Points - `insert`/`erase` shift elements; prefer end operations - Insert/erase **invalidate** iterators - Range-based `for` with `const auto&` is the default - `.end()` is **one past** the last; `[begin, end)` half-open interval - `auto` saves you from verbose iterator types - `
`: `sort`, `find`, `count`, `min_element` --- prefer them over hand-rolled loops - `find`/`find_if` return `end()` when nothing matches **Read:** chapter 9 of *Gorgo Starting C++* (I/O streams). **Do:** exercises 1-12.