Encapsulation
Encapsulation is the practice of bundling an object's data together with the functions that operate on it, and restricting direct access to that data from outside the class. Instead of letting any code anywhere reach in and change a member variable directly, you expose a controlled interface — typically a set of public methods — through which the outside world is allowed to interact with the object.
Restricting access
In C++, encapsulation is enforced with access specifiers — public, private, and protected — covered in full on the next page. For now, the key idea is simple: mark member variables private, and provide public methods for the operations you actually want to allow.
Hiding internal state behind a public interface
class BankAccount {
private:
double balance; // hidden — no outside code can touch this directly
public:
BankAccount(double initialBalance) : balance(initialBalance) {}
void deposit(double amount) {
if (amount > 0) balance += amount; // invariant enforced here
}
bool withdraw(double amount) {
if (amount <= 0 || amount > balance) return false; // reject invalid withdrawals
balance -= amount;
return true;
}
double getBalance() const {
return balance;
}
};Why this matters
Protecting invariants —
BankAccountguaranteesbalancenever goes negative because every path that changes it goes through code that checks first. Ifbalancewere public, any code anywhere could set it to an invalid value.Freedom to change the implementation — the internal representation of
BankAccount(adouble, cents as anint, a ledger of transactions) can change later without breaking any code that callsdeposit(),withdraw(), andgetBalance(). The public interface is a stable contract; the internals are free to evolve.A single place to reason about correctness — if something goes wrong with
balance, you only need to look insideBankAccount's own methods, not search the entire codebase for anywhere it might have been modified.
Getters and setters
The classic encapsulation pattern pairs a private member with public accessor (getter) and mutator (setter) methods, as getBalance() does above. Setters are the natural place to put validation logic — as deposit() and withdraw() do by rejecting invalid amounts.
What's next
The mechanics behind
public/private/protected, and how they interact with inheritance, are covered next in Access Specifiers.