latency.lab
Module 04

Classes & RAII

Level: coreTime: ~2 hrsPrereq: Module 03

By the end you can

Struct vs class

They are the same thing with one difference: struct members default to public, class members default to private. Convention: use struct for plain data bags (like the Order and Trade in your capstone), class for types with behavior and invariants (like the OrderBook).

A class with an invariant

account.cpp
class Account {
public:
    // constructor: runs when an Account is created
    Account(int64_t initial) : balance_(initial) {}

    void deposit(int64_t amount) { balance_ += amount; }

    bool withdraw(int64_t amount) {
        if (amount > balance_) return false;  // invariant: never negative
        balance_ -= amount;
        return true;
    }

    int64_t balance() const { return balance_; }  // const: doesn't modify

private:
    int64_t balance_;   // hidden state; only member functions touch it
};
  • Encapsulation: balance_ is private, so outside code can't set it to a bad value. The class enforces its own rules.
  • Constructor: guarantees every Account starts in a valid state.
  • Const member function: balance() const promises not to modify the object. Mark every non-mutating method const — the compiler enforces it and callers rely on it.

Member initializer lists

The : balance_(initial) part is the initializer list. It constructs members directly, before the constructor body runs. This is strictly better than assigning inside the body:

init.cpp
// GOOD: direct initialization
Widget(int x, const std::string& s) : x_(x), name_(s) {}

// WORSE: default-constructs members, then assigns — two steps, sometimes impossible
Widget(int x, const std::string& s) { x_ = x; name_ = s; }
Order gotcha

Members are initialized in declaration order, not the order you list them. Compile with -Wall and the compiler warns if your initializer list order disagrees with declaration order — fix it, because it's a real source of bugs when one member depends on another.

Destructors and RAII

The destructor runs automatically when an object is destroyed. This is where RAII lives: acquire a resource in the constructor, release it in the destructor, and the object's scope guarantees cleanup.

raii.cpp
class FileHandle {
public:
    FileHandle(const char* path) : f_(std::fopen(path, "r")) {}
    ~FileHandle() { if (f_) std::fclose(f_); }  // released automatically

    FILE* get() const { return f_; }
private:
    FILE* f_;
};

void use() {
    FileHandle fh("data.txt");
    // ... use fh.get() ...
}   // destructor runs here — file closed even if an exception is thrown

This is the pattern behind std::vector, std::string, std::unique_ptr, and std::lock_guard. Master it here and the rest of the standard library stops being mysterious.

The whole point of RAII

You never write "cleanup" code at call sites. No manual close, no manual delete, no "don't forget to unlock." The destructor does it, on every exit path, guaranteed. Resource leaks become structurally impossible for RAII-wrapped resources.

this

Inside a member function, this is a pointer to the object the function was called on. You rarely need it explicitly, but it appears in interviews and in returning *this for chaining.

Build

Exercise 04.1 — Build an RAII timer

Write a class ScopedTimer that measures how long a scope takes:

  • In the constructor, record the current time (std::chrono::steady_clock::now()).
  • In the destructor, compute elapsed nanoseconds and print them.
  • Use it by declaring one at the top of a function; when the function ends, it prints the duration automatically.
  • This exact tool measures your order book's throughput in Module 12 — you're building a real instrument.
You've reached the first gate

Modules 00–04 are the foundation. Next up is Milestone Exam I, closed-book, covering everything so far. Pass it before starting Block II.

Takeaways

  • struct = public by default (data bags); class = private by default (types with invariants).
  • Constructors guarantee a valid initial state; use the initializer list, not body assignment.
  • Members initialize in declaration order — keep the initializer list in that order.
  • Mark non-mutating methods const.
  • RAII: acquire in constructor, release in destructor. Cleanup becomes automatic and exception-safe.
Live

Loading starter code…

Checkpoint · Classes & RAII

6 questions · pass at 70%

Q01
What is the only default difference between struct and class in C++?
WhyThey are otherwise identical. struct defaults to public access, class to private. Convention uses struct for plain data and class for types with invariants.
Q02
Why prefer a member initializer list over assigning members in the constructor body?
WhyThe initializer list constructs members directly. Body assignment first default-constructs then assigns (two steps), and is outright impossible for const members, references, and types without a default constructor.
Q03
In what order are class members initialized?
WhyMembers always initialize in declaration order regardless of initializer-list order. Mismatches are a real bug source; -Wall warns about them.
Q04
When does an object's destructor run for a stack-allocated object?
WhyFor automatic (stack) objects, the destructor runs at scope exit — including when an exception unwinds the stack. This is what makes RAII exception-safe.
Q05
What resource-management guarantee does RAII give you?
WhyRAII ties the resource to object lifetime, so the destructor frees it deterministically on all exits, including exceptions — no manual cleanup needed.
Q06
Why mark int64_t balance() const as const?
WhyA const member function promises not to mutate the object. The compiler enforces this, and it allows the method to be called on const instances and const references.

Finished Module 04?

Pass the quiz, then take Milestone Exam I.

+ Note