Object-Oriented Programming
Access Specifiers
public, private, and protected — controlling member visibility and encapsulation
Interview: Core encapsulation concept — every OOP interview discusses access control
Access Specifiers
Access specifiers control the visibility of class members — who can read or call them. They are the primary mechanism for encapsulation: hiding implementation details and exposing only the intended interface.
| Specifier | Same Class | Derived Class | External Code | Use When |
|---|---|---|---|---|
| public | ✓ | ✓ | ✓ | Interface — callable by all users |
| protected | ✓ | ✓ | ✗ | Inheritance hooks — only derived classes |
| private | ✓ | ✗ | ✗ | Implementation details — default in class |
Encapsulation and Invariants
Making member variables private is the foundation of encapsulation. Public setters can validate input and maintain class invariants (e.g., ensuring a counter is non-negative). Public getters can return derived values without exposing the raw storage. This allows changing the internal representation without breaking code that uses the class.
struct Default vs class Default
In struct, all members and inheritance is public by default. In class, all members and inheritance is private by default. Everything else — constructors, methods, templates — is identical. Convention: use struct for passive data aggregates, class for types with invariants.
Interview Corner
Q: Why should data members almost always be private?
A: Public data members break encapsulation — any code can modify them without the class knowing, making it impossible to maintain invariants. If a member is later changed (from int to float, or to a computed value), all code accessing it must change. Private members with public accessors allow the internal implementation to change without affecting users of the class.
Q: When should you use protected vs private?
A: Use protected for members that derived classes need to access directly — extension hooks, virtual helper methods that subclasses override. Keep implementation details private even from derived classes — this gives the base class freedom to change its internals without breaking derived classes. As a guideline: if a derived class needs a member, either make it protected or provide a protected accessor.
Common Pitfalls
- Making everything public for convenience: Loses encapsulation — anyone can break invariants. Make members only as accessible as needed.
- Overusing protected: Protected data members in a base class couple derived classes tightly to the implementation — prefer providing protected methods instead.
Best Practices
- Default to
privatefor all data members — expose only what is necessary through well-designed public methods. - Prefer protected methods over protected data — derived classes should call behavior, not manipulate raw state.