CodeNFacts
CodeHub
Home

All Categories


Sign In

C++ - the complete reference

From your first "Hello, World!" to smart pointers, templates, and the STL - a single-page walkthrough of the C++ language, with diagrams, code you can actually run, and a cheat sheet at the end for quick lookups.

37 topicsrunnable snippetsdiagramsinterview prep
01

Introduction to C++

C++ was created by Bjarne Stroustrup in 1985 as an extension of C, adding classes and object-oriented features. It compiles to native machine code, which is why it still powers game engines, operating systems, browsers, and trading systems where performance is non-negotiable.

It is a statically typed, compiled, multi-paradigm language: you can write procedural code, object-oriented code, or generic template code in the same file.

Key points to remember
  • C++ is a superset of C — most valid C compiles as C++.
  • Compiled languages trade a build step for raw execution speed.
  • Standardized by ISO; current widely-used revisions are C++17, C++20, C++23.
02

Features of C++

C++ blends low-level control (manual memory, pointer arithmetic) with high-level abstractions (classes, templates, STL). This dual nature is its whole identity.

Key points to remember
  • Object-oriented: classes, inheritance, polymorphism.
  • Fast: compiles directly to machine code, no VM overhead.
  • Portable: same source runs on any platform with a conforming compiler.
  • Rich standard library: containers, algorithms, iterators (the STL).
  • Fine-grained memory control via pointers, new/delete, and RAII.
03

Structure of a C++ Program

Every C++ program needs at least one function named main — execution always starts there. Headers bring in declarations you need; the compiler stitches everything together at build time.

main.cpp
#include <iostream>   // header for cin/cout
using namespace std;   // avoid typing std:: everywhere

int main() {
    cout << "Hello, World!" << endl;
    return 0;           // 0 means "exited successfully"
}
Key points to remember
  • #include pulls in a header before compilation.
  • main() is the mandatory entry point.
  • return 0 signals success to the operating system.
04

Input & Output

C++ streams model I/O as a flow of data. cin reads from standard input, cout writes to standard output, cerr writes unbuffered errors, and clog writes buffered log messages.

main.cpp
int age;
cout << "Enter your age: ";
cin >> age;
cout << "You are " << age << " years old.\n";
Key points to remember
  • << inserts into a stream, >> extracts from one.
  • cin >> stops at whitespace — use getline() for full lines.
  • cerr is unbuffered, so error messages appear immediately.
05

Variables

A variable is a named, typed storage location. C++ requires every variable to have a declared type before use, and that type cannot change afterward.

main.cpp
int score = 0;
double price = 19.99;
char grade = 'A';
bool passed = true;
Key points to remember
  • Names are case-sensitive and cannot start with a digit.
  • Prefer initializing at declaration to avoid garbage values.
  • Scope (block, function, global, class) controls a variable's lifetime.
06

Data Types

Built-in types fall into a few families: integer types, floating-point types, character types, and bool. Sizes are platform-dependent but guaranteed minimums exist.

main.cpp
int      i = 42;        // typically 4 bytes
short    s = 10;         // >= 2 bytes
long     l = 100000L;    // >= 4 bytes
float    f = 3.14f;      // ~7 digit precision
double   d = 3.14159;    // ~15 digit precision
char     c = 'X';        // 1 byte
bool     b = false;      // true / false
void*    p = nullptr;    // typeless pointer
Key points to remember
  • Use sizeof(type) to check the exact size on your platform.
  • Prefer double over float unless memory is tight.
  • auto lets the compiler deduce the type from the initializer.
07

Operators

Operators combine into expressions following precedence and associativity rules, the same way arithmetic order-of-operations works on paper.

main.cpp
a + b, a - b, a * b, a / b, a % b   // arithmetic
a == b, a != b, a < b, a >= b       // relational
a && b, a || b, !a                  // logical
a & b, a | b, a ^ b, ~a, a<<1, a>>1 // bitwise
a = b, a += b, a -= b               // assignment
a > b ? a : b                       // ternary
Key points to remember
  • % (modulo) only works on integer types.
  • && and || short-circuit — the right side may never evaluate.
  • Bitwise operators are distinct from logical ones (& vs &&).
08

Type Conversion

