Skip to content

C++ Cheatsheet

C++ is a general-purpose programming language created by Bjarne Stroustrup in 1979, first released in 1985. It supports procedural, object-oriented, and generic programming. C++ compiles to native machine code, giving direct control over hardware and memory — which makes it the standard choice for systems software, game engines, embedded systems, and high-performance applications. This cheatsheet is a comprehensive reference for building real applications in C++.

C++ is a compiled language — you write source code in .cpp files, then a compiler translates it into a native executable that your OS can run directly. There is no interpreter or runtime like Python or JavaScript.

source.cpp → [Compiler] → program (executable)
CompilerCommandPlatformsNotes
GCC (g++)g++Linux, macOS, Windows (MinGW)Most common on Linux. Free, open-source.
Clang (clang++)clang++macOS, Linux, WindowsDefault on macOS (via Xcode). Better error messages.
MSVCclWindowsComes with Visual Studio. Default on Windows.

All three support C++17 and C++20. For this cheatsheet, examples use g++ — replace with clang++ if you prefer, the flags are the same.

macOS — Clang is included with Xcode Command Line Tools:

Terminal window
xcode-select --install
# Verify:
clang++ --version

Ubuntu / Debian:

Terminal window
sudo apt update
sudo apt install g++
# Verify:
g++ --version

Fedora / RHEL:

Terminal window
sudo dnf install gcc-c++

Windows — install one of:

  • Visual Studio (includes MSVC) — full IDE
  • MSYS2 (includes GCC/MinGW) — Unix-like terminal
  • WSL — run Ubuntu inside Windows, then apt install g++

Create a file called hello.cpp:

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

Compile and run:

Terminal window
g++ hello.cpp -o hello # compile
./hello # run
# Output: Hello, World!
FlagPurposeExample
-o nameName the output executableg++ main.cpp -o app
-std=c++17Use C++17 standardg++ -std=c++17 main.cpp -o app
-WallEnable all common warningsCatches potential bugs
-WextraEnable extra warningsEven more thorough
-WerrorTreat warnings as errorsForces you to fix all warnings
-gInclude debug infoRequired for gdb / lldb debugging
-O2Optimization level 2Faster executable, slower compile
-I pathAdd include directoryg++ -I include main.cpp
-l libLink a libraryg++ main.cpp -lpthread
-cCompile only (produce .o, don’t link)g++ -c main.cpp -o main.o

A typical development command:

Terminal window
g++ -std=c++17 -Wall -Wextra -Werror -g main.cpp -o app

A typical release build:

Terminal window
g++ -std=c++17 -O2 main.cpp -o app
StandardYearKey Features
C++981998Original standard, STL, templates
C++112011auto, lambdas, smart pointers, move semantics, nullptr, range-for
C++142014Generic lambdas, make_unique, relaxed constexpr
C++172017Structured bindings, optional, variant, string_view, if constexpr
C++202020Concepts, ranges, coroutines, std::format, modules
C++232023std::expected, std::print, std::flat_map

Use -std=c++17 as a default — it has the best balance of modern features and compiler support. Most code in this cheatsheet uses C++17 or earlier.

RFC 959, Section Getting Started — "cppreference.com"

Every C++ program follows a standard structure: include header files, declare the namespace, and define the main() function as the entry point. The #include directive imports library headers, using namespace std; brings the standard library into scope, and main() is where execution begins.

#include <iostream>
using namespace std;
int main()
{
cout << "Hello, World!" << endl;
int num;
cout << "Enter a number: ";
cin >> num;
cout << "You entered: " << num << endl;
return 0;
}

The return 0; statement signals successful program termination to the operating system.

RFC 959, Section Basic Structure of C++ Program

Comments are non-executable annotations in source code. C++ supports two forms:

  • Single-line comments start with // and extend to the end of the line.
  • Multi-line comments are enclosed between /* and */ and can span multiple lines.
// This is a single-line comment
/* This is a
multi-line comment */

RFC 959, Section Comments

Real C++ projects separate declarations from implementations using header files (.h or .hpp) and source files (.cpp). Headers contain class declarations, function prototypes, and constants. Source files contain the implementations.

// math_utils.h — declaration
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
double circleArea(double radius);
class Calculator {
double result;
public:
Calculator();
void add(double val);
void subtract(double val);
double getResult() const;
};
#endif
// math_utils.cpp — implementation
#include "math_utils.h"
#include <cmath>
double circleArea(double radius) {
return M_PI * radius * radius;
}
Calculator::Calculator() : result(0) {}
void Calculator::add(double val) { result += val; }
void Calculator::subtract(double val) { result -= val; }
double Calculator::getResult() const { return result; }
// main.cpp — entry point
#include <iostream>
#include "math_utils.h"
int main() {
Calculator calc;
calc.add(10);
calc.subtract(3);
std::cout << calc.getResult() << std::endl; // 7
return 0;
}
my_project/
├── CMakeLists.txt # Build configuration
├── include/ # Public headers
│ └── my_project/
│ └── math_utils.h
├── src/ # Source files
│ ├── main.cpp
│ └── math_utils.cpp
├── tests/ # Test files
│ └── test_math.cpp
├── lib/ # Third-party libraries
└── build/ # Build output (gitignored)

CMake is the standard build system for C++ projects.

CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(MyProject LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(app
src/main.cpp
src/math_utils.cpp
)
target_include_directories(app PRIVATE include)

Build commands:

Terminal window
mkdir build && cd build
cmake ..
cmake --build .
./app

A Makefile automates compilation without requiring CMake. This Makefile works with the project layout above — it discovers all .cpp files in src/, compiles them to obj/, and links them into an executable.

NAME = my_app
SRC_DIR = src
OBJ_DIR = obj
INCLUDE_DIR = include
SRC = $(wildcard $(SRC_DIR)/*.cpp)
OBJ = $(SRC:$(SRC_DIR)/%.cpp=$(OBJ_DIR)/%.o)
CC = g++
C_FLAGS = -Wall -Wextra -Werror
L_FLAGS =
all: $(NAME)
# Link
$(NAME): $(OBJ)
$(CC) $(L_FLAGS) $(OBJ) -o $@
$(OBJ_DIR):
mkdir -p $(OBJ_DIR)
# Compile
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp | $(OBJ_DIR)
$(CC) $(C_FLAGS) -c $< -I $(INCLUDE_DIR) -o $@
clean:
rm -rf $(OBJ_DIR)
fclean: clean
rm -f $(NAME)
re: fclean all

Build commands:

Terminal window
make # build
make clean # remove object files
make fclean # remove object files + executable
make re # full rebuild
./my_app # run
VariablePurpose
NAMEExecutable name
SRC_DIR / OBJ_DIR / INCLUDE_DIRDirectory paths
SRCAll .cpp files discovered via wildcard
OBJCorresponding .o files via pattern substitution
CCCompiler (g++, clang++)
C_FLAGSCompiler flags (warnings, standard)
L_FLAGSLinker flags (e.g. -lm, -lpthread)
TargetEffect
allBuild the executable (default)
cleanRemove object files
fcleanRemove object files and executable
reFull clean rebuild

C++ compilation happens in four stages:

StageWhat happensTool
PreprocessingExpands #include, #define, macrosPreprocessor
CompilationTranslates .cpp to assemblyCompiler
AssemblyConverts assembly to object files (.o)Assembler
LinkingCombines object files into executableLinker
Terminal window
# Compile and link manually
g++ -std=c++17 -c src/main.cpp -o build/main.o
g++ -std=c++17 -c src/math_utils.cpp -o build/math_utils.o
g++ build/main.o build/math_utils.o -o build/app

RFC 959, Section Project Structure — "cppreference.com"

A variable is a named storage location in memory. Variables must be declared with a type before use. Variable names can contain letters, digits, and underscores, and must begin with a letter or underscore. Identifiers are unique names for variables. Constants are values that do not change during program execution.

int age = 20;
double height = 5.7;
char grade = 'A';
string name = "John";

RFC 959, Section Variables

C++ provides six fundamental data types for storing different kinds of values.

NUMERIC Whole numbers & decimals int 4 bytes 10, -5, 0 float 4 bytes 3.14f double 8 bytes 3.14159 TEXT & LOGIC Characters, booleans & strings char 1 byte 'A', 'b' bool 1 byte true, false string Variable "Hello"

RFC 959, Section Data Types

TypeSizeDescriptionExample
int4 bytesWhole numbers10, -5, 0
float4 bytesDecimal numbers, single precision3.14f
double8 bytesDecimal numbers, double precision3.14159
char1 byteSingle characters'A', 'b'
bool1 byteBoolean valuestrue, false
stringVariableText sequences"Hello"

RFC 959, Section Data Types

Each type has a specific use case:

int age = 25;
float pi = 3.14f;
float area = pi * 5 * 5;
double price = 99.99;
double discount = price * 0.1;
char grade = 'A';
bool isStudent = true;
string firstName = "John";
string lastName = "Doe";
string fullName = firstName + " " + lastName;

RFC 959, Section Data Types

C++ uses the <iostream> library for console I/O. The cout object with the insertion operator << writes output to the console. The cin object with the extraction operator >> reads input from the user.

#include <iostream>
using namespace std;
int main()
{
int age;
cout << "Enter your age: ";
cin >> age;
cout << "Your age is: " << age << endl;
return 0;
}

The << operator can be chained to output multiple values in a single statement. The endl manipulator inserts a newline and flushes the output buffer.

RFC 959, Section Input and Output

Conditional statements control the flow of execution based on boolean conditions. C++ provides five forms.

Executes a block of code only when the condition evaluates to true.

int i = 10;
if (i < 15) {
cout << "10 is less than 15";
}

Provides two execution paths — one for true, one for false.

int i = 10;
if (i < 15) {
cout << "10 is less than 15";
} else {
cout << "10 is not less than 15";
}

Tests multiple conditions sequentially. The first condition that evaluates to true executes its block; the rest are skipped.

int marks = 85;
if (marks >= 90) {
cout << "A" << endl;
} else if (marks >= 80) {
cout << "B" << endl;
} else if (marks >= 70) {
cout << "C" << endl;
} else if (marks >= 60) {
cout << "D" << endl;
} else {
cout << "F" << endl;
}

Evaluates a variable against multiple case values. Each case requires a break to prevent fall-through. The default case handles unmatched values.

char x = 'A';
switch (x) {
case 'A':
cout << "A";
break;
case 'B':
cout << "B";
break;
default:
cout << "Other than A and B";
break;
}

A compact conditional expression: condition ? value_if_true : value_if_false.

int x = 10, y = 20;
int max_val = (x > y) ? x : y;
cout << "The maximum value is " << max_val;

RFC 959, Section Conditional Statements

Loops execute a block of code repeatedly. C++ provides three loop constructs.

Repeats a fixed number of times. The loop header declares the initializer, condition, and increment in a single line.

for (int i = 5; i < 10; i++) {
cout << "Hi" << endl;
}

Repeats while a condition is true. The condition is checked before each iteration — if it is false initially, the body never executes.

int i = 0;
while (i < 5) {
cout << "Hi" << endl;
i++;
}

Executes the body at least once, then checks the condition. The condition is evaluated after each iteration.

int i = 0;
do {
cout << "Hi" << endl;
i++;
} while (i < 5);

RFC 959, Section Loops

An array is a fixed-size collection of elements of the same type, stored in contiguous memory. Elements are accessed by zero-based index.

int arr[5] = {2, 4, 8, 12, 16};
// Access elements
cout << arr[0] << endl; // 2
cout << arr[2] << endl; // 8
// Modify an element
arr[1] = 10;
// Iterate over array
for (int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}

RFC 959, Section Arrays

A multi-dimensional array is an array of arrays. The most common form is a two-dimensional array organized as rows and columns.

int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// Access element at row 1, column 2
cout << matrix[1][2] << endl; // 7
// Iterate over 2D array
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
cout << matrix[i][j] << " ";
}
cout << endl;
}

RFC 959, Section Multi-Dimensional Arrays

A vector is a dynamic array from the C++ Standard Template Library (STL). Unlike fixed-size arrays, vectors can grow and shrink at runtime. Include <vector> to use them.

#include <vector>
using namespace std;
// Empty vector
vector<int> v1;
// Vector of size 3, all elements initialized to 5
vector<int> v2(3, 5);
// Vector with initializer list
vector<int> v3 = {1, 2, 3};

Key vector operations:

vector<int> v = {1, 2, 3};
v.push_back(4); // Add element to end: {1, 2, 3, 4}
v.pop_back(); // Remove last element: {1, 2, 3}
cout << v.size(); // Number of elements: 3
v.clear(); // Remove all elements
MethodDescription
push_back(val)Adds an element to the end
pop_back()Removes the last element
size()Returns the number of elements
clear()Removes all elements

RFC 959, Section Vectors

A reference is an alias for an existing variable. It shares the same memory address as the original and cannot be reassigned after initialization.

int x = 10;
int &ref = x; // ref is an alias for x
ref = 22; // modifies x
cout << x << endl; // 22
RuleExample
Must be initialized at declarationint& r = x; (not int& r;)
Cannot be reassignedAfter int& r = x;, r = y; changes x’s value, not the binding
Cannot be nullReferences always refer to a valid object

RFC 959, Section References

A pointer is a variable that stores the memory address of another variable. The address-of operator & retrieves a variable’s address. The dereference operator * accesses the value at the address.

int var = 10;
int *ptr = &var; // ptr holds the address of var
cout << var; // 10 (value)
cout << &var; // 0x7ffc... (address)
cout << ptr; // 0x7ffc... (same address)
cout << *ptr; // 10 (dereference — value at address)
*ptr = 20; // modifies var through the pointer
cout << var; // 20

nullptr (C++11) is a type-safe null pointer constant. Always use it instead of NULL or 0.

int* ptr = nullptr;
if (ptr == nullptr) {
cout << "Pointer is null" << endl;
}
// Shorthand — nullptr evaluates to false
if (!ptr) {
cout << "Pointer is null" << endl;
}

Pointers support arithmetic that moves by the size of the pointed-to type.

int arr[5] = {10, 20, 30, 40, 50};
int* p = arr; // points to arr[0]
cout << *p; // 10
cout << *(p + 1); // 20 (next int, 4 bytes forward)
cout << *(p + 3); // 40
p++; // now points to arr[1]
cout << *p; // 20
// Distance between pointers
int* start = &arr[0];
int* end = &arr[4];
cout << (end - start); // 4 (elements, not bytes)

An array name decays to a pointer to its first element. This is why arrays and pointers are often interchangeable.

int arr[3] = {10, 20, 30};
int* p = arr; // arr decays to &arr[0]
// These are equivalent:
cout << arr[1]; // 20
cout << *(arr + 1); // 20
cout << p[1]; // 20
cout << *(p + 1); // 20
DeclarationCan change value?Can change pointer?
int* pYesYes
const int* pNoYes
int* const pYesNo
const int* const pNoNo
int x = 10, y = 20;
const int* ptr1 = &x; // pointer to const int
// *ptr1 = 30; // ERROR: cannot change value
ptr1 = &y; // OK: can change where it points
int* const ptr2 = &x; // const pointer to int
*ptr2 = 30; // OK: can change value
// ptr2 = &y; // ERROR: cannot change pointer

Read it right-to-left: const int* p — “p is a pointer to int that is const”. int* const p — “p is a const pointer to int”.

Use the arrow operator -> to access members through a pointer.

struct Point { int x, y; };
Point p = {3, 4};
Point* ptr = &p;
cout << ptr->x; // 3 (same as (*ptr).x)
cout << ptr->y; // 4
ptr->x = 10; // modify through pointer

A function pointer stores the address of a function. Useful for callbacks and strategy patterns.

int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }
// Declare function pointer
int (*op)(int, int) = add;
cout << op(3, 4); // 7
op = mul;
cout << op(3, 4); // 12
// As function parameter
void apply(int a, int b, int (*fn)(int, int)) {
cout << fn(a, b) << endl;
}
apply(5, 3, add); // 8
apply(5, 3, mul); // 15

Pointers vs References — When to Use Which

Section titled “Pointers vs References — When to Use Which”
PointerReference
Can be nullYesNo
Can be reassignedYesNo
Can do arithmeticYesNo
Syntax*ptr, ptr->memberDirect use, like the original
Use forOptional values, dynamic memory, arrays, polymorphismFunction parameters, aliases, operator overloading

RFC 959, Section Pointers — "cppreference.com"

A function is a reusable block of code that performs a specific task. Functions can accept parameters and return values. The general syntax is:

returnType functionName(parameters) {
// function body
return value;
}

Example:

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

Functions help avoid repeating code and make programs more organized. They can take inputs (parameters) and return values, both of which are optional.

RFC 959, Section Functions

void log(const string& msg, int level = 1) {
if (level > 0) cout << "[" << level << "] " << msg << endl;
}
log("hello"); // level defaults to 1
log("error", 3); // level = 3
void byValue(int x) { x = 0; } // copy — original unchanged
void byRef(int& x) { x = 0; } // reference — modifies original
void byPtr(int* x) { *x = 0; } // pointer — modifies original
void byConstRef(const string& s) { ... } // read-only, no copy
int area(int side) { return side * side; }
int area(int width, int height) { return width * height; }
double area(double radius) { return 3.14159 * radius * radius; }

The inline keyword suggests the compiler replace the function call with its body. Modern compilers typically decide this automatically.

inline int square(int x) { return x * x; }
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}

RFC 959, Section Functions — "cppreference.com"

The string class provides built-in methods for common text operations.

FunctionDescriptionExample
length()Returns the number of characterss.length()
substr(pos, len)Extracts a substring starting at pos with length lens.substr(0, 5)
append(str)Appends str to the end of the strings.append(" world")
compare(str)Lexicographic comparison; returns 0 if equals.compare("hello")
empty()Returns true if the string has no characterss.empty()
string s = "Hello, World!";
cout << s.length() << endl; // 13
cout << s.substr(0, 5) << endl; // Hello
s.append(" C++"); // "Hello, World! C++"
cout << s.compare("Hello") << endl; // non-zero (not equal)
cout << s.empty() << endl; // 0 (false)

RFC 959, Section String Functions

C++ provides standard mathematical functions for common operations.

FunctionDescriptionExample
min(x, y)Returns the smaller of two valuesmin(5, 3) returns 3
max(x, y)Returns the larger of two valuesmax(5, 3) returns 5
sqrt(x)Returns the square rootsqrt(16) returns 4
ceil(x)Rounds up to the nearest integerceil(4.3) returns 5
floor(x)Rounds down to the nearest integerfloor(4.7) returns 4
pow(x, n)Returns x raised to the power npow(2, 3) returns 8
#include <cmath>
cout << min(5, 3) << endl; // 3
cout << max(5, 3) << endl; // 5
cout << sqrt(16) << endl; // 4
cout << ceil(4.3) << endl; // 5
cout << floor(4.7) << endl; // 4
cout << pow(2, 3) << endl; // 8

RFC 959, Section Math Functions

C++ provides a comprehensive set of operators for performing computations, comparisons, and logical operations.

OperatorDescriptionExample
+Additiona + b
-Subtractiona - b
*Multiplicationa * b
/Divisiona / b
%Modulus (remainder)a % b
++Incrementa++ or ++a
--Decrementa-- or --a
OperatorDescriptionExample
==Equal toa == b
!=Not equal toa != b
>Greater thana > b
<Less thana < b
>=Greater than or equal toa >= b
<=Less than or equal toa <= b
OperatorDescriptionExample
&&Logical ANDa && b
||Logical ORa || b
!Logical NOT!a
OperatorDescriptionExample
&Bitwise ANDa & b
|Bitwise ORa | b
^Bitwise XORa ^ b
~Bitwise NOT~a
<<Left shifta << 2
>>Right shifta >> 2
OperatorDescriptionExample
=Assigna = 5
+=Add and assigna += 3
-=Subtract and assigna -= 3
*=Multiply and assigna *= 3
/=Divide and assigna /= 3
%=Modulus and assigna %= 3

RFC 959, Section Operators — "cppreference.com"

The const qualifier makes a variable or parameter read-only after initialization. Use it liberally — it prevents bugs and documents intent.

const int MAX = 100;
// MAX = 200; // ERROR: cannot modify
const string& name = getName(); // read-only reference, no copy
class Circle {
double radius;
public:
double area() const { return 3.14 * radius * radius; } // does not modify object
};

constexpr (C++11) marks values and functions that can be evaluated at compile time.

constexpr int square(int x) { return x * x; }
constexpr int val = square(5); // computed at compile time: 25
constexpr int fib(int n) {
return (n <= 1) ? n : fib(n - 1) + fib(n - 2);
}
static_assert(fib(10) == 55); // verified at compile time
DeclarationMeaning
const int x = 5x cannot be changed
const int* pCannot change value through p (pointer to const)
int* const pCannot reassign p itself (const pointer)
const int* const pBoth are const
void f(const T& x)Function does not modify x
int get() constMember function does not modify the object

RFC 959, Section const and constexpr — "cppreference.com"

A class is a user-defined data type that bundles data members (variables) and member functions (methods) into a single unit. An object is an instance of a class.

class Car {
public:
string brand;
int year;
void display() {
cout << brand << " (" << year << ")" << endl;
}
};
int main() {
Car c;
c.brand = "Toyota";
c.year = 2024;
c.display(); // Toyota (2024)
}

RFC 959, Section Object-Oriented Programming

Access specifiers control the visibility of class members.

SpecifierAccess within classAccess in derived classAccess outside class
publicYesYesYes
protectedYesYesNo
privateYesNoNo
class Account {
private:
double balance; // only accessible within this class
protected:
string accountId; // accessible in derived classes
public:
string ownerName; // accessible everywhere
void setBalance(double b) { balance = b; }
double getBalance() { return balance; }
};

RFC 959, Section Object-Oriented Programming — "cppreference.com"

A constructor initializes an object when it is created. A destructor cleans up when an object goes out of scope.

class Point {
int x, y;
public:
Point() : x(0), y(0) {} // default constructor
Point(int x, int y) : x(x), y(y) {} // parameterized constructor
Point(const Point& p) : x(p.x), y(p.y) {} // copy constructor
~Point() {} // destructor
};
Point p1; // default: (0, 0)
Point p2(3, 4); // parameterized: (3, 4)
Point p3(p2); // copy: (3, 4)

RFC 959, Section Object-Oriented Programming — "cppreference.com"

The this pointer is an implicit pointer available inside member functions that points to the calling object.

class Box {
int size;
public:
Box& setSize(int size) {
this->size = size; // distinguishes member from parameter
return *this; // enables method chaining
}
};

RFC 959, Section Object-Oriented Programming — "cppreference.com"

Encapsulation bundles data and the functions that operate on it into a single class, restricting direct access to internal state through access specifiers. External code interacts with the object through getters and setters.

class Temperature {
double celsius; // private by default
public:
void set(double c) {
if (c >= -273.15) // validation logic
celsius = c;
}
double get() const { return celsius; }
double fahrenheit() const { return celsius * 9.0 / 5.0 + 32; }
};

RFC 959, Section Pillars of OOPs

static class members belong to the class itself, not to any specific object. Static data members are shared across all instances. Static member functions can be called without an object.

class Counter {
static int count; // declaration
public:
Counter() { count++; }
~Counter() { count--; }
static int getCount() { return count; } // no this pointer
};
int Counter::count = 0; // definition (required, usually in .cpp)
Counter a, b, c;
cout << Counter::getCount(); // 3
ContextEffect
static local variablePersists across function calls, initialized once
static global variable/functionInternal linkage — visible only in the current translation unit
static class memberShared across all instances of the class
void counter() {
static int n = 0; // initialized once, persists across calls
cout << ++n << " ";
}
counter(); counter(); counter(); // 1 2 3

RFC 959, Section static Members — "cppreference.com"

Inheritance allows a derived class to acquire the properties and behavior of a base class, promoting code reuse.

class Animal {
public:
string name;
void eat() { cout << name << " eats" << endl; }
};
class Dog : public Animal {
public:
void bark() { cout << name << " barks" << endl; }
};
Dog d;
d.name = "Rex";
d.eat(); // Rex eats
d.bark(); // Rex barks

A class can inherit from more than one base class.

class Printable {
public:
void print() { cout << "Printing..." << endl; }
};
class Scannable {
public:
void scan() { cout << "Scanning..." << endl; }
};
class Printer : public Printable, public Scannable {};
Printer p;
p.print(); // Printing...
p.scan(); // Scanning...

The inheritance access specifier controls how base class members are exposed in the derived class.

Base memberpublic inheritanceprotected inheritanceprivate inheritance
publicpublicprotectedprivate
protectedprotectedprotectedprivate
privateNot accessibleNot accessibleNot accessible

A derived class can override a base class method by defining a function with the same signature.

class Shape {
public:
void draw() { cout << "Drawing shape" << endl; }
};
class Circle : public Shape {
public:
void draw() { cout << "Drawing circle" << endl; }
};
Circle c;
c.draw(); // Drawing circle

RFC 959, Section Pillars of OOPs — "cppreference.com"

A friend declaration grants a non-member function or another class access to private and protected members.

class Box {
double width;
public:
Box(double w) : width(w) {}
friend void printWidth(const Box& b); // friend function
friend class BoxFactory; // friend class
};
void printWidth(const Box& b) {
cout << b.width; // can access private member
}

Use friend sparingly — it breaks encapsulation. Common legitimate uses: operator overloading (e.g., operator<<) and factory classes.

RFC 959, Section friend — "cppreference.com"

When a class inherits from two bases that share a common ancestor, the diamond problem creates two copies of the ancestor’s members. Virtual inheritance ensures only one copy exists.

class Animal { public: string name; };
class Mammal : virtual public Animal {}; // virtual
class WingedAnimal : virtual public Animal {}; // virtual
class Bat : public Mammal, public WingedAnimal {};
Bat b;
b.name = "Bruce"; // OK — only one copy of Animal::name

Without virtual, b.name would be ambiguous (two copies of Animal).

RFC 959, Section Virtual Inheritance — "cppreference.com"

Polymorphism allows one interface to represent different underlying types. C++ supports both compile-time and runtime polymorphism.

Multiple functions can share the same name if they differ in parameter types or count.

int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
int add(int a, int b, int c) { return a + b + c; }

Class-specific behavior for operators is defined using the operator keyword.

class Vec2 {
public:
double x, y;
Vec2(double x, double y) : x(x), y(y) {}
Vec2 operator+(const Vec2& v) const {
return Vec2(x + v.x, y + v.y);
}
};
Vec2 a(1, 2), b(3, 4);
Vec2 c = a + b; // (4, 6)

A virtual function in the base class allows derived classes to provide their own implementation, resolved at runtime through a base pointer or reference.

class Shape {
public:
virtual void draw() { cout << "Shape" << endl; }
virtual ~Shape() {} // virtual destructor
};
class Circle : public Shape {
public:
void draw() override { cout << "Circle" << endl; }
};
Shape* s = new Circle();
s->draw(); // Circle (resolved at runtime)
delete s;

Abstract Classes and Pure Virtual Functions

Section titled “Abstract Classes and Pure Virtual Functions”

A pure virtual function has no implementation in the base class. A class with at least one pure virtual function is an abstract class and cannot be instantiated. This is the C++ equivalent of an interface.

class Drawable {
public:
virtual void draw() = 0; // pure virtual
virtual ~Drawable() {}
};
class Square : public Drawable {
public:
void draw() override { cout << "Drawing square" << endl; }
};
// Drawable d; // ERROR: cannot instantiate abstract class
Square sq;
sq.draw(); // Drawing square

RFC 959, Section Pillars of OOPs — "cppreference.com"

A struct is identical to a class except that its members are public by default (class members are private by default).

struct Point {
double x, y; // public by default
double distance() const {
return sqrt(x * x + y * y);
}
};
Point p = {3.0, 4.0};
cout << p.distance(); // 5
Featurestructclass
Default accesspublicprivate
Default inheritancepublicprivate
ConventionPlain data aggregatesObjects with behavior

RFC 959, Section Structs — "cppreference.com"

Enums define a set of named integer constants.

enum Color { RED, GREEN, BLUE }; // RED=0, GREEN=1, BLUE=2
Color c = GREEN;
cout << c; // 1

Traditional enums are unscoped — their values leak into the enclosing scope and implicitly convert to int.

Scoped enums (C++11) provide type safety and prevent implicit conversions.

enum class Direction { UP, DOWN, LEFT, RIGHT };
Direction d = Direction::UP;
// int n = d; // ERROR: no implicit conversion
int n = static_cast<int>(d); // explicit conversion: 0

RFC 959, Section Enums — "cppreference.com"

Templates enable writing generic code that works with any data type. The compiler generates type-specific versions at compile time.

template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
cout << maximum(3, 7); // 7 (int)
cout << maximum(3.5, 2.1); // 3.5 (double)
template <typename T>
class Stack {
vector<T> data;
public:
void push(const T& val) { data.push_back(val); }
T pop() {
T top = data.back();
data.pop_back();
return top;
}
bool empty() const { return data.empty(); }
};
Stack<int> intStack;
intStack.push(42);

RFC 959, Section Templates — "cppreference.com"

C++ handles runtime errors with try, catch, and throw. When an error occurs, throw creates an exception that propagates up the call stack until a matching catch block is found.

try {
int age = -1;
if (age < 0)
throw invalid_argument("Age cannot be negative");
} catch (const invalid_argument& e) {
cout << "Error: " << e.what() << endl;
} catch (const exception& e) {
cout << "General error: " << e.what() << endl;
} catch (...) {
cout << "Unknown error" << endl;
}

Catch blocks are matched top-to-bottom — put more specific types first. Always catch by const& to avoid slicing and unnecessary copies.

All standard exceptions inherit from std::exception. The two main branches are logic_error (programmer mistakes) and runtime_error (external failures).

ExceptionHeaderUse
std::exception<exception>Base class for all standard exceptions
std::logic_error<stdexcept>Errors due to faulty logic (preventable bugs)
std::invalid_argument<stdexcept>Invalid function arguments
std::out_of_range<stdexcept>Index out of bounds
std::runtime_error<stdexcept>Errors detectable only at runtime
std::overflow_error<stdexcept>Arithmetic overflow
std::bad_alloc<new>Memory allocation failure
std::bad_cast<typeinfo>Failed dynamic_cast on references

For real projects, define a project-level base exception that inherits from a standard exception, then derive specific error types from it. This lets callers catch all project errors with one type, or handle specific errors individually.

exceptions.hpp
#pragma once
#include <stdexcept>
#include <string>
namespace ftp {
class FtpException : public std::runtime_error {
public:
explicit FtpException(const std::string& msg) : std::runtime_error(msg) {}
};
class ArgsException : public FtpException {
public:
explicit ArgsException(const std::string& msg) : FtpException(msg) {}
};
class UsageError : public ArgsException {
public:
UsageError() : ArgsException("Usage: ./my_ftp <port> <path>") {}
};
class ParsePortError : public ArgsException {
public:
ParsePortError() : ArgsException("port must be a number") {}
};
class InvalidPortError : public ArgsException {
public:
InvalidPortError() : ArgsException("Port must be between 1024 and 65535") {}
};
} // namespace ftp

This hierarchy allows granular catching:

try {
ftp::Server server(argc, argv);
server.run();
} catch (const ftp::ArgsException& e) {
cerr << "Argument error: " << e.what() << endl;
return 1;
} catch (const ftp::FtpException& e) {
cerr << "FTP error: " << e.what() << endl;
return 1;
} catch (const std::exception& e) {
cerr << "Unexpected error: " << e.what() << endl;
return 1;
}

Mark functions noexcept when they are guaranteed not to throw. If a noexcept function does throw, the program calls std::terminate immediately.

int size() const noexcept { return n; } // simple getter, cannot fail
void swap(Buffer& other) noexcept; // swap should never throw
~MyClass() noexcept; // destructors are noexcept by default
Buffer(Buffer&& other) noexcept; // move operations should be noexcept
Mark noexceptDo NOT mark noexcept
DestructorsFunctions that allocate memory
Move constructors/assignmentFunctions that do I/O
Swap functionsFunctions that call potentially-throwing code
Simple getters/settersVirtual functions that subclasses may override with throwing code

Use bare throw; inside a catch block to rethrow the current exception without slicing it.

try {
riskyOperation();
} catch (const std::exception& e) {
logError(e.what()); // log it
throw; // rethrow original exception (preserves type)
// throw e; // BAD: slices to std::exception, loses derived type
}
DoDon’t
Throw by value, catch by const&Catch by value (causes slicing)
Use exceptions for actual errorsUse exceptions for control flow
Build a project exception hierarchyThrow strings or ints
Clean up resources with RAIIUse manual try/catch for cleanup
Mark non-throwing functions noexceptMark everything noexcept optimistically
Catch at the level that can handle the errorCatch and silently ignore
Let exceptions propagate when you can’t recoverCatch and rethrow with throw e; (slices)

RFC 959, Section Exception Handling — "cppreference.com"

RFC 959, Section E: Error handling — "C++ Core Guidelines"

A lambda expression (C++11) creates an anonymous function object inline. The syntax is [capture](parameters) -> return_type { body }.

auto greet = []() { cout << "Hello" << endl; };
greet(); // Hello
auto add = [](int a, int b) { return a + b; };
cout << add(3, 4); // 7

The capture clause [] specifies which variables from the enclosing scope the lambda can access.

CaptureMeaning
[]Capture nothing
[=]Capture all local variables by value
[&]Capture all local variables by reference
[x]Capture x by value
[&x]Capture x by reference
[=, &x]All by value except x by reference
int factor = 3;
auto multiply = [factor](int x) { return x * factor; };
cout << multiply(5); // 15
auto increment = [&factor]() { factor++; };
increment();
cout << factor; // 4

RFC 959, Section Lambda Expressions — "cppreference.com"

C++ allocates memory on the heap using new and releases it with delete. Forgetting to delete causes memory leaks.

// Single object
int* p = new int(42);
cout << *p; // 42
delete p;
// Array
int* arr = new int[5]{1, 2, 3, 4, 5};
cout << arr[2]; // 3
delete[] arr;

RFC 959, Section Dynamic Memory — "cppreference.com"

Smart pointers (C++11, <memory>) automate memory management by destroying the owned object when the pointer goes out of scope. They eliminate manual new/delete and the bugs that come with it.

Smart PointerOwnershipCopy?Use When
unique_ptrExclusive (one owner)No (move only)Default choice. One clear owner.
shared_ptrShared (reference counted)YesMultiple owners need the same object
weak_ptrNon-owning observerN/ABreak circular references, optional access to shared object

unique_ptr owns an object exclusively — it cannot be copied, only moved. This is the default smart pointer you should reach for.

#include <memory>
// Create
auto p = make_unique<int>(42); // preferred
unique_ptr<int> p2(new int(42)); // also works
// Use — just like a raw pointer
cout << *p; // 42
*p = 100;
// Transfer ownership
unique_ptr<int> p3 = move(p); // p is now nullptr
// unique_ptr<int> p4 = p3; // ERROR: cannot copy
// unique_ptr with arrays
auto arr = make_unique<int[]>(5); // array of 5 ints
arr[0] = 10;
MethodDescription
*ptrDereference (access value)
ptr->memberAccess member of pointed-to object
ptr.get()Get raw pointer (non-owning)
ptr.reset()Delete owned object, set to nullptr
ptr.reset(new_ptr)Delete owned, take ownership of new_ptr
ptr.release()Release ownership, return raw pointer (caller must delete)
move(ptr)Transfer ownership to another unique_ptr
if (ptr)Check if non-null

Common pattern — factory functions:

class Widget { /* ... */ };
unique_ptr<Widget> createWidget(int type) {
if (type == 1) return make_unique<WidgetA>();
if (type == 2) return make_unique<WidgetB>();
return nullptr;
}
auto w = createWidget(1); // caller owns the widget

shared_ptr allows multiple pointers to share ownership of an object. An internal reference count tracks how many shared_ptrs point to the object. The object is destroyed when the last one is destroyed or reset.

auto a = make_shared<int>(10);
cout << a.use_count(); // 1
{
shared_ptr<int> b = a; // reference count: 2
shared_ptr<int> c = a; // reference count: 3
cout << a.use_count(); // 3
} // b and c destroyed, count drops to 1
cout << a.use_count(); // 1
// a destroyed at end of scope, object deleted
MethodDescription
ptr.use_count()Current reference count
ptr.unique()True if use_count() == 1 (deprecated C++17, removed C++20)
ptr.reset()Release this pointer’s ownership (decrements count)
ptr.get()Get raw pointer

weak_ptr observes an object owned by shared_ptr without affecting the reference count. Used to break circular references that would otherwise leak memory.

shared_ptr<int> sp = make_shared<int>(5);
weak_ptr<int> wp = sp;
cout << wp.use_count(); // 1 (counts shared_ptrs only)
cout << wp.expired(); // false
// Must lock() to access the value — returns shared_ptr or nullptr
if (auto locked = wp.lock()) {
cout << *locked; // 5
}
sp.reset(); // object destroyed
cout << wp.expired(); // true
auto locked = wp.lock(); // returns nullptr

Circular reference problem:

struct Node {
shared_ptr<Node> next; // if both nodes point to each other,
// neither is ever deleted (reference count never reaches 0)
};
// Fix: use weak_ptr for back-references
struct Node {
shared_ptr<Node> next;
weak_ptr<Node> prev; // does not prevent deletion
};

Smart pointers work naturally with base/derived classes and virtual functions.

class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
double r;
public:
Circle(double r) : r(r) {}
double area() const override { return 3.14159 * r * r; }
};
// Store derived types through base pointer
vector<unique_ptr<Shape>> shapes;
shapes.push_back(make_unique<Circle>(5.0));
for (auto& s : shapes)
cout << s->area() << endl; // 78.5397 — virtual dispatch works
DoDon’t
Use make_unique / make_sharedUse new directly with smart pointers
Default to unique_ptrDefault to shared_ptr “just in case”
Pass unique_ptr by value to transfer ownershipPass smart pointers by reference when ownership isn’t changing
Pass raw pointer or reference when function doesn’t affect lifetimePass shared_ptr to every function (spreads reference counting overhead)
Use weak_ptr for caches and back-referencesCreate circular shared_ptr references
Use unique_ptr for polymorphic containersUse raw new/delete for polymorphic objects

