← Back to all guides

C++ TLDR

A rapid reference guide to modern C++. Power, performance, and flexibility for systems programming.

C++23 — December 2025

What is C++?

C++ is a general-purpose programming language created as an extension of C. It supports procedural, object-oriented, and generic programming, offering high performance with low-level memory control.

Performance

Zero-overhead abstractions and direct hardware access. Compile to native code for maximum speed.

Multi-Paradigm

Procedural, object-oriented, functional, and generic programming. Use what fits your problem.

RAII

Resource Acquisition Is Initialization. Automatic resource management tied to object lifetime.

Rich Ecosystem

Massive standard library, countless third-party libraries, and decades of tools and frameworks.

Basics

Hello World

#include <iostream>

int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

Variables

// Basic types
int x = 42;
double pi = 3.14159;
bool flag = true;
char letter = 'A';

// Auto type deduction (C++11)
auto y = 42;        // int
auto z = 3.14;      // double

// Constants
const int MAX = 100;
constexpr int SIZE = 256;  // compile-time constant

// Initialization (C++11)
int a{5};           // uniform initialization
int b = {5};
int c(5);           // constructor syntax

Functions

// Basic function
int add(int a, int b) {
    return a + b;
}

// Function overloading
double add(double a, double b) {
    return a + b;
}

// Default parameters
void greet(std::string name = "World") {
    std::cout << "Hello, " << name << "!\n";
}

// Inline functions
inline int square(int x) {
    return x * x;
}

// Lambda expressions (C++11)
auto multiply = [](int a, int b) { return a * b; };
int result = multiply(3, 4);

Control Flow

// if-else
if (x > 0) {
    // positive
} else if (x < 0) {
    // negative
} else {
    // zero
}

// switch
switch (value) {
    case 1:
        // do something
        break;
    case 2:
        // do something else
        break;
    default:
        // default case
}

// for loop
for (int i = 0; i < 10; i++) {
    std::cout << i << "\n";
}

// range-based for (C++11)
std::vector<int> nums = {1, 2, 3, 4, 5};
for (auto num : nums) {
    std::cout << num << "\n";
}

// while
while (condition) {
    // loop body
}

// do-while
do {
    // loop body
} while (condition);

Types

Primitive Types

Type Size Range/Notes
bool 1 byte true or false
char 1 byte -128 to 127 or 0 to 255
short 2 bytes -32,768 to 32,767
int 4 bytes ~-2 billion to 2 billion
long 4-8 bytes Platform dependent
long long 8 bytes Very large integers
float 4 bytes Single precision
double 8 bytes Double precision

Compound Types

// Arrays
int arr[5] = {1, 2, 3, 4, 5};
int matrix[3][3];  // 2D array

// std::array (C++11) - safer than raw arrays
std::array<int, 5> arr2 = {1, 2, 3, 4, 5};

// Structures
struct Point {
    int x;
    int y;
};
Point p = {10, 20};

// Enums
enum Color { RED, GREEN, BLUE };

// Enum class (C++11) - type-safe
enum class Status {
    OK,
    ERROR,
    PENDING
};
Status s = Status::OK;

Strings

#include <string>

// C-style strings
char str[] = "Hello";
const char* ptr = "World";

// std::string
std::string s1 = "Hello";
std::string s2("World");

// String operations
std::string full = s1 + ", " + s2;  // concatenation
size_t len = s1.length();
char first = s1[0];
s1.append("!");
s1.substr(0, 5);           // substring
s1.find("ell");             // returns position or npos

// String view (C++17) - non-owning
std::string_view sv = "Hello";

Pointers & References

Pointers

// Pointer basics
int x = 42;
int* ptr = &x;        // pointer to x
int value = *ptr;     // dereference (get value)

// Null pointers
int* p1 = nullptr;    // C++11 (preferred)
int* p2 = NULL;       // old style
int* p3 = 0;          // old style

// Pointer arithmetic
int arr[] = {1, 2, 3};
int* p = arr;
p++;                   // points to arr[1]
int val = *(p + 1);  // arr[2]

// Const pointers
const int* p4 = &x;      // pointer to const int
int* const p5 = &x;      // const pointer to int
const int* const p6 = &x; // const pointer to const int

References