Implicit conversion happens automatically when types mix (int to double, for example). Explicit conversion — casting — is when you tell the compiler exactly what you want.

main.cpp
int x = 10;
double y = x;               // implicit: int -> double

double pi = 3.14159;
int truncated = (int)pi;    // C-style cast
int safer = static_cast<int>(pi); // preferred C++ cast
Key points to remember
  • Prefer static_cast, dynamic_cast, const_cast over C-style casts.
  • Narrowing conversions (double -> int) can silently lose data.
  • dynamic_cast is checked at runtime and used with polymorphic types.
09

Constants

A constant is a value that cannot change after initialization. Prefer const or constexpr over the older #define macro, since they respect scope and type.

main.cpp
const double PI = 3.14159;
constexpr int MAX_USERS = 100; // evaluated at compile time
Key points to remember
  • const is checked by the type system; #define is a blind text substitution.
  • constexpr guarantees the value is known at compile time.
  • Literal suffixes like 10L, 3.14f, 'a' fix a literal's type.
10

Control Statements

Control statements decide which code runs, based on a condition. if/else branches on a boolean expression; switch branches on a single value against several cases.

main.cpp
if (score >= 90) {
    grade = 'A';
} else if (score >= 80) {
    grade = 'B';
} else {
    grade = 'C';
}

switch (day) {
    case 1: cout << "Mon"; break;
    case 2: cout << "Tue"; break;
    default: cout << "Other";
}
Key points to remember
  • Forgetting break in a switch falls through to the next case.
  • switch only compares against constant integral/enum values.
  • Every if should have braces, even for one-line bodies — safer to maintain.
11

Loops

Loops repeat a block of code. Choose for when you know the iteration count, while when the condition drives it, and do-while when the body must run at least once.

main.cpp
for (int i = 0; i < 5; i++) cout << i;

int n = 5;
while (n > 0) { cout << n; n--; }

do { cout << "runs once"; } while (false);

for (int x : {1, 2, 3}) cout << x; // range-based for
Key points to remember
  • break exits a loop immediately; continue skips to the next iteration.
  • Range-based for avoids off-by-one indexing errors.
  • Infinite loops (for(;;)) are legal — used with an internal break.
12

Arrays

An array is a fixed-size, contiguous block of elements of the same type. Indexing starts at 0, and C++ does not check bounds for you.

main.cpp
int scores[5] = {90, 85, 77, 60, 95};
cout << scores[0];            // 90

int grid[2][3] = {{1,2,3}, {4,5,6}}; // 2D array
cout << grid[1][2];           // 6
Key points to remember
  • Array size is fixed at compile time (unless dynamically allocated).
  • Out-of-bounds access is undefined behavior — no automatic error.
  • Prefer std::array or std::vector in modern C++ for safety.
13

Strings

C-style strings are just char arrays terminated by \0. std::string, from the standard library, manages its own memory and offers a much richer, safer API.

main.cpp
char cstr[] = "Hi";          // C-style, null-terminated

#include <string>
std::string s = "Hello";
s += ", World!";
cout << s.length();          // 13
cout << s.substr(0, 5);      // "Hello"
Key points to remember
  • Always prefer std::string in new code.
  • s.length() and s.size() are equivalent.
  • std::string overloads + for concatenation and == for comparison.
14

Functions

A function packages a task under a name so it can be reused. Parameters can have default values, and two functions can share a name if their parameter lists differ (overloading).

main.cpp
int add(int a, int b = 10) {  // default argument
    return a + b;
}

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

add(5);        // 15 (uses default)
add(2.5, 3.5); // calls the double version
Key points to remember
  • A function needs a declaration (prototype) before it is used, or a definition above the call site.
  • Overload resolution picks the best-matching signature at compile time.
  • Pass large objects by const reference to avoid copying.
15

Recursion

A recursive function calls itself with a smaller version of the problem, and stops at a base case. Each call adds a frame to the call stack, so unbounded recursion overflows it.

main.cpp
int factorial(int n) {
    if (n <= 1) return 1;      // base case
    return n * factorial(n-1); // recursive case
}
Key points to remember
  • Every recursive function needs at least one base case.
  • Deep recursion risks a stack overflow — loops are safer for large n.
  • Some recursive problems (tree traversal, divide-and-conquer) are far clearer than their loop equivalent.
