Skip to content
Laviyx Learn

Try

C++ OOP and Memory Cheat Sheet

Classes, constructors, inheritance, virtual functions, references, pointers, smart pointers, and move semantics.

Updated

Class basics#

C++
class Account {
public:
    explicit Account(std::string owner, double balance = 0)
        : owner_(std::move(owner)), balance_(balance) {}   // initializer list

    void deposit(double amount) {
        if (amount > 0) balance_ += amount;
    }
    double balance() const { return balance_; }   // const: does not modify

private:
    std::string owner_;
    double balance_ = 0;                            // default member value
};

Account a("Asha", 500);
a.deposit(250);
a.balance();    // 750
AccessVisible to
publiceveryone
protectedthe class and derived classes
privatethe class only (default for class)

struct is the same as class, except members are public by default.

Constructors and destructors#

C++
class Timer {
public:
    Timer() = default;                       // keep the default constructor
    explicit Timer(int seconds) : s_(seconds) {}
    Timer(int m, int s) : Timer(m * 60 + s) {}   // delegating constructor
    ~Timer() {}                              // runs automatically at end of scope

private:
    int s_ = 0;
};
  • Members are initialized in declaration order.
  • Objects are destroyed in reverse order of construction.
  • Mark one-argument constructors explicit.

Inheritance and polymorphism#

C++
class Shape {
public:
    virtual ~Shape() = default;              // always for polymorphic bases
    virtual double area() const = 0;         // pure virtual: Shape is abstract
    virtual std::string name() const { return "shape"; }
};

class Circle : public Shape {
public:
    explicit Circle(double r) : r_(r) {}
    double area() const override { return 3.14159 * r_ * r_; }
    std::string name() const override { return "circle"; }

private:
    double r_;
};

std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(2.0));
for (const auto& s : shapes) s->area();      // calls Circle::area
  • Polymorphism needs a reference or pointer to the base; passing by value slices.
  • Write override on every overriding function.
  • Base::method() calls the base version from a derived class.

Operator overloading#

C++
struct Vec2 {
    double x = 0, y = 0;
    Vec2& operator+=(const Vec2& o) { x += o.x; y += o.y; return *this; }
    bool operator==(const Vec2&) const = default;   // C++20
    auto operator<=>(const Vec2&) const = default;  // <, >, <=, >= (C++20)
};
Vec2 operator+(Vec2 a, const Vec2& b) { return a += b; }
std::ostream& operator<<(std::ostream& out, const Vec2& v) {
    return out << '(' << v.x << ", " << v.y << ')';
}

References and pointers#

C++
int x = 10;
int& ref = x;             // alias: must be initialized, never null
int* ptr = &x;            // address of x
*ptr = 20;                // write through the pointer
ptr = nullptr;            // pointers can be null...
if (ptr) { }              // ...so check before using

const int* p1 = &x;       // can't change *p1
int* const p2 = &x;       // can't change p2 itself
ParameterUse for
T valuesmall types, or a copy you will keep
const T& valuereading larger objects
T& valuemodifying the caller's object
T* valueoptional object (may be nullptr)

Smart pointers (<memory>)#

C++
auto u = std::make_unique<Account>("Asha");   // one owner
auto u2 = std::move(u);                       // transfer ownership; u is now null

auto s = std::make_shared<Account>("Ravi");   // shared ownership
auto s2 = s;                                  // s.use_count() == 2

std::weak_ptr<Account> w = s;                 // observe without owning
if (auto locked = w.lock()) { }               // use if still alive

Avoid naked new and delete; use containers and smart pointers (RAII).

Move semantics#

C++
std::string a = "large text";
std::string b = std::move(a);        // steals a's buffer; a is valid but unspecified

class Buffer {
public:
    Buffer(Buffer&& other) noexcept;              // move constructor
    Buffer& operator=(Buffer&& other) noexcept;   // move assignment
};
  • Rule of Zero: prefer members that manage themselves, and write none of the special members.
  • Rule of Five: if you write one of destructor / copy ctor / copy assignment / move ctor / move assignment, consider all five.
  • Mark moves noexcept; return locals by value (not return std::move(x);).

Lambdas#

C++
auto square = [](int x) { return x * x; };
int limit = 10;
auto above = [limit](int x) { return x > limit; };    // capture by value
int total = 0;
auto add = [&total](int x) { total += x; };           // capture by reference
auto counter = [n = 0]() mutable { return ++n; };      // init capture + mutable
std::function<int(int)> f = square;                   // store any callable

Exceptions#

C++
try {
    throw std::invalid_argument("bad value");
} catch (const std::invalid_argument& e) {    // most specific first
    std::cout << e.what();
} catch (const std::exception& e) {           // then the general case
} catch (...) {                               // anything else
}

Throw by value, catch by const&, rethrow with throw;, and never throw from a destructor.

Want the full explanations? Go to the Learn C++ course →