TeqStudy ยท C++ Track ยท Phase 07

The STL

vector, map, and set โ€” the containers that replace manual arrays and pointer-based memory management for everyday use โ€” plus iterators, algorithms, and lambda expressions.

๐Ÿ“ฆ ๐Ÿ“š ๐Ÿ—บ๏ธ ๐ŸŽฏ ๐Ÿ
Course Progress
0%
MODULE 01

๐Ÿ“ฆ Why the STL Exists

Phase 3's arrays and Phase 4's manual dynamic memory both work, but both have genuine sharp edges. The Standard Template Library exists specifically to solve them.

๐Ÿ˜– The Problems You've Already Run Into
  • Fixed-size arrays can't grow โ€” you must know the size in advance (Phase 3 Module 1).
  • Manual dynamic arrays (Phase 4's capstone) require you to hand-write growing/shrinking logic yourself, correctly, every time.
  • Neither one tracks its OWN current size safely โ€” you're always responsible for passing it around separately (Phase 4 Module 3).
  • Common tasks like sorting or searching need to be hand-written from scratch, every single time, in every project.
๐Ÿ“ฆ The STL โ€” Battle-Tested, Ready-Made Tools

The Standard Template Library is a genuinely huge part of what makes modern C++ practical for real work โ€” a collection of ready-made, extensively tested data structures (containers) and functions (algorithms) that solve exactly these recurring problems, so you don't have to keep reinventing them.

STL PieceSolves
std::vector (Module 2)A resizable array that manages its own memory and size
std::map (Module 4)Looking things up by a meaningful key, not just a numeric index
std::set (Module 5)Storing unique values automatically
Algorithms like std::sort (Module 7)Common operations, correctly implemented once, reusable everywhere
๐Ÿ’ก
This doesn't waste Phase 3 and 4's workUnderstanding arrays and manual memory management first is exactly why the STL will make genuine sense to you now โ€” you understand precisely what problem each container is solving, rather than just memorizing syntax. Real C++ code also still uses arrays and manual pointers in specific situations, so that foundation stays genuinely useful going forward.
๐Ÿง  Quick Check
What core problem does the STL's std::vector solve, compared to a plain array?
MODULE 02

๐Ÿ“š std::vector โ€” A Resizable Array

The single most commonly used STL container, and the direct upgrade from everything Phase 3 taught about arrays.

๐Ÿ†š Array vs. vector, Side by Side
C++
#include <vector>

int scores[5] = {85, 92, 78, 95, 88};  // fixed size, forever

std::vector<int> scoresVec = {85, 92, 78, 95, 88};  // can grow!
scoresVec.push_back(100);  // adds a 6th element, automatically resizing

std::vector<int> reads as "a vector holding int values" โ€” the type inside <> is what the vector stores. Accessing elements works EXACTLY like arrays (scoresVec[0], still zero-indexed, Phase 3 Module 1), but the size can genuinely change while your program runs.

โž• Adding and Removing Elements
MethodWhat It Does
.push_back(value)Adds a new element to the end
.pop_back()Removes the last element
.size()Returns the current number of elements โ€” always accurate
.empty()Returns true if the vector has zero elements
๐Ÿ’ก
No more sizeof tricks, no more manual growArray functions.size() directly replaces Phase 3 Module 2's manual sizeof calculation, and .push_back() replaces the ENTIRE manual grow-copy-delete pattern from Phase 4's capstone โ€” the vector handles all of that internally, correctly, every time.
๐Ÿง  Quick Check
What does calling .push_back(100) on a vector actually do?
MODULE 03

๐Ÿ” vector Methods & Iteration

A modern, genuinely cleaner way to loop through a vector โ€” plus the rest of the everyday toolkit.

๐Ÿ†• The Range-Based for Loop
C++
std::vector<int> scores = {85, 92, 78};

// Phase 2's classic way still works fine:
for (int i = 0; i < scores.size(); i++) {
    std::cout << scores[i] << " ";
}

// The modern, range-based way โ€” reads directly as "for each score in scores"
for (int score : scores) {
    std::cout << score << " ";
}

The range-based for loop reads naturally, avoids off-by-one errors entirely (Phase 2 Module 6), and is now the preferred style in modern C++ whenever you don't specifically need the index itself.

โœ๏ธ Modifying Elements While Iterating
C++
for (int& score : scores) {   // note the &
    score += 5;   // actually modifies the original vector's elements
}

Just like Phase 3 Module 7's pass-by-reference, adding & in the range-based loop lets you modify the ACTUAL elements, not just a local copy โ€” without &, changes to score inside the loop wouldn't affect the real vector at all.

๐Ÿง  Quick Check
In for (int score : scores), without the & symbol, what happens if you modify score inside the loop body?
MODULE 04

๐Ÿ—บ๏ธ std::map โ€” Key-Value Pairs

A vector looks things up by numeric position. A map looks things up by a meaningful key โ€” a name, a word, an ID โ€” instead.

๐Ÿ”‘ Looking Up by Key, Not Position
C++
#include <map>

std::map<std::string, int> ages;
ages["Alex"] = 25;
ages["Jordan"] = 30;

std::cout << ages["Alex"];   // prints 25 โ€” looked up by name, not position

std::map<std::string, int> reads as "a map from string keys to int values." Instead of vector[0] meaning "the first element," ages["Alex"] means "the value stored under the key 'Alex'" โ€” genuinely different lookup logic, suited to genuinely different problems.

๐Ÿ” Checking If a Key Exists
C++
if (ages.find("Alex") != ages.end()) {
    std::cout << "Found Alex's age.";
}
// or, more directly:
if (ages.count("Alex") > 0) {
    std::cout << "Found Alex's age.";
}
โš ๏ธ
A genuine trap โ€” accessing a missing key creates itUsing ages["Sam"] to check whether "Sam" exists, before you know it's actually there, SILENTLY CREATES a new entry for "Sam" with a default value (0 for int) if it wasn't already present. Always check with .find() or .count() first if you're not certain the key exists โ€” a real, common, sneaky bug otherwise.
๐Ÿง  Quick Check
If you access ages["Sam"] on a map that doesn't yet have a "Sam" key, what happens?
MODULE 05

๐ŸŽฏ std::set โ€” Unique, Sorted Values

A container that automatically enforces two rules for you: no duplicates, and always sorted.

๐Ÿšซ Duplicates Are Automatically Rejected
C++
#include <set>

std::set<int> uniqueNumbers;
uniqueNumbers.insert(5);
uniqueNumbers.insert(3);
uniqueNumbers.insert(5);   // silently ignored โ€” 5 is already in the set

// uniqueNumbers now contains exactly: 3, 5 (and always in sorted order)

Attempting to insert a value that's already present is a harmless no-op โ€” the set just stays the same. This makes std::set a genuinely convenient tool whenever you need to guarantee "no duplicates" without writing that checking logic yourself.

๐Ÿ“Š Always Automatically Sorted

A std::set maintains its elements in sorted order at all times, automatically โ€” you never insert values "out of order" from the set's perspective, since it always reorganizes itself internally. This differs genuinely from std::vector, which preserves whatever order you actually insert things in.

๐ŸŽฏ When to Reach for set vs. vector
SituationBetter Fit
Order of insertion matters, duplicates are finestd::vector
You need guaranteed uniqueness and/or sorted orderstd::set
๐Ÿง  Quick Check
If you insert the value 5 into a std::set that already contains 5, what happens?
MODULE 06

โžก๏ธ Iterators

The range-based for loop from Module 3 is actually built on top of a more fundamental concept, worth understanding directly โ€” especially since some STL operations genuinely need it.

โžก๏ธ A Pointer-Like Way to Walk Through a Container
C++
std::vector<int> scores = {85, 92, 78};

for (std::vector<int>::iterator it = scores.begin(); it != scores.end(); it++) {
    std::cout << *it << " ";   // dereference it, just like a pointer!
}

An iterator is genuinely similar to Phase 4's pointers โ€” *it dereferences it to get the current value, and it++ moves it forward, exactly like Phase 4 Module 4's pointer arithmetic. .begin() gives an iterator to the first element; .end() gives one representing "just past the last element" โ€” the loop runs until it reaches that end marker.

๐ŸŽฏ Why Iterators Matter Beyond Just Looping

Many STL algorithms (Module 7) and container operations โ€” like erasing a specific element from the middle of a vector, or specifying WHERE in a container to insert something โ€” take iterators as arguments, not raw index numbers. Understanding iterators as "a generalized, safer pointer for walking through any container" is what makes those functions' syntax make sense.

C++
std::vector<int> scores = {85, 92, 78};
scores.erase(scores.begin() + 1);   // removes the element at position 1 (92)
๐Ÿง  Quick Check
What does an iterator's .begin() and .end() actually represent?
MODULE 07

๐Ÿงฎ Common Algorithms

Sorting and searching, correctly implemented once by the standard library โ€” no more hand-writing these from scratch.

๐Ÿ”€ std::sort
C++
#include <algorithm>

std::vector<int> scores = {85, 92, 78, 95};
std::sort(scores.begin(), scores.end());
// scores is now: 78, 85, 92, 95

std::sort takes a range โ€” Module 5's iterators, marking the start and end โ€” and sorts everything in between, in place. This single line replaces having to hand-write and debug a sorting algorithm yourself.

๐Ÿ” std::find
C++
auto result = std::find(scores.begin(), scores.end(), 92);
if (result != scores.end()) {
    std::cout << "Found 92!";
} else {
    std::cout << "Not found.";
}

std::find returns an iterator pointing to the first matching element โ€” or .end() if nothing matched, exactly the same "check against end()" pattern from Module 5. auto here just lets the compiler figure out the exact iterator type automatically, saving you from writing it out by hand.

๐Ÿง  Quick Check
What does std::find return if the value you're searching for isn't present in the range?
MODULE 08

ฮป Lambda Expressions

Small, unnamed functions written right where you need them โ€” genuinely useful for customizing how algorithms like sort() actually behave.

ฮป A Function Without a Name
C++
auto square = [](int x) {
    return x * x;
};

std::cout << square(5);   // prints 25

A lambda is a small function written inline, without needing a separate name or declaration the way Phase 3's functions did. [] starts it, (int x) is the parameter list โ€” genuinely identical in spirit to a regular function's parameters.

๐ŸŽฏ The Real, Common Use โ€” Customizing sort()
C++
std::vector<int> scores = {85, 92, 78};

// sort in DESCENDING order, using a lambda to define "what comes first"
std::sort(scores.begin(), scores.end(), [](int a, int b) {
    return a > b;   // "a should come before b if a is bigger"
});
// scores is now: 92, 85, 78

Module 6's std::sort normally sorts smallest-to-largest by default. Passing a lambda as a third argument lets you define your OWN comparison rule โ€” here, "descending" โ€” without needing to write and name an entire separate function just for this one sort call.

๐Ÿง  Quick Check
What is a lambda expression, in plain terms?
MODULE 09

๐Ÿ‘ซ std::pair & Structured Data

A small, genuinely useful STL tool for bundling exactly two related values together โ€” and a quick look at what a map's contents actually are, under the hood.

๐Ÿ‘ซ Bundling Two Values Together
C++
#include <utility>

std::pair<std::string, int> person = {"Alex", 25};
std::cout << person.first;    // "Alex"
std::cout << person.second;   // 25

std::pair holds exactly two values, accessed via .first and .second โ€” useful for functions that need to return two related pieces of information at once, without needing to design an entire custom class just for that.

๐Ÿ—บ๏ธ Module 4's std::map Is Genuinely Built From Pairs
C++
std::map<std::string, int> ages = {{"Alex", 25}, {"Jordan", 30}};

for (const auto& entry : ages) {
    std::cout << entry.first << ": " << entry.second << std::endl;
    // entry.first is the key, entry.second is the value
}

Looping through a std::map gives you a sequence of pairs โ€” entry.first is the key, entry.second is the value. Recognizing this connects Module 4's map directly back to this lesson's std::pair, rather than treating it as unrelated new syntax.

๐Ÿง  Quick Check
When looping through a std::map, what do .first and .second on each entry represent?
MODULE 10 ๐Ÿ

๐Ÿ Capstone โ€” Simple Inventory System

Every idea from this phase, combined: a map for lookups by name, a vector for order-preserving history, sort with a lambda, and clean STL-based iteration throughout.

๐ŸŽฏ The Goal

Build a small inventory tracker: a map from item name to quantity, functions to add/remove stock with validation, and a report that lists all items sorted by quantity, highest first.

๐Ÿ’ป A Reference Solution
main.cpp
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>

std::map<std::string, int> inventory;

void addStock(std::string item, int qty) {
    if (qty > 0) {
        inventory[item] += qty;
    }
}

bool removeStock(std::string item, int qty) {
    if (inventory.count(item) == 0 || inventory[item] < qty) {
        std::cout << "Not enough stock of " << item << std::endl;
        return false;
    }
    inventory[item] -= qty;
    return true;
}

void printReport() {
    std::vector<std::pair<std::string, int>> items(inventory.begin(), inventory.end());

    std::sort(items.begin(), items.end(), [](auto& a, auto& b) {
        return a.second > b.second;   // highest quantity first
    });

    for (const auto& item : items) {
        std::cout << item.first << ": " << item.second << std::endl;
    }
}

int main() {
    addStock("Widgets", 50);
    addStock("Gadgets", 120);
    addStock("Gizmos", 30);
    removeStock("Widgets", 10);

    printReport();
    return 0;
}
๐Ÿ” Why It's Built This Way
  • inventory[item] += qty in addStock takes advantage of Module 4's exact map-access behavior โ€” it creates the entry (defaulting to 0) if new, or adds to the existing value.
  • removeStock validates BEFORE modifying anything (Phase 5 Module 6's encapsulation principle, applied here without a formal class), and returns a bool so the caller knows if it actually worked.
  • printReport copies the map's pairs into a vector specifically because std::map is always sorted BY KEY automatically โ€” to sort by VALUE instead, you need a vector plus Module 7's std::sort with a custom lambda.
๐Ÿ Extend It Yourself

Add a Low-Stock Alert

Using Module 6's std::find pattern or a simple loop, write a function that returns a std::vector<std::string> of every item with quantity below some threshold (say, 20). This combines the map, vector, and validation patterns from this entire capstone into one new, self-contained piece.

Roadmap