Modern C++ Complete

Modern C++

C++11 through C++20 changed the language's culture. Smart pointers, move semantics, and type inference let you write memory-safe, fast code that reads almost like a managed language.

C++11 → C++20 Interview Key 4 Topics
01

Smart Pointers

Smart pointers are RAII wrappers around heap memory that free it automatically. They make raw new / delete obsolete in modern code, eliminating leaks and dangling pointers by construction.

C++ — unique_ptr & shared_ptr
#include <memory>

// unique_ptr — sole owner, freed automatically. Zero overhead.
std::unique_ptr<int> p = std::make_unique<int>(42);
*p;                  // 42
// no delete needed — freed when p goes out of scope

// shared_ptr — reference-counted, freed when the last owner dies
std::shared_ptr<int> a = std::make_shared<int>(10);
std::shared_ptr<int> b = a;   // both own it, count = 2
a.use_count();       // 2
// freed only when BOTH a and b are gone
Smart pointerOwnershipUse when
unique_ptrExactly one ownerDefault — cheapest, most common
shared_ptrShared, reference-countedMultiple owners, unclear lifetime
weak_ptrNon-owning observerBreak shared_ptr reference cycles
The modern rule: never write raw new / delete. Use make_unique by default, make_shared only when ownership is genuinely shared. This is how modern C++ gets memory safety without a garbage collector.
02

Move Semantics

Move semantics (C++11) let you transfer a resource out of an object instead of copying it. Returning a big vector from a function no longer copies the whole thing — it moves the internal buffer, which is nearly free.

C++ — std::move
#include <utility>

std::vector<int> a = {1, 2, 3, 4, 5};

// copy — duplicates all elements (expensive)
std::vector<int> b = a;

// move — steals a's buffer, a is left empty (cheap)
std::vector<int> c = std::move(a);
a.size();      // 0 — a was moved-from, do not rely on its value
c.size();      // 5 — c now owns the data

// returning a local by value moves automatically (or is elided)
std::vector<int> build() {
    std::vector<int> v = {1, 2, 3};
    return v;   // moved out, not copied
}
After std::move(a), a is in a valid but unspecified state — you may assign to it or destroy it, but don't read its value. Moving is a one-way transfer, not a copy.
03

auto, Range-for & Structured Bindings

Modern syntax cuts the verbosity C++ was famous for. Type inference, clean iteration, and unpacking make the language far more readable than its 1990s reputation suggests.

C++ — Modern Syntax
// auto — no more typing out long iterator types
std::map<std::string, int> scores = {{"a", 1}, {"b", 2}};
auto it = scores.begin();   // instead of std::map<...>::iterator

// range-for + structured bindings (C++17) — unpack key/value
for (const auto& [key, value] : scores) {
    std::cout << key << " = " << value << "\n";
}

// structured bindings on a pair
auto [lo, hi] = std::minmax({3, 1, 4, 1, 5});
// lo = 1, hi = 5

// nullptr, constexpr, and more all landed in modern C++
constexpr int SIZE = 100;   // computed at compile time
04

Common Pitfalls

Raw new / delete

Manual memory management is error-prone. Use make_unique / make_shared so cleanup is automatic and leaks are impossible.

Reading a moved-from object

After std::move(x), x holds an unspecified value. Assign a new value before using it again.

shared_ptr reference cycles

Two objects holding shared_ptrs to each other never reach count zero, so neither is freed. Break the cycle with a weak_ptr.

Prefer the STL and RAII

vector, string, and smart pointers already do memory management correctly. Lean on them instead of hand-rolling buffers and pointers.

Quick Quiz

1. The default smart pointer to reach for is…
2. std::move(v) does what?
3. shared_ptr frees its object when…
4. After std::move(x), reading x's value is…
5. Structured bindings let you…