RFC 959, Section Smart Pointers — "cppreference.com"

Move semantics (C++11) transfer resources from one object to another instead of copying. An rvalue reference (T&&) binds to temporaries and objects about to be destroyed.

class Buffer {
int* data;
size_t size;
public:
Buffer(size_t n) : data(new int[n]), size(n) {}
~Buffer() { delete[] data; }
// Copy — expensive
Buffer(const Buffer& other) : data(new int[other.size]), size(other.size) {
copy(other.data, other.data + size, data);
}
// Move — cheap, steals resources
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;
}
};

std::move casts an lvalue to an rvalue reference, enabling the move constructor or move assignment.

Buffer a(1000);
Buffer b = std::move(a); // moves a's resources to b; a is now empty

RFC 959, Section Move Semantics — "cppreference.com"

Resource Acquisition Is Initialization is the core C++ idiom for resource management. Resources (memory, files, locks, sockets) are acquired in the constructor and released in the destructor. When the object goes out of scope, cleanup happens automatically — no manual release needed.

class FileHandle {
FILE* f;
public:
FileHandle(const char* path) : f(fopen(path, "r")) {
if (!f) throw runtime_error("Cannot open file");
}
~FileHandle() { if (f) fclose(f); } // automatic cleanup
FileHandle(const FileHandle&) = delete; // prevent copying
FileHandle& operator=(const FileHandle&) = delete;
};
void readConfig() {
FileHandle config("app.conf");
// ... use config ...
} // config.~FileHandle() called automatically, file closed

