C++ Cheatsheet
Classes
Use this C++ reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Class Basics
class Point { public: // access specifier double x, y; // data members // Constructor Point(double x, double y) : x(x), y(y) {} // member initializer list // Member function double distFromOrigin() const { // const: doesn't modify *this return std::sqrt(x*x + y*y); } }; struct Vec2 { double x, y; }; // struct: members public by default
Access specifiers:
| Specifier | Accessible from |
|---|---|
public | anywhere |
protected | class itself + derived classes |
private | class itself (and friends) |
Default: private for class, public for struct.
Constructors
class Widget { int id_; std::string name_; public: Widget() : id_(0), name_("") {} // default constructor Widget(int id, std::string n) : id_(id), name_(std::move(n)) {} // Delegating constructor (C++11); explicit blocks implicit conversion explicit Widget(int id) : Widget(id, "unnamed") {} // Default / delete Widget(const Widget&) = default; // explicitly generate Widget& operator=(const Widget&) = delete; // forbid copy-assign }; Widget a; // default constructor Widget b(1, "foo"); // parameterized Widget c = Widget(2); // explicit — ok; Widget c = 2; would fail (explicit) Widget d{3, "bar"}; // brace-init (aggregate or list-init)
Member initializer list runs before the constructor body; always prefer it over assignment in the body (especially for const members and references).
Copy and Move (The Rule of Five)
If you define any of these, consider all five:
class Buffer { int* data_; std::size_t size_; public: Buffer(std::size_t n) : data_(new int[n]), size_(n) {} // 1. Destructor ~Buffer() { delete[] data_; } // 2. Copy constructor Buffer(const Buffer& o) : data_(new int[o.size_]), size_(o.size_) { std::copy(o.data_, o.data_ + o.size_, data_); } // 3. Copy assignment Buffer& operator=(const Buffer& o) { if (this != &o) { delete[] data_; data_ = new int[o.size_]; size_ = o.size_; std::copy(o.data_, o.data_ + o.size_, data_); } return *this; } // 4. Move constructor Buffer(Buffer&& o) noexcept : data_(o.data_), size_(o.size_) { o.data_ = nullptr; o.size_ = 0; } // 5. Move assignment Buffer& operator=(Buffer&& o) noexcept { if (this != &o) { delete[] data_; data_ = o.data_; size_ = o.size_; o.data_ = nullptr; o.size_ = 0; } return *this; } };
Rule of Zero: if your class only manages resources through RAII members (unique_ptr, vector, etc.), define none of the five — the compiler generates correct versions automatically.
Destructors
class Resource { public: ~Resource() { // called automatically when object leaves scope cleanup(); // or when delete is called } }; // Virtual destructor required in polymorphic base classes: class Base { public: virtual ~Base() = default; // without this, delete on Base* is UB };
Operator Overloading
class Vec2 { public: double x, y; Vec2(double x, double y) : x(x), y(y) {} // Arithmetic (member or free; free preferred for symmetry) Vec2 operator+(const Vec2& o) const { return {x+o.x, y+o.y}; } Vec2 operator-(const Vec2& o) const { return {x-o.x, y-o.y}; } Vec2 operator*(double s) const { return {x*s, y*s}; } Vec2 operator-() const { return {-x, -y}; } // unary // Compound assignment Vec2& operator+=(const Vec2& o) { x += o.x; y += o.y; return *this; } // Comparison (C++20: define <=> and == for all six) bool operator==(const Vec2& o) const { return x==o.x && y==o.y; } auto operator<=>(const Vec2& o) const = default; // C++20 spaceship // Subscript double& operator[](int i) { return i==0 ? x : y; } const double& operator[](int i) const { return i==0 ? x : y; } // Call double operator()(int i) const { return i==0 ? x : y; } // Output (free function, needs friend) friend std::ostream& operator<<(std::ostream& os, const Vec2& v) { return os << "(" << v.x << ", " << v.y << ")"; } // Prefix / postfix increment Vec2& operator++() { ++x; ++y; return *this; } // prefix Vec2 operator++(int) { Vec2 tmp(*this); ++*this; return tmp; } // postfix }; // Scalar * Vec2 (cannot be member since left operand is double) Vec2 operator*(double s, const Vec2& v) { return v * s; }
Operators that must be members: =, [], (), ->.
Operators that should be free: <<, >>, binary arithmetic.
Inheritance
class Shape { public: virtual double area() const = 0; // pure virtual → Shape is abstract virtual void draw() const { std::cout << "Shape\n"; } // can override virtual ~Shape() = default; protected: std::string color_; }; class Circle : public Shape { // public inheritance (IS-A) double r_; public: explicit Circle(double r) : r_(r) {} double area() const override { return 3.14159 * r_ * r_; } void draw() const override { Shape::draw(); // call base version std::cout << "Circle r=" << r_ << "\n"; } }; // Multiple inheritance class A { public: virtual void f(); }; class B { public: virtual void f(); }; class C : public A, public B { void f() override { A::f(); } // disambiguate };
Inheritance types: public (IS-A), protected, private (implementation detail).
override — compile error if no matching virtual in base (use always).
final — prevents further overriding or derivation.
class Leaf final : public Shape { // cannot derive from Leaf double area() const final override; // cannot override area in subclasses };
Virtual Dispatch and vtable
Shape* s = new Circle(5.0); s->area(); // dynamic dispatch → Circle::area() via vtable delete s; // calls Circle::~Circle() because destructor is virtual // Pure virtual: class with at least one pure virtual cannot be instantiated // Shape s; // error
Static Members
class Counter { static int count_; // declaration; one copy per class public: Counter() { ++count_; } ~Counter() { --count_; } static int count() { return count_; } // static function: no this }; int Counter::count_ = 0; // definition outside class (non-inline) Counter::count(); // call without object
Since C++17, static data members can be inline:
class Cfg { public: inline static int limit = 100; // defined in class body };
const Member Functions and mutable
class Cache { mutable int hits_ = 0; // mutable: modifiable even in const context int data_ = 0; public: int get() const { ++hits_; // OK because hits_ is mutable return data_; } };
Friend
class Secret { int value_ = 42; friend class Printer; // class friend friend std::ostream& operator<<(std::ostream&, const Secret&); // free fn friend };
Nested Classes and this
class Outer { public: class Inner { // nested class; no implicit access to Outer members void f(); }; void show() { std::cout << this->id_; // explicit this pointer } private: int id_ = 0; };
Aggregate Initialization and Designated Initializers (C++20)
struct Config { int width = 800; int height = 600; bool fullscreen = false; }; Config c1{1920, 1080, true}; // positional Config c2{.width=1920, .height=1080}; // designated (C++20); unspecified → default
Class Templates (see also Templates)
template<typename T> class Stack { std::vector<T> data_; public: void push(T val) { data_.push_back(std::move(val)); } T pop() { T v = std::move(data_.back()); data_.pop_back(); return v; } bool empty() const { return data_.empty(); } }; Stack<int> si; Stack<std::string> ss;
RAII Pattern
Resource Acquisition Is Initialization — acquire in constructor, release in destructor. The destructor always runs, even on exceptions.
class FileHandle { FILE* f_; public: explicit FileHandle(const char* path) : f_(std::fopen(path, "r")) { if (!f_) throw std::runtime_error("open failed"); } ~FileHandle() { if (f_) std::fclose(f_); } FileHandle(const FileHandle&) = delete; FileHandle& operator=(const FileHandle&) = delete; FILE* get() const { return f_; } };