Skip to content

Module 4 — Encapsulation

After this module you can design a class whose callers cannot put it into an invalid state, and justify every member of its public interface.


At a glance

Prerequisites Module 3
Estimated study time 7–9 hours
Lab Lab 4 — sealed types with enforced invariants
Exercises Three tiers — warm-up, core, challenge
Cognitive levels L2 Apply → L5 Design — exit level L5 Design
Self-assessment Module quiz

Why this module matters

Making data private is easy. Knowing why you did it is the difference between encapsulation and superstition — and the difference shows up the moment requirements change.

Consider a class that stores a member's borrowed-item count as a plain int member. Nothing stops another part of the program from setting it to -1. One month later, a second place sets it, then a third. The rule "the count is never negative" exists only in the programmer's head, and it is enforced nowhere.

Encapsulation is the practice of turning rules like that into something the compiler and the class itself enforce. You will learn to work in this order — invariant first, then interface, then representation — rather than the usual reverse of writing public fields and hoping.

A useful test you will apply repeatedly: if I changed the private representation completely, would any correct caller need to change? If yes, the interface leaked its implementation.


Learning objectives

By the end of this module you will be able to:

  1. [L2 Apply] Define the invariant of a class, and write it down in one sentence.
  2. [L5 Design] Design a class in the order: invariant → public interface → private representation.
  3. [L2 Apply] Keep data members private and expose behaviour rather than raw state.
  4. [L2 Apply] Write const member functions, and explain why a class that is not const-correct cannot be used with const objects.
  5. [L4 Analyze] Explain what mutable does, and apply the test that decides whether its use is justified.
  6. [L4 Analyze] Use friend functions or classes deliberately, and state the coupling cost of doing so.
  7. [L4 Analyze] Distinguish a getter that exposes state from a method that expresses a capability, and defend each choice with the caller's need rather than a preference.
  8. [L5 Design] Judge when a setter should not exist at all, and design the operation that replaces it.
  9. [L4 Analyze] Explain cohesion and coupling, and use them to explain why one class design is better than another.
  10. [L5 Design] Change a private representation without breaking any correct caller, and demonstrate that this is true.

This module's exit level is 5 Design: you are finished when you can produce an interface for a type, justify every public member by a caller's need, and show that a caller cannot put the object into a state its invariant forbids.


Core concepts

  • Encapsulation — bundling data with the operations on it, and controlling access to the data.
  • Abstraction — presenting what something does while hiding how it does it. Encapsulation is the mechanism; abstraction is the benefit.
  • Information hiding — a caller cannot depend on what it cannot see.
  • Invariant — the property that must hold for every valid object, at every moment its public interface is not executing. Established in constructors; preserved by every public operation.
  • Interface vs implementation — what callers may depend on, versus what may change freely.
  • const member function — declares that calling it does not modify the object's observable state, so it can be called on const objects.
  • const-correctness — marking everything that can be const as const, which turns "you modified something you should not have" into a compile error.
  • Overloading on const — providing both a const and a non-const version of the same member function, commonly for operator[].
  • mutable — allows a member to be modified inside a const member function. Legitimate for caches and locks; a red flag anywhere else.
  • friend — grants one specific function or class access to private members. Sometimes necessary (typically for operator<<), always a coupling decision.
  • Accessor vs capability — getBalance() exposes state; canAfford(price) expresses a question the class can answer. The second keeps the rule inside the class.
  • Cohesion — how single-mindedly a class does one job.
  • Coupling — how much one part of a program must know about another. The aim is high cohesion and low coupling.

Module contents

Section What it contains
Introduction (this page) Overview, prerequisites, objectives, concept inventory
Concepts and explanations Why private is not encapsulation, invariant-first design, capabilities versus accessors, the four leaks, const-correctness, mutable, friend, cohesion and coupling
Worked examples Four programs — including one interface with two representations — plus a worked interface design
Code examples examples/module-04/ — four programs; the representation swap builds twice and must diff silently
Misconceptions and mistakes Eight wrong mental models, three real messages, and the two silences
Debugging The attack test, the three silent failures, and the escalation ladder
Exercises Three tiers, including an attack analysis and a representation swap
Lab 4 Seal the running system, and justify every public member in writing
Challenge problems A rule spanning two types, a justification review, and a sealed refactor
Self-assessment Nine questions, including four verdicts on four signatures
Summary What must be retained, the terms to be able to define, and the checklist
Further reading Books and official documentation

What you can do after this module

  • Write a class and state its invariant in one sentence.
  • Refuse a set accessor with a reason, not a preference.
  • Reproduce the example where changing the private representation breaks nothing, which is the proof that encapsulation worked.
  • Explain why a const object cannot call a non-const member function, and fix it correctly rather than by casting away const.

Where this is used later

  • const-correctness is required for the operator overloads in Module 5 and for using your types in standard containers in Module 14.
  • Invariants are what inheritance must preserve in Module 6, and what contracts formalise in Module 8.
  • friend and operator<< are covered together in Module 5.
  • Cohesion and coupling are the raw material of the SOLID principles in Module 15.

Common mistakes to watch for

Symptom Cause
error: passing 'const Book' as 'this' argument discards qualifiers Calling a non-const member function on a const object. Mark the function const if it does not modify the object; do not cast the const away
A "getter" returns a reference to an internal std::vector The caller can now modify internals freely — the invariant is gone. Return a const reference, or offer query methods
Every field has a getter and a setter The class has no interface, only a public representation. Nothing is hidden, so nothing can change safely
friend added to make something compile A design decision disguised as a keyword. Consider whether the operation belongs in the class instead
An invariant "sometimes" breaks It is enforced in more than one place, or in none. Enforce it in exactly one

Skills checklist

Levels this module works at: [L2 Apply → L5 Design] — see How you will learn.

Tick an item only when the evidence exists:

  • [ ] I can state a type's invariant in one sentence, above the class — evidence: every type in your Lab 4 solution
  • [ ] I can justify every public member by a caller's need — evidence: justification.md, one row per member, with the deleted members listed too
  • [ ] I can recognise all four kinds of representation leak — evidence: self-assessment question 3, all four verdicts correct
  • [ ] I can swap a private representation without any caller changing — evidence: a silent diff between two builds of your own class
  • [ ] I can write const-correct types — evidence: a free function taking const T& that prints an entire object
  • [ ] I can judge a mutable or friend proposal against its test — evidence: the two sentences justifying each in your own code, or their removal
  • [ ] I can find a leak by attacking my own class — evidence: your attack file, with the line of each attack and what it does now
  • [ ] I can name what a caller can no longer do — evidence: the trade paragraph in challenge X3, naming who is worse off

Suggested next step

Previous: Module 3 — Constructors and Destructors · Next: Module 5 — Operators and Static Members · All modules: Modules index