The standard library uses RAII everywhere: vector manages heap memory, unique_ptr manages owned objects, lock_guard manages mutexes, fstream manages file handles.

RFC 959, Section RAII — "cppreference.com"

The Standard Template Library provides generic container classes for every common data structure. All containers are templates — they work with any type.

NeedContainerWhy
Dynamic array, general purposevectorContiguous memory, fast random access, cache-friendly
Fixed-size arrayarrayNo heap allocation, bounds-checkable with .at()
FIFO queuequeuepush to back, pop from front
LIFO stackstackpush and pop from top
Priority queue / heappriority_queueAlways gives you the largest (or smallest) element
Fast insert/remove at both endsdequeLike vector but O(1) push/pop at front too
Fast insert/remove anywherelistDoubly linked list, O(1) splice and insert at iterator
Key-value lookupmap or unordered_mapSorted keys (map) or O(1) average lookup (unordered)
Unique collectionset or unordered_setSorted (set) or O(1) average lookup (unordered)
Sorted data with duplicatesmultiset / multimapLike set/map but allows duplicate keys

Dynamic array. Elements stored contiguously in memory. The default choice for most situations.

#include <vector>
vector<int> v = {1, 2, 3};
v.push_back(4); // {1, 2, 3, 4}
v.pop_back(); // {1, 2, 3}
v.insert(v.begin(), 0); // {0, 1, 2, 3}
v.erase(v.begin() + 1); // {0, 2, 3}
cout << v[0]; // 0
cout << v.at(1); // 2 (bounds-checked)
cout << v.size(); // 3
cout << v.empty(); // 0 (false)
v.clear(); // {}
MethodComplexityDescription
push_back(val)Amortized O(1)Add to end
pop_back()O(1)Remove from end
operator[] / at(i)O(1)Access by index (at throws on out-of-range)
insert(pos, val)O(n)Insert at iterator position
erase(pos)O(n)Remove at iterator position
size() / empty()O(1)Element count / emptiness check
front() / back()O(1)First / last element
clear()O(n)Remove all elements
reserve(n)O(n)Pre-allocate capacity to avoid reallocations
shrink_to_fit()O(n)Release unused capacity