// References (must be initialized)
int x = 42;
int& ref = x;         // reference to x
ref = 100;              // modifies x

// Function parameters (pass by reference)
void increment(int& n) {
    n++;
}

// Const reference (read-only, no copy)
void print(const std::string& s) {
    std::cout << s << "\n";
}

// Rvalue references (C++11) - for move semantics
void process(std::string&& s) {
    // s is an rvalue reference
}

Dynamic Memory

// new and delete
int* p = new int(42);
delete p;

// Arrays
int* arr = new int[10];
delete[] arr;

// NOTE: Prefer smart pointers in modern C++!
In modern C++, prefer references over pointers when you don't need to reassign or handle null values.

Memory Management & RAII

RAII (Resource Acquisition Is Initialization) ties resource lifetime to object lifetime. When the object is destroyed, resources are automatically released.

Smart Pointers (C++11)

#include <memory>

// unique_ptr - exclusive ownership
std::unique_ptr<int> p1 = std::make_unique<int>(42);
// std::unique_ptr<int> p2 = p1;  // ERROR: cannot copy
std::unique_ptr<int> p2 = std::move(p1);  // OK: move ownership

// shared_ptr - shared ownership (reference counted)
std::shared_ptr<int> s1 = std::make_shared<int>(42);
std::shared_ptr<int> s2 = s1;  // OK: shared ownership
std::cout << s1.use_count() << "\n";  // 2

// weak_ptr - non-owning reference (breaks circular refs)
std::weak_ptr<int> w1 = s1;
if (auto sp = w1.lock()) {
    // object still exists
}

RAII Example

// File handle automatically closed when object destroyed
class FileHandler {
    FILE* file;
public:
    FileHandler(const char* name) {
        file = fopen(name, "r");
    }

    ~FileHandler() {
        if (file) fclose(file);
    }

    // Delete copy operations
    FileHandler(const FileHandler&) = delete;
    FileHandler& operator=(const FileHandler&) = delete;
};

Move Semantics (C++11)

class Buffer {
    int* data;
    size_t size;
public:
    // Move constructor
    Buffer(Buffer&& other) noexcept
        : data(other.data), size(other.size) {
        other.data = nullptr;
        other.size = 0;
    }

    // Move assignment
    Buffer& operator=(Buffer&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            size = other.size;
            other.data = nullptr;
            other.size = 0;
        }
        return *this;
    }
};
Always prefer smart pointers over raw pointers. Use make_unique and make_shared for exception safety.

Classes & Objects

Basic Class

class Rectangle {
private:
    int width;
    int height;

public:
    // Constructor
    Rectangle(int w, int h) : width(w), height(h) {}

    // Default constructor
    Rectangle() : width(0), height(0) {}

    // Member function
    int area() const {
        return width * height;
    }

    // Getter
    int getWidth() const { return width; }

    // Setter
    void setWidth(int w) { width = w; }
};

// Usage
Rectangle r1(10, 5);
std::cout << r1.area() << "\n";  // 50

Constructor Initialization List

class Person {
    std::string name;
    int age;
    const int id;  // must use initialization list

public:
    // Preferred: initialization list
    Person(std::string n, int a, int i)
        : name(n), age(a), id(i) {}
};

Special Member Functions

class MyClass {
public:
    // Constructor
    MyClass() { }

    // Destructor
    ~MyClass() { }

    // Copy constructor
    MyClass(const MyClass& other) { }

    // Copy assignment
    MyClass& operator=(const MyClass& other) {
        return *this;
    }

    // Move constructor (C++11)
    MyClass(MyClass&& other) noexcept { }

    // Move assignment (C++11)
    MyClass& operator=(MyClass&& other) noexcept {
        return *this;
    }
};

// Rule of Five: if you define one, define all or delete them
// Or use = default / = delete
class Simple {
public:
    Simple() = default;
    Simple(const Simple&) = delete;  // no copying
};

Operator Overloading

class Vector2D {
    double x, y;
public:
    Vector2D(double x, double y) : x(x), y(y) {}

    // Operator +
    Vector2D operator+(const Vector2D& other) const {
        return Vector2D(x + other.x, y + other.y);
    }

    // Operator ==
    bool operator==(const Vector2D& other) const {
        return x == other.x && y == other.y;
    }

