Chapter 09 · Week 9

From Struct to Class

"Everyone to whom much was given, of him much will be required." — Luke 12:48
9

Try to break a class. The class doesn't let you.

The widget below runs the same sequence of operations on a struct (no rules) and a class (rules enforced). Try the "bad" buttons. Watch the struct accept; watch the class refuse.

Struct vs. Class — Try to Break the Rules

Both versions track a stewardship account. Click each action button below; watch what happens. The struct lets you do anything — including nonsense. The class refuses operations that violate its invariants. That's encapsulation.

STRUCT no rules · no protection
balance $100.00
Ledger
CLASS rules enforced by the compiler
balance $100.00
Ledger
The struct can't say no. Anyone, anywhere in the program, can set the balance to any value — negative, absurd, anything. The class makes balance private and only exposes methods that enforce rules. Project 9 is exactly this move on a longer scale.

Why This Matters

Welcome to Phase 2. From here forward, the data you handle gets smarter.

In Phase 1 you bundled related data into structs. A struct is passive. It just holds data. Anyone, anywhere in your program, can reach in and modify any field to anything they want, including nonsense.

A class is the fix. A class is a struct with rules attached. It hides some of its data (private) so outside code can't reach in directly. It exposes a small number of methods that are the only legitimate way to modify the data. The class can enforce its own invariants. The compiler — not your discipline — enforces that no other code bypasses them.

For the apologetics theme: stewardship. Christianity has long held that what you have isn't fundamentally yours — it's entrusted, and the entrustment carries rules. A bank account that lets anyone reach in and set the balance to whatever they want isn't really a bank account. The class encodes the rules.

The Class Declaration

class Account {
private:
    double balance;
    string owner;

public:
    void deposit(double amount) {
        if (amount > 0) balance += amount;
    }

    double get_balance() { return balance; }
};

Two access labels: private (only this class can touch it) and public (anyone can). The rule of thumb: data is private, methods are public, methods enforce rules.

Using a Class

Account a;
a.deposit(50);       // ✓ public method — works
a.balance = 9999;    // ✗ compile error — balance is private

The compiler — not you — guarantees that no outside code can corrupt the balance. That's encapsulation working.

Methods Belong to Objects

A method is a function defined inside a class. Its body uses the class's fields directly because it belongs to the object. Each object has its own state, and methods operate on the state of the object they were called on.

Account checking;
Account savings;

checking.deposit(100);
savings.deposit(2000);
// checking has $100; savings has $2000. Independent state.

Coach's Note — The shift from "data with rules I'll remember to enforce" to "data with rules the class enforces for me" is genuinely a worldview shift. If it clicks here, the next four chapters become easier. If it hasn't clicked yet, slow down. The shift is worth taking time for.

This Week's Project

You're ready for Project 9: Stewardship Account. A StewardshipAccount class with private balance/owner, public deposit/give/spend/print_statement/get_balance, guard clauses that enforce rules. A main that runs at least 10 ops across at least 2 accounts without ever touching the private fields directly.

Check Your Reps

Classes — Quick Check

Question 1 of 4
What's the practical difference between a struct and a class in C++?
Why: C++ struct and class are nearly identical. The single language-level difference is the default access level. By convention we use struct for pure data with no rules, class when there are rules to enforce — but the language doesn't require it.
Question 2 of 4
Why does this fail to compile?
class Account {
private:
    double balance;
};

int main() {
    Account a;
    a.balance = 9999;   // ← here
    return 0;
}
Why: This is encapsulation working. balance is declared private, so only methods of Account can read or write it. main is outside the class. To modify balance from outside you have to go through a public method (like deposit) that enforces the rules.
Question 3 of 4
A method named set_balance(double) that lets any caller overwrite the balance to any value: is that a good idea?
Why: If you expose a public setter that takes any value, you've effectively made balance public again — just with extra syntax. The whole point of making it private was to enforce rules. Use named methods (deposit, withdraw) that do enforce rules. Save plain setters for cases where any value really is valid.
Question 4 of 4
Two Account objects are constructed. a.deposit(50) is called on the first. What happens to the second?
Why: Each instance of a class has its own copy of every non-static field. Calling a.deposit(50) modifies a's balance only. b is independent. The fourth answer is technically true — if a field is static it's shared across all instances — but you should not use static fields for per-instance state, and we haven't introduced static yet.
YOU FINISHED. NICE WORK.

← WEEK 8: MIDTERM   ·   WEEK 10: CONSTRUCTORS →