Fixed-size array (C++11). Size is a compile-time constant. No heap allocation. Safer alternative to C-style arrays.

#include <array>
array<int, 4> a = {10, 20, 30, 40};
cout << a[2]; // 30
cout << a.at(5); // throws std::out_of_range
cout << a.size(); // 4
a.fill(0); // {0, 0, 0, 0}

Double-ended queue. Like vector but supports O(1) push/pop at both front and back. Elements are not stored contiguously.

#include <deque>
deque<int> dq = {2, 3, 4};
dq.push_front(1); // {1, 2, 3, 4}
dq.push_back(5); // {1, 2, 3, 4, 5}
dq.pop_front(); // {2, 3, 4, 5}
dq.pop_back(); // {2, 3, 4}
cout << dq[1]; // 3 (random access)
MethodComplexity
push_front(val) / push_back(val)Amortized O(1)
pop_front() / pop_back()O(1)
operator[] / at(i)O(1)
insert(pos, val) / erase(pos)O(n)

Doubly linked list. Fast insert/remove at any position given an iterator, but no random access.

#include <list>
list<int> lst = {1, 2, 3};
lst.push_front(0); // {0, 1, 2, 3}
lst.push_back(4); // {0, 1, 2, 3, 4}
auto it = lst.begin();
advance(it, 2); // move iterator to index 2
lst.insert(it, 99); // {0, 1, 99, 2, 3, 4}
lst.erase(it); // {0, 1, 99, 3, 4}
lst.sort(); // {0, 1, 3, 4, 99}
lst.reverse(); // {99, 4, 3, 1, 0}
lst.remove(3); // {99, 4, 1, 0}
MethodComplexity
push_front / push_backO(1)
insert(it, val) / erase(it)O(1) at known position
sort()O(n log n)
remove(val)O(n)
splice(pos, other_list)O(1) — moves nodes without copying