    // Operator << (friend function)
    friend std::ostream& operator<<(std::ostream& os, const Vector2D& v) {
        os << "(" << v.x << ", " << v.y << ")";
        return os;
    }
};

Static Members

class Counter {
    static int count;  // shared by all instances
public:
    Counter() { count++; }

    static int getCount() { return count; }
};

// Define static member
int Counter::count = 0;

// Access without instance
int n = Counter::getCount();

Inheritance & Polymorphism

Basic Inheritance

class Animal {
protected:
    std::string name;
public:
    Animal(std::string n) : name(n) {}

    void eat() {
        std::cout << name << " is eating\n";
    }
};

// Derived class
class Dog : public Animal {
public:
    Dog(std::string n) : Animal(n) {}

    void bark() {
        std::cout << name << " barks!\n";
    }
};

Dog d("Rex");
d.eat();   // inherited
d.bark();  // own method

Virtual Functions & Polymorphism

class Shape {
public:
    // Virtual function
    virtual double area() const {
        return 0;
    }

    // Virtual destructor (important!)
    virtual ~Shape() = default;
};

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}

    // Override (C++11 keyword)
    double area() const override {
        return 3.14159 * radius * radius;
    }
};

// Polymorphism
Shape* s = new Circle(5);
std::cout << s->area() << "\n";  // calls Circle::area
delete s;

Abstract Classes

// Pure virtual function = abstract class
class Drawable {
public:
    virtual void draw() const = 0;  // pure virtual
    virtual ~Drawable() = default;
};

class Rectangle : public Drawable {
public:
    void draw() const override {
        std::cout << "Drawing rectangle\n";
    }
};

Access Specifiers

public
Accessible from anywhere
protected
Accessible in class and derived classes
private
Accessible only within the class

Templates

Templates enable generic programming. Write code once, use with any type.

Function Templates

// Generic function
template<typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

// Usage
int i = max(3, 7);           // T = int
double d = max(3.5, 2.1);   // T = double

// Multiple template parameters
template<typename T, typename U>
auto add(T a, U b) {
    return a + b;
}

Class Templates

// Generic class
template<typename T>
class Stack {
    std::vector<T> elements;
public:
    void push(const T& elem) {
        elements.push_back(elem);
    }

    T pop() {
        T elem = elements.back();
        elements.pop_back();
        return elem;
    }

    bool empty() const {
        return elements.empty();
    }
};

// Usage
Stack<int> intStack;
Stack<std::string> strStack;

Template Specialization

// Generic template
template<typename T>
class Storage {
    T data;
public:
    void print() { std::cout << data << "\n"; }
};

// Specialization for bool
template<>
class Storage<bool> {
    bool data;
public:
    void print() {
        std::cout << (data ? "true" : "false") << "\n";
    }
};

Variadic Templates (C++11)

// Variable number of arguments
template<typename... Args>
void print(Args... args) {
    (std::cout << ... << args) << "\n";  // fold expression (C++17)
}

print(1, " hello ", 3.14, " world");

STL Containers

Sequence Containers

#include <vector>
#include <deque>
#include <list>
#include <array>

// vector - dynamic array
std::vector<int> v = {1, 2, 3};
v.push_back(4);
v.pop_back();
int first = v[0];
int last = v.back();
size_t sz = v.size();

// deque - double-ended queue
std::deque<int> dq;
dq.push_front(1);
dq.push_back(2);
dq.pop_front();

// list - doubly linked list
std::list<int> lst = {1, 2, 3};
lst.push_front(0);
lst.push_back(4);

// array - fixed-size array (C++11)
std::array<int, 5> arr = {1, 2, 3, 4, 5};

Associative Containers

#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>

// map - ordered key-value pairs
std::map<std::string, int> ages;
ages["Alice"] = 30;
ages.insert({"Bob", 25});
if (ages.count("Alice")) { /* exists */ }

// set - ordered unique elements
std::set<int> s = {3, 1, 4, 1, 5};  // {1, 3, 4, 5}
s.insert(2);
bool has = s.count(3);  // true

// unordered_map - hash map (C++11)
std::unordered_map<std::string, int> scores;
scores["player1"] = 100;