16

Pointers

A pointer stores the memory address of another variable. & takes an address, * dereferences a pointer to reach the value it points to.

Pointerpx = 10p holds x's address · can be reassigned · can be nullptrReferenceref= xref is just another name for x itself — same address, no reassignment, never null
main.cpp
int x = 10;
int* p = &x;      // p holds the address of x
cout << *p;        // 10, dereferenced value
*p = 20;            // changes x through the pointer
cout << x;          // 20

int* q = nullptr;   // points to nothing
Key points to remember
  • A pointer can be reassigned to point elsewhere; a reference cannot.
  • Always initialize pointers — an uninitialized pointer is dangerous.
  • Dereferencing nullptr or a dangling pointer is undefined behavior.
17

References

A reference is an alias for an existing variable — a second name for the same memory. Unlike a pointer, it must be bound at creation and can never be null or reseated.

main.cpp
int x = 10;
int& ref = x;   // ref IS x, just another name
ref = 20;        // x becomes 20

void increment(int& n) { n++; } // pass by reference
increment(x);   // x becomes 21, no copy made
Key points to remember
  • References cannot be null — they always refer to a valid object.
  • Pass-by-reference avoids copying and lets a function modify the caller's variable.
  • const int& lets you avoid a copy while preventing modification.
18

Dynamic Memory

new allocates memory on the heap at runtime; delete frees it. Unlike stack variables, heap memory persists until you explicitly release it — forgetting to do so is a memory leak.

Code / Text segmentcompiled instructionsData / BSS segmentglobal & static variablesHeapgrows upward — new / mallocStackgrows downward — local variables, function callsgrows ↓grows ↑
main.cpp
int* p = new int(42);   // heap allocation
cout << *p;
delete p;                 // release it
p = nullptr;               // avoid a dangling pointer

int* arr = new int[10];   // array on the heap
delete[] arr;              // must use delete[] for arrays
Key points to remember
  • Every new must be paired with exactly one delete (or delete[]).
  • Forgetting delete leaks memory; calling delete twice is undefined behavior.
  • Modern C++ prefers smart pointers over raw new/delete (see section 32).
19

Structures

A struct groups related variables of different types under one name. In C++ (unlike C), a struct can also have functions, constructors, and inheritance — the only default difference from a class is that struct members are public by default.

main.cpp
struct Point {
    int x;
    int y;
};

Point p1 = {3, 4};
cout << p1.x << ", " << p1.y;
Key points to remember
  • struct members default to public; class members default to private.
  • Use struct for simple data bundles, class when encapsulation matters.
  • Structs can be nested inside other structs or classes.
20

Enumerations

An enum gives readable names to a set of integer constants. enum class (a "scoped enum") is the modern, safer version — its values don't leak into the surrounding scope and don't implicitly convert to int.

main.cpp
enum Color { RED, GREEN, BLUE };      // classic
Color c = RED;

enum class Direction { North, South, East, West }; // scoped
Direction d = Direction::North;
Key points to remember
  • Plain enum values start at 0 unless assigned otherwise.
  • enum class prevents accidental comparisons between unrelated enums.
  • Prefer enum class in new code for type safety.
21

Object-Oriented Programming

OOP models a program as a collection of interacting objects, each bundling data (attributes) with behavior (methods). A class is the blueprint; an object is an instance built from it.

OOP4 pillarsEncapsulationbundle data + methods, hide internalsAbstractionexpose what, hide howInheritancereuse & extend a base classPolymorphismone interface, many behaviors
main.cpp
class Car {
public:
    string brand;
    void honk() { cout << brand << " says beep!"; }
};

Car myCar;
myCar.brand = "Toyota";
myCar.honk();
Key points to remember
  • The four pillars: encapsulation, abstraction, inheritance, polymorphism.
  • A class defines a type; an object is a concrete value of that type.
  • Members are accessed with the dot operator on an object, or -> on a pointer.
22

Constructors

A constructor is a special function that runs automatically when an object is created, typically to set up initial state. It shares the class name and has no return type.

main.cpp
class Point {
public:
    int x, y;
    Point() : x(0), y(0) {}           // default constructor
    Point(int a, int b) : x(a), y(b) {} // parameterized
    Point(const Point& other) = default; // copy constructor
};

