C++ OOP and Memory Cheat Sheet
Classes, constructors, inheritance, virtual functions, references, pointers, smart pointers, and move semantics.
Updated
Class basics#
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(); // 750struct is the same as class, except members are public by default.
Constructors and destructors#
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#
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
overrideon every overriding function. Base::method()calls the base version from a derived class.
Operator overloading#
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#
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 itselfSmart pointers (<memory>)#
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 aliveAvoid naked new and delete; use containers and smart pointers (RAII).
Move semantics#
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 (notreturn std::move(x);).
Lambdas#
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 callableExceptions#
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 →