LIFO (Last In, First Out) adaptor. Wraps deque by default.

#include <stack>
stack<int> st;
st.push(10);
st.push(20);
st.push(30);
cout << st.top(); // 30
st.pop();
cout << st.top(); // 20
cout << st.size(); // 2
cout << st.empty(); // 0 (false)
MethodComplexityDescription
push(val)O(1)Add to top
pop()O(1)Remove from top (does NOT return value)
top()O(1)View top element
size() / empty()O(1)Element count / emptiness check

FIFO (First In, First Out) adaptor. Wraps deque by default.

#include <queue>
queue<int> q;
q.push(10);
q.push(20);
q.push(30);
cout << q.front(); // 10
cout << q.back(); // 30
q.pop();
cout << q.front(); // 20
cout << q.size(); // 2
MethodComplexityDescription
push(val)O(1)Add to back
pop()O(1)Remove from front (does NOT return value)
front() / back()O(1)View front / back element
size() / empty()O(1)Element count / emptiness check

Max-heap by default — top() always returns the largest element.

#include <queue>
priority_queue<int> pq;
pq.push(3);
pq.push(1);
pq.push(4);
pq.push(1);
pq.push(5);
cout << pq.top(); // 5
pq.pop();
cout << pq.top(); // 4
// Min-heap: reverse the ordering
priority_queue<int, vector<int>, greater<int>> minPq;
minPq.push(3); minPq.push(1); minPq.push(4);
cout << minPq.top(); // 1
MethodComplexityDescription
push(val)O(log n)Insert element
pop()O(log n)Remove top element
top()O(1)View top (largest by default) element