Point p1;         // calls default constructor
Point p2(3, 4);    // calls parameterized constructor
Point p3 = p2;     // calls copy constructor
Key points to remember
  • A member initializer list (: x(a), y(b)) is preferred over assigning in the body.
  • The compiler generates a default and copy constructor if you define none.
  • A constructor can be overloaded just like any other function.
23

Destructors

A destructor runs automatically when an object goes out of scope or is deleted, and is the natural place to release resources the object owns (memory, files, locks) — the basis of the RAII pattern.

main.cpp
class FileHandler {
public:
    FileHandler() { cout << "Opening file\n"; }
    ~FileHandler() { cout << "Closing file\n"; } // destructor
};

{
    FileHandler f; // constructor runs
} // f goes out of scope here -> destructor runs automatically
Key points to remember
  • A destructor has no parameters and cannot be overloaded.
  • RAII (Resource Acquisition Is Initialization) ties resource lifetime to object lifetime.
  • Mark destructors virtual in a base class if you plan to delete derived objects through a base pointer.
24

Inheritance

Inheritance lets a derived class reuse and extend a base class's members. It models an "is-a" relationship: a Dog is an Animal.

SingleBaseDerivedMultilevelAB : AC : BHierarchicalBaseDerivedADerivedBMultipleBase ABase BDerived
main.cpp
class Animal {
public:
    void eat() { cout << "eating\n"; }
};

class Dog : public Animal {   // Dog inherits from Animal
public:
    void bark() { cout << "woof\n"; }
};

Dog d;
d.eat();  // inherited
d.bark(); // its own
Key points to remember
  • public inheritance keeps access levels as-is; protected/private tighten them.
  • Types: single, multilevel, multiple, hierarchical, hybrid.
  • C++ allows multiple inheritance, which other languages often avoid due to ambiguity.
25

Polymorphism

Polymorphism means "many forms": the same interface behaves differently depending on the actual object. Compile-time polymorphism is function/operator overloading; runtime polymorphism uses virtual functions resolved through a base pointer or reference.

main.cpp
class Shape {
public:
    virtual double area() const { return 0; }
};

class Circle : public Shape {
    double r;
public:
    Circle(double r) : r(r) {}
    double area() const override { return 3.14159 * r * r; }
};

Shape* s = new Circle(2.0);
cout << s->area();  // calls Circle's version at runtime
Key points to remember
  • virtual enables runtime dispatch; override documents the intent and is checked by the compiler.
  • Without virtual, the base class version always runs, even for a derived object.
  • Runtime polymorphism needs access through a pointer or reference, not a plain object.
26

Abstraction

Abstraction hides implementation detail behind a simple interface. An abstract class — one with at least one pure virtual function — defines "what" without saying "how", forcing derived classes to provide the implementation.

main.cpp
class Shape {
public:
    virtual double area() const = 0; // pure virtual -> abstract class
};

// Shape s; // ERROR: cannot instantiate an abstract class

class Square : public Shape {
    double side;
public:
    Square(double s) : side(s) {}
    double area() const override { return side * side; }
};
Key points to remember
  • An abstract class cannot be instantiated directly.
  • A derived class must override every pure virtual function to become concrete.
  • Abstraction defines a contract; encapsulation protects the data behind it.
27

Encapsulation

Encapsulation bundles data with the methods that operate on it, and restricts direct access to that data using access specifiers, exposing a controlled interface instead.

main.cpp
class BankAccount {
private:
    double balance = 0;
public:
    void deposit(double amt) {
        if (amt > 0) balance += amt;   // validated access
    }
    double getBalance() const { return balance; }
};
Key points to remember
  • private hides members from outside the class; public exposes them; protected exposes them to derived classes only.
  • Getters/setters let you validate or change internal representation later without breaking callers.
  • Encapsulation reduces the surface area for bugs by limiting who can touch what.
28

File Handling

The <fstream> header provides ifstream for reading files, ofstream for writing, and fstream for both. Streams close automatically when they go out of scope, thanks to RAII.

main.cpp
#include <fstream>

ofstream out("data.txt");
out << "Hello, file!";
out.close();

