Encapsulation
Encapsulation bundles data with the operations that act on it and hides the representation behind a controlled interface, so external code depends on behavior, not layout.
Why it matters
It is what lets a class guarantee its invariants: if a Temperature’s kelvin field is private and the only mutators reject negatives, “no negative kelvin” becomes a property the compiler helps enforce, not a comment. It also preserves change freedom — you can swap a std::vector for a flat hash map internally without touching a single caller, the foundation of stable APIs and ABI in libraries.
How it works
Encapsulation is enforced through access-specifiers: private data, a public method interface, and const-correct accessors.
- Invariants are conditions a valid object always satisfies; constructors establish them and only member functions (which you control) can change state (constructors-destructors).
- Prefer behavior-rich methods over getter/setter pairs — exposing
set_x/get_xfor every field is “anemic” and leaks the representation just as plainpublicdata would. - The pImpl idiom (pointer to an implementation struct) hides all members behind an opaque pointer, giving a stable ABI and faster recompiles by breaking the header dependency.
- Encapsulation is per-class and intentional holes use friend-functions-classes; it is access control, not a security boundary.
Example
A class can validate on every mutation because no other code can reach the field:
class Percent {
int v_; // invariant: 0 <= v_ <= 100
public:
explicit Percent(int v) { set(v); }
void set(int v) { v_ = std::clamp(v, 0, 100); } // enforced here
int value() const { return v_; }
};Making v_ public would let any code write p.v_ = 999, destroying the invariant the rest of the class relies on.
Pitfalls
- Anemic getters/setters for every field encapsulate nothing — callers still couple to the field set; expose intent-level operations instead.
- Returning a non-const reference/pointer to a private member (
std::vector<int>& data()) hands out a back door that bypasses your invariants. constis shallow: aconstmethod can still mutate the pointee of a member pointer, silently breaking logical constness.- Overusing
friendorprotecteddata re-opens the encapsulation you built; grant access narrowly.