Ordered key-value store. Keys are unique and sorted. Implemented as a red-black tree.

#include <map>
map<string, int> ages;
ages["Alice"] = 30;
ages["Bob"] = 25;
ages.insert({"Charlie", 35});
cout << ages["Alice"]; // 30
cout << ages.at("Bob"); // 25 (throws if key missing)
cout << ages.count("Eve"); // 0 (not found)
ages.erase("Bob");
for (auto& [name, age] : ages)
cout << name << ": " << age << endl;
// Alice: 30, Charlie: 35 (sorted by key)
auto it = ages.find("Alice");
if (it != ages.end())
cout << it->second; // 30
MethodComplexityDescription
operator[key]O(log n)Access/insert (creates default if missing)
at(key)O(log n)Access (throws out_of_range if missing)
insert({k, v})O(log n)Insert if key doesn’t exist
erase(key) / erase(it)O(log n)Remove by key or iterator
find(key)O(log n)Returns iterator (or end() if not found)
count(key)O(log n)Returns 0 or 1
size() / empty()O(1)Element count / emptiness check

Hash map. Same interface as map but O(1) average lookup instead of O(log n). Keys are not sorted.

#include <unordered_map>
unordered_map<string, int> scores;
scores["Alice"] = 95;
scores["Bob"] = 87;
// Same API as map: [], at(), find(), erase(), count(), size()
mapunordered_map
LookupO(log n)O(1) average, O(n) worst
InsertionO(log n)O(1) average
Ordered iterationYes (by key)No
MemoryLessMore (hash table overhead)
Use whenNeed sorted keys or range queriesNeed fastest lookup

Ordered unique collection. Elements are sorted and deduplicated. Implemented as a red-black tree.

#include <set>
set<int> s = {3, 1, 4, 1, 5, 9, 2, 6};
// s = {1, 2, 3, 4, 5, 6, 9} — sorted, duplicates removed
s.insert(7); // {1, 2, 3, 4, 5, 6, 7, 9}
s.erase(4); // {1, 2, 3, 5, 6, 7, 9}
cout << s.count(3); // 1 (present)
cout << s.count(4); // 0 (removed)
auto it = s.find(5);
if (it != s.end())
cout << *it; // 5
// Range: elements >= 3 and < 7
auto lo = s.lower_bound(3); // iterator to 3
auto hi = s.lower_bound(7); // iterator to 7
for (auto it = lo; it != hi; ++it)
cout << *it << " "; // 3 5 6
MethodComplexityDescription
insert(val)O(log n)Add element (ignored if duplicate)
erase(val) / erase(it)O(log n)Remove element
find(val)O(log n)Returns iterator or end()
count(val)O(log n)Returns 0 or 1
lower_bound(val)O(log n)Iterator to first element >= val
upper_bound(val)O(log n)Iterator to first element > val

unordered_set provides the same interface with O(1) average operations but no ordering.

Like map and set but allow duplicate keys/values.

#include <map>
#include <set>
multimap<string, int> grades;
grades.insert({"Alice", 90});
grades.insert({"Alice", 85}); // duplicate key OK
grades.insert({"Bob", 92});
cout << grades.count("Alice"); // 2
// Iterate all values for a key
auto [lo, hi] = grades.equal_range("Alice");
for (auto it = lo; it != hi; ++it)
cout << it->second << " "; // 90 85
multiset<int> ms = {1, 1, 2, 3, 3, 3};
cout << ms.count(3); // 3

pair holds two values. tuple holds any number of values. Both are in <utility> and <tuple>.

#include <utility>
#include <tuple>
pair<string, int> p = {"Alice", 30};
cout << p.first << " " << p.second; // Alice 30
tuple<int, double, string> t = {1, 3.14, "hello"};
cout << get<0>(t); // 1
cout << get<2>(t); // hello
// Structured bindings (C++17)
auto [name, age] = p;
auto [x, y, z] = t;

Fixed-size bit array for compact boolean storage and bitwise operations. Size is a compile-time constant.

#include <bitset>
bitset<8> b("10110011");
cout << b; // 10110011
cout << b.count(); // 5 (number of 1-bits)
cout << b.test(0); // 1 (bit at position 0)
b.flip(); // 01001100
b.set(7); // 11001100
b.reset(7); // 01001100
cout << b.to_ulong(); // 76
ContainerAccessInsert/Remove (end)Insert/Remove (middle)Find
vectorO(1)Amortized O(1)O(n)O(n)
dequeO(1)O(1) both endsO(n)O(n)
listO(n)O(1) both endsO(1) at iteratorO(n)
stack / queueO(1) top/frontO(1)N/AN/A
priority_queueO(1) topO(log n)N/AN/A
map / setO(log n)O(log n)O(log n)O(log n)
unordered_map / setO(1) avgO(1) avgO(1) avgO(1) avg

RFC 959, Section STL Containers — "cppreference.com"

Iterators are the standard way to traverse containers. Every STL container provides begin() and end() iterators.

vector<int> v = {10, 20, 30, 40};
// Iterator loop
for (auto it = v.begin(); it != v.end(); ++it) {
cout << *it << " ";
}
// Range-based for (preferred)
for (int x : v) cout << x << " ";
// Reverse iteration
for (auto it = v.rbegin(); it != v.rend(); ++it) {
cout << *it << " ";
}

Include <algorithm> and <numeric>. Algorithms operate on iterator ranges [begin, end).

AlgorithmDescriptionExample
sort(b, e)Sort range ascendingsort(v.begin(), v.end())
sort(b, e, cmp)Sort with custom comparatorsort(v.begin(), v.end(), greater<int>())
find(b, e, val)Find first occurrenceauto it = find(v.begin(), v.end(), 42)
count(b, e, val)Count occurrencesint n = count(v.begin(), v.end(), 0)
reverse(b, e)Reverse range in placereverse(v.begin(), v.end())
accumulate(b, e, init)Sum of range (<numeric>)int sum = accumulate(v.begin(), v.end(), 0)
transform(b, e, out, fn)Apply function to each elementtransform(v.begin(), v.end(), v.begin(), [](int x){ return x*2; })
remove_if(b, e, pred)Move non-matching to frontauto it = remove_if(v.begin(), v.end(), [](int x){ return x < 0; })
unique(b, e)Remove consecutive duplicatesauto it = unique(v.begin(), v.end())
binary_search(b, e, val)Check if value exists (sorted)bool found = binary_search(v.begin(), v.end(), 42)
min_element(b, e)Iterator to smallest elementauto it = min_element(v.begin(), v.end())
max_element(b, e)Iterator to largest elementauto it = max_element(v.begin(), v.end())
for_each(b, e, fn)Apply function to each elementfor_each(v.begin(), v.end(), [](int x){ cout << x; })
#include <algorithm>
#include <numeric>
vector<int> v = {5, 2, 8, 1, 9, 3};
sort(v.begin(), v.end()); // {1, 2, 3, 5, 8, 9}
reverse(v.begin(), v.end()); // {9, 8, 5, 3, 2, 1}
int sum = accumulate(v.begin(), v.end(), 0); // 28
auto it = find(v.begin(), v.end(), 5); // iterator to 5
bool has3 = binary_search(v.begin(), v.end(), 3); // requires sorted
// Erase-remove idiom
v.erase(remove_if(v.begin(), v.end(),
[](int x) { return x < 3; }), v.end());