ifstream in("data.txt");
string line;
while (getline(in, line)) cout << line << "\n";
in.close();
Key points to remember
  • Always check if(file) or file.is_open() before reading/writing.
  • Streams flush and close automatically when destroyed, but closing explicitly is clearer.
  • Open in ios::binary mode for non-text data.
29

Exception Handling

Exceptions separate error-handling code from normal logic. throw raises an exception, a try block wraps code that might fail, and catch handles a specific exception type.

main.cpp
#include <stdexcept>

double divide(double a, double b) {
    if (b == 0) throw std::runtime_error("division by zero");
    return a / b;
}

try {
    cout << divide(10, 0);
} catch (const std::runtime_error& e) {
    cout << "Error: " << e.what();
}
Key points to remember
  • Catch by const reference to avoid slicing derived exception types.
  • A catch(...) block catches anything, useful as a last resort.
  • Exceptions unwind the stack, calling destructors along the way — RAII keeps this safe.
30

Templates

Templates let you write a function or class once and have the compiler generate a version for whatever type you use it with — the foundation of generic programming and the STL itself.

main.cpp
template <typename T>
T maxVal(T a, T b) {
    return (a > b) ? a : b;
}

maxVal(3, 7);        // T = int
maxVal(2.5, 1.1);     // T = double

template <typename T>
class Box {
    T value;
public:
    Box(T v) : value(v) {}
    T get() const { return value; }
};
Key points to remember
  • Templates are resolved at compile time — no runtime overhead.
  • A template only compiles for a given type when it is actually used with that type.
  • Class templates power containers like std::vector<T>.
31

STL (Standard Template Library)

The STL is a library of generic containers, iterators, and algorithms. Containers store data, iterators traverse it uniformly, and algorithms (sort, find, accumulate...) operate on any container through iterators.

SequencevectordequelistarrayAssociativemapsetmultimapmultisetUnorderedunordered_mapunordered_setAdaptersstackqueuepriority_queue
main.cpp
#include <vector>
#include <algorithm>

vector<int> nums = {5, 2, 8, 1};
sort(nums.begin(), nums.end());          // 1 2 5 8

for (int n : nums) cout << n << " ";

auto it = find(nums.begin(), nums.end(), 8);
if (it != nums.end()) cout << "found";
Key points to remember
  • Common containers: vector, list, deque, map, set, unordered_map, stack, queue.
  • Algorithms work generically across containers via iterators.
  • vector is the default choice unless you need a specific container's tradeoffs.
32

Smart Pointers

Smart pointers, from <memory>, wrap a raw pointer and automatically delete it when no longer needed — eliminating most manual new/delete bugs.

main.cpp
#include <memory>

unique_ptr<int> u = make_unique<int>(5); // sole owner
// u2 = u; // ERROR: cannot copy a unique_ptr

shared_ptr<int> s1 = make_shared<int>(10); // shared ownership
shared_ptr<int> s2 = s1;                    // ref count = 2

weak_ptr<int> w = s1; // observes without owning
Key points to remember
  • unique_ptr: exclusive ownership, cannot be copied, only moved.
  • shared_ptr: reference-counted shared ownership, freed when the count hits zero.
  • weak_ptr breaks reference cycles between shared_ptrs.
33

Lambda Functions

A lambda is an anonymous, inline function, useful for short callbacks passed to STL algorithms. The [] capture list decides which surrounding variables it can access.

main.cpp
auto add = [](int a, int b) { return a + b; };
cout << add(2, 3); // 5

int threshold = 5;
auto above = [threshold](int x) { return x > threshold; }; // capture by value

vector<int> nums = {1, 6, 3, 9};
count_if(nums.begin(), nums.end(), above);
Key points to remember
  • [] captures nothing, [=] captures all by value, [&] captures all by reference.
  • Lambdas can be stored in a variable with auto or a std::function.
  • They are heavily used as predicates for sort, find_if, count_if, and more.
34

Namespaces

A namespace groups related names to prevent collisions between identically-named identifiers from different libraries.

main.cpp
namespace math {
    int square(int x) { return x * x; }
}

cout << math::square(4); // 16
using namespace math;
cout << square(5);        // 25, now unqualified
Key points to remember
  • std is the namespace holding the entire standard library.
  • using namespace std; is convenient but risky in headers — it can cause name clashes.
  • Nested namespaces are written namespace a::b { ... } since C++17.