// unordered_set - hash set (C++11)
std::unordered_set<int> uset = {1, 2, 3};

Container Adaptors

#include <stack>
#include <queue>

// stack - LIFO
std::stack<int> stk;
stk.push(1);
stk.push(2);
int top = stk.top();
stk.pop();

// queue - FIFO
std::queue<int> q;
q.push(1);
int front = q.front();
q.pop();

// priority_queue - heap
std::priority_queue<int> pq;
pq.push(3);
pq.push(1);
pq.push(4);
int max = pq.top();  // 4

Iterators

// Iterator basics
std::vector<int> v = {1, 2, 3, 4, 5};

// Range-based for loop (C++11)
for (auto x : v) {
    std::cout << x << " ";
}

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

// Reverse iterator
for (auto it = v.rbegin(); it != v.rend(); ++it) {
    std::cout << *it << " ";
}

Algorithms & Iterators

Common Algorithms

#include <algorithm>
#include <numeric>

std::vector<int> v = {3, 1, 4, 1, 5, 9};

// Sort
std::sort(v.begin(), v.end());

// Sort with custom comparator
std::sort(v.begin(), v.end(), std::greater<int>());

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

// Count
int count = std::count(v.begin(), v.end(), 1);

// Min/Max
auto min = std::min_element(v.begin(), v.end());
auto max = std::max_element(v.begin(), v.end());

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

// Reverse
std::reverse(v.begin(), v.end());

// Remove duplicates (needs sort first)
std::sort(v.begin(), v.end());
v.erase(std::unique(v.begin(), v.end()), v.end());

Transform & Lambda

// Transform with lambda
std::vector<int> v = {1, 2, 3, 4, 5};
std::vector<int> result(v.size());

std::transform(v.begin(), v.end(), result.begin(),
    [](int x) { return x * x; });

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

// Count_if
int evens = std::count_if(v.begin(), v.end(),
    [](int x) { return x % 2 == 0; });

Ranges (C++20)

#include <ranges>

namespace ranges = std::ranges;

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

// Ranges algorithms
ranges::sort(v);
auto it = ranges::find(v, 3);

// Views (lazy evaluation)
auto evens = v | std::views::filter([](int x) { return x % 2 == 0; });
auto squared = evens | std::views::transform([](int x) { return x * x; });

Modern C++ Features

auto & decltype (C++11)

// auto - type deduction
auto x = 42;              // int
auto pi = 3.14;           // double
auto s = "hello";         // const char*
auto v = std::vector<int>{};

// decltype - get type of expression
int a = 5;
decltype(a) b = 10;      // b is int

Range-based for (C++11)

std::vector<int> v = {1, 2, 3};

// By value
for (auto x : v) {
    std::cout << x;
}

// By reference (modify)
for (auto& x : v) {
    x *= 2;
}

// By const reference (no copy)
for (const auto& x : v) {
    std::cout << x;
}

nullptr (C++11)

// Old way: NULL or 0
int* p1 = NULL;

// Modern way: nullptr
int* p2 = nullptr;

if (p2 == nullptr) {
    // null pointer
}

Structured Bindings (C++17)

// Unpack tuple/pair/struct
std::pair<int, std::string> p = {1, "hello"};
auto [id, name] = p;

// With map
std::map<std::string, int> ages;
for (const auto& [key, value] : ages) {
    std::cout << key << ": " << value;
}

Optional (C++17)

#include <optional>

// Optional return value
std::optional<int> find_value() {
    if (success) {
        return 42;
    }
    return std::nullopt;
}

// Usage
if (auto val = find_value()) {
    std::cout << *val;
}

std::variant (C++17)

#include <variant>

// Type-safe union
std::variant<int, double, std::string> v;

v = 42;
v = 3.14;
v = "hello";

// Get value
if (auto p = std::get_if<std::string>(&v)) {
    std::cout << *p;
}

Concepts (C++20)

// Define concept
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;

// Use concept
template<Numeric T>
T add(T a, T b) {
    return a + b;
}

if/switch with initializer (C++17)

// if with initializer
if (auto it = m.find(key); it != m.end()) {
    // use it
}

// switch with initializer
switch (auto val = getValue(); val) {
    case 1: break;
    default: break;
}