RFC 959, Section STL Algorithms — "cppreference.com"

C++ provides four named cast operators that replace C-style casts with explicit intent.

CastPurpose
static_cast<T>Compile-time conversions between compatible types
dynamic_cast<T>Safe downcasting in class hierarchies (requires virtual functions)
const_cast<T>Add or remove const qualifier
reinterpret_cast<T>Bit-level reinterpretation between unrelated types
// static_cast — numeric conversion
double pi = 3.14;
int n = static_cast<int>(pi); // 3
// dynamic_cast — safe downcast
class Base { public: virtual ~Base() {} };
class Derived : public Base { public: void hello() {} };
Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b); // succeeds
if (d) d->hello();
delete b;
// const_cast — remove const
const int val = 10;
int* mutable_ptr = const_cast<int*>(&val);
// reinterpret_cast — raw reinterpretation
int x = 42;
void* vp = reinterpret_cast<void*>(&x);

RFC 959, Section Type Casting — "cppreference.com"

Namespaces group related declarations under a named scope to prevent name collisions.

namespace Math {
const double PI = 3.14159265;
double square(double x) { return x * x; }
}
// Qualified access
cout << Math::PI;
cout << Math::square(5);
// Using directive — brings all names into scope
using namespace Math;
cout << PI;
// Using declaration — brings a single name into scope
using Math::square;
cout << square(3);

RFC 959, Section Namespaces — "cppreference.com"

The preprocessor processes source files before compilation. Directives start with #.

DirectivePurpose
#include <file>Include a standard header
#include "file"Include a local header
#define NAME valueDefine a macro constant
#define F(x) exprDefine a function-like macro
#ifdef NAMECompile block if NAME is defined
#ifndef NAMECompile block if NAME is not defined
#endifEnd conditional compilation block
#pragma onceInclude guard (non-standard but widely supported)
#ifndef CONFIG_H
#define CONFIG_H
#define MAX_SIZE 100
#define SQUARE(x) ((x) * (x))
#ifdef DEBUG
#define LOG(msg) cout << msg << endl
#else
#define LOG(msg)
#endif
#endif

RFC 959, Section Preprocessor Directives — "cppreference.com"

The auto keyword (C++11) lets the compiler deduce a variable’s type from its initializer.

auto x = 42; // int
auto pi = 3.14; // double
auto name = "hello"s; // std::string (with using namespace std::string_literals)
auto v = vector<int>{1, 2, 3};
// With iterators — avoids verbose type names
map<string, vector<int>> data;
for (auto& [key, values] : data) { // structured binding (C++17)
for (auto val : values) {
cout << key << ": " << val << endl;
}
}

Decompose structs, pairs, tuples, and arrays into named variables.

pair<string, int> p = {"Alice", 30};
auto [name, age] = p;
map<string, int> scores = {{"A", 90}, {"B", 80}};
for (auto& [name, score] : scores)
cout << name << ": " << score << endl;
auto [x, y, z] = tuple{1, 2.0, "three"};

RFC 959, Section auto — "cppreference.com"

Represents a value that may or may not be present. Replaces the “return -1 or nullptr on failure” pattern.

#include <optional>
optional<int> findIndex(const vector<int>& v, int target) {
for (int i = 0; i < v.size(); i++)
if (v[i] == target) return i;
return nullopt;
}
auto idx = findIndex({10, 20, 30}, 20);
if (idx) cout << "Found at " << *idx; // Found at 1
cout << idx.value_or(-1); // 1

A type-safe union that holds one of several types.

#include <variant>
variant<int, double, string> val = "hello";
cout << get<string>(val); // hello
val = 42;
cout << get<int>(val); // 42
// Visit pattern
visit([](auto&& arg) { cout << arg; }, val);

Holds any type — like void* but type-safe.

#include <any>
any a = 42;
cout << any_cast<int>(a); // 42
a = string("hello");
cout << any_cast<string>(a); // hello

A lightweight, non-owning reference to a string. Avoids copies when you only need to read.

#include <string_view>
void print(string_view sv) { // accepts string, const char*, string_view
cout << sv << " (" << sv.size() << " chars)" << endl;
}
print("hello"); // no allocation
string s = "world";
print(s); // no copy
print(string_view(s).substr(0, 3)); // "wor", no allocation

std::function is a general-purpose function wrapper that can hold any callable.

#include <functional>
function<int(int, int)> op;
op = [](int a, int b) { return a + b; };
cout << op(3, 4); // 7
op = [](int a, int b) { return a * b; };
cout << op(3, 4); // 12
// As callback parameter
void repeat(int n, function<void()> fn) {
for (int i = 0; i < n; i++) fn();
}
repeat(3, []() { cout << "Hi "; }); // Hi Hi Hi

RFC 959, Section Modern C++ Features — "cppreference.com"

C++11 introduced built-in threading support in <thread>, <mutex>, and <future>.

#include <thread>
void work(int id) {
cout << "Thread " << id << " running" << endl;
}
thread t1(work, 1);
thread t2(work, 2);
t1.join(); // wait for t1 to finish
t2.join(); // wait for t2 to finish

A mutex prevents data races when multiple threads access shared data. lock_guard locks the mutex on construction and unlocks on destruction (RAII).

#include <mutex>
mutex mtx;
int counter = 0;
void increment(int times) {
for (int i = 0; i < times; i++) {
lock_guard<mutex> lock(mtx); // automatically unlocks at scope end
counter++;
}
}
thread t1(increment, 10000);
thread t2(increment, 10000);
t1.join(); t2.join();
cout << counter; // 20000 (guaranteed correct)

std::async runs a function asynchronously and returns a future representing the result.

#include <future>
future<int> result = async(launch::async, []() {
// expensive computation
return 42;
});
cout << result.get(); // blocks until ready, returns 42
PrimitiveHeaderPurpose
thread<thread>Run function in a new thread
mutex<mutex>Mutual exclusion lock
lock_guard<mutex><mutex>RAII mutex lock
unique_lock<mutex><mutex>Flexible RAII lock (supports deferring, unlocking)
condition_variable<condition_variable>Block thread until notified
async<future>Launch async task
future<T><future>Retrieve result of async task
atomic<T><atomic>Lock-free thread-safe variable
#include <atomic>
atomic<int> counter{0};
// safe to increment from multiple threads without a mutex
counter.fetch_add(1);

RFC 959, Section Multithreading — "cppreference.com"

int main(int argc, char* argv[]) {
cout << "Program: " << argv[0] << endl;
for (int i = 1; i < argc; i++) {
cout << "Arg " << i << ": " << argv[i] << endl;
}
return 0;
}

argc is the argument count (including the program name). argv is an array of C-strings.

./app
./app hello world
# Arg 1: hello
# Arg 2: world

RFC 959, Section Command Line Arguments — "cppreference.com"

C++ performs file operations through the <fstream> library. Two primary classes handle file I/O: ofstream for writing to files and ifstream for reading from files.

#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
// Writing to a file
ofstream outputFile("example.txt");
if (outputFile.is_open()) {
outputFile << "Hello, World!" << endl;
outputFile << 42 << endl;
outputFile.close();
}
// Reading from a file
ifstream inputFile("example.txt");
if (inputFile.is_open()) {
string line;
while (getline(inputFile, line)) {
cout << line << endl;
}
inputFile.close();
}
return 0;
}

Key file operations:

OperationDescription
open(filename)Opens a file
is_open()Checks if the file was opened successfully
close()Closes the file
getline(stream, str)Reads a line into a string
<< operatorWrites data to the file

RFC 959, Section File Handling

C++ Language Reference

A comprehensive reference combining cppreference.com, the ISO C++ standard, and community resources.