35

Preprocessor Directives

Directives run before compilation proper, on the raw text of the source file. #include inserts a file's contents; #define creates a macro; conditional directives include or exclude code blocks.

main.cpp
#include <iostream>       // insert a header's contents
#define MAX 100             // text-substitution macro

#ifndef HEADER_H            // include guard
#define HEADER_H
// declarations here
#endif
Key points to remember
  • Macros are blind text substitution — no type checking, so prefer const/constexpr/inline functions.
  • Include guards (or #pragma once) stop a header from being processed twice.
  • Directives start with # and don't end with a semicolon.
36

Best Practices

Idiomatic, modern C++ ("C++ Core Guidelines" style) avoids the sharp edges of the language rather than fighting them.

Key points to remember
  • Prefer smart pointers and containers over raw new/delete.
  • Mark anything that doesn't modify state const, including member functions.
  • Initialize every variable at the point of declaration.
  • Pass small types by value, large types by const reference.
  • Follow the Rule of Zero/Five: let compiler-generated special members handle resources, or define all five if you must manage one manually.
  • Prefer range-based for and STL algorithms over manual index loops.
  • Compile with warnings treated seriously (-Wall -Wextra) — most bugs show up there first.
  • Avoid using namespace std; in header files.
37

Common Interview Questions

A quick-fire set of questions that come up often in C++ interviews, useful as a final self-check.

Questions & short answers
  • What is the difference between a pointer and a reference? — A reference cannot be null or reseated; a pointer can be reassigned and can be null.
  • What is a virtual function, and why does it matter? — It enables runtime polymorphism, letting a base pointer call the derived class's override.
  • What is the difference between struct and class? — Only the default access level: public for struct, private for class.
  • What is RAII? — Tying a resource's lifetime to an object's lifetime, so it's released automatically when the object is destroyed.
  • What happens if you don't define a destructor? — The compiler generates a default one, which is fine unless your class manually owns a resource.
  • What is the Rule of Three/Five? — If you define one of destructor, copy constructor, or copy assignment, you likely need all three (plus move constructor/assignment for Five).
  • What is a memory leak? — Allocated heap memory that is never freed because the last pointer to it is lost.
  • What is the difference between shallow copy and deep copy? — A shallow copy duplicates pointers (sharing the pointed-to data); a deep copy duplicates the underlying data too.
  • What is function overloading vs overriding? — Overloading picks a function by signature at compile time; overriding replaces a virtual base function at runtime.
  • What is a dangling pointer? — A pointer that still holds the address of memory that has already been freed.

⚠ Things that trip people up most often

  • Every new needs exactly one matching delete (or use smart pointers instead).
  • = is assignment, == is comparison — mixing them up in an if is a classic bug.
  • Array bounds are not checked — an out-of-range index is silent undefined behavior.
  • A missing break in a switch falls through to the next case.
  • Returning a reference or pointer to a local variable leaves it dangling once the function returns.
  • Comparing floating-point numbers with == is unreliable — compare within a small epsilon instead.
Quick reference

Cheat Sheet

Type sizes (typical, 64-bit)

char1 byte
short2 bytes
int4 bytes
long8 bytes
float4 bytes
double8 bytes
bool1 byte
pointer8 bytes

STL container quick pick

vector<T>dynamic array, fast random access
deque<T>fast push/pop at both ends
list<T>doubly linked list, fast insert/erase
map<K,V>sorted key-value pairs, O(log n)
unordered_map<K,V>hash map, avg O(1) lookup
set<T>sorted unique elements
stack<T>LIFO adapter
queue<T>FIFO adapter
priority_queue<T>max-heap by default

Common syntax at a glance

auto x = 5;compiler deduces the type
const int x = 5;immutable value
int& r = x;reference / alias
int* p = &x;pointer to x
new T(...) / delete p;heap allocate / free
try { } catch (...) { }exception handling
[capture](args){ }lambda expression
template<typename T>generic function/class
class C : public Base {}public inheritance
virtual void f() = 0;pure virtual (abstract)
C++ Reference · 37 topics · built for quick learning and even quicker lookups