From Struct to Class
"Everyone to whom much was given, of him much will be required." — Luke 12:48
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.
Classes — Quick Check
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. class Account {
private:
double balance;
};
int main() {
Account a;
a.balance = 9999; // ← here
return 0;
} 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. set_balance(double) that lets any caller overwrite the balance to any value: is that a good idea?deposit, withdraw) that do enforce rules. Save plain setters for cases where any value really is valid. Account objects are constructed. a.deposit(50) is called on the first. What happens to the second?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.