C++ Programming Tutorial

Learn modern C++ step-by-step — from basics to advanced features like templates, STL, and smart pointers. Clear explanations with practical, runnable examples.

C++ Standard Template Library (STL)

The STL provides a rich collection of containers, algorithms, and iterators that work together seamlessly. Using the STL means writing less code, fewer bugs, and better performance than hand-rolled data structures.

Sequence Containers

sequence.cpp
#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <array>

int main() {
    // vector — dynamic array (most used container)
    std::vector<int> vec{1, 2, 3, 4, 5};
    vec.push_back(6);
    vec.pop_back();
    std::cout << "vec size: " << vec.size() << "
";

    // deque — double-ended queue (fast insert at both ends)
    std::deque<int> dq{10, 20, 30};
    dq.push_front(5);
    dq.push_back(40);

    // list — doubly linked list (fast insert/remove anywhere)
    std::list<int> lst{1, 2, 3};
    lst.push_front(0);
    lst.remove(2);  // remove all elements with value 2

    // array — fixed-size (compile-time size)
    std::array<int, 4> arr{10, 20, 30, 40};
    std::cout << "arr[2]: " << arr[2] << "
";

    return 0;
}

Associative Containers

associative.cpp
#include <iostream>
#include <map>
#include <unordered_map>
#include <set>

int main() {
    // map — ordered key-value pairs (red-black tree, O(log n))
    std::map<std::string, int> ages;
    ages["Alice"] = 30;
    ages["Bob"] = 25;
    ages.insert({"Charlie", 35});

    for (const auto& [name, age] : ages) {  // structured bindings (C++17)
        std::cout << name << ": " << age << "
";
    }  // sorted by key: Alice, Bob, Charlie

    // Check if key exists
    if (ages.count("Alice")) {
        std::cout << "Alice found: " << ages["Alice"] << "
";
    }

    // unordered_map — hash table (O(1) average)
    std::unordered_map<std::string, double> prices;
    prices["apple"] = 1.50;
    prices["banana"] = 0.75;

    // set — unique sorted values
    std::set<int> unique_nums{5, 3, 1, 4, 1, 3};  // stores: {1, 3, 4, 5}
    for (int n : unique_nums) std::cout << n << " ";
    std::cout << "
";

    return 0;
}

Algorithms

algorithms.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>

int main() {
    std::vector<int> v{5, 2, 8, 1, 9, 3, 7, 4, 6};

    // Sort
    std::sort(v.begin(), v.end());  // ascending
    // std::sort(v.begin(), v.end(), std::greater<>());  // descending

    // Find
    auto it = std::find(v.begin(), v.end(), 7);
    if (it != v.end()) {
        std::cout << "Found 7 at index " << (it - v.begin()) << "
";
    }

    // Count
    int count = std::count_if(v.begin(), v.end(), [](int x) { return x > 5; });
    std::cout << "Numbers > 5: " << count << "
";

    // Transform (in-place: double each element)
    std::transform(v.begin(), v.end(), v.begin(), [](int x) { return x * 2; });

    // Accumulate (sum)
    int sum = std::accumulate(v.begin(), v.end(), 0);
    std::cout << "Sum: " << sum << "
";

    // Min/Max
    auto [min_it, max_it] = std::minmax_element(v.begin(), v.end());
    std::cout << "Min: " << *min_it << ", Max: " << *max_it << "
";

    // Remove-erase idiom (remove even numbers)
    v.erase(std::remove_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }), v.end());

    // For each
    std::for_each(v.begin(), v.end(), [](int x) { std::cout << x << " "; });
    std::cout << "
";

    return 0;
}

Container Comparison

ContainerAccessInsert/RemoveOrderedUse When
vectorO(1) randomO(1) back, O(n) middleInsertion orderDefault choice, contiguous memory
dequeO(1) randomO(1) front/backInsertion orderNeed fast push_front
listO(n) sequentialO(1) anywhere (with iterator)Insertion orderFrequent mid-list insert/remove
mapO(log n)O(log n)Sorted by keyOrdered key-value, range queries
unordered_mapO(1) avgO(1) avgNoFast lookup by key (hash table)
setO(log n)O(log n)SortedUnique sorted values

Iterators

iterators.cpp
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v{10, 20, 30, 40, 50};

    // Iterator-based loop (how algorithms work internally)
    for (auto it = v.begin(); it != v.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << "
";

    // Reverse iteration
    for (auto it = v.rbegin(); it != v.rend(); ++it) {
        std::cout << *it << " ";  // 50 40 30 20 10
    }
    std::cout << "
";

    // const iterators (read-only)
    for (auto it = v.cbegin(); it != v.cend(); ++it) {
        std::cout << *it << " ";
        // *it = 0;  // ERROR: const iterator
    }

    return 0;
}

Best Practices

  • Default to std::vector — cache-friendly, fastest for most workloads.
  • Use algorithms over raw loopsstd::sort, std::find, std::transform are well-tested and optimized.
  • Use unordered_map over map when you do not need ordered keys — O(1) vs O(log n).
  • Use range-based for (for (auto& x : container)) when you do not need the iterator itself.
  • Use structured bindings (C++17) for cleaner map/pair iteration.
  • Reserve vector capacity (v.reserve(n)) when you know the approximate size upfront.

Keep Practicing

Use the online compiler to run every example and experiment with modifications. The best way to learn C++ is by writing code — even small programs build strong foundations.