Lesson 12
OOP Encapsulation in C++ (Module 2 Conclusion)
In this module, you studied the basic concept of
encapsulation in C++'s implementation of object-oriented programming. You learned:
- Why encapsulation is central to object-oriented programming
- What classes and objects are
- How to write member functions as part of an abstract data type
- How to limit access to an ADT's member data and functions
- How a class differs from a struct
Why Encapsulation Is Central to OOP
Lesson 1 established the module's foundation: OOP in C++ is not about getters and setters everywhere — it is a set of design practices that emphasize modeling (types represent real concepts), responsibility (a type owns its invariants), and interfaces (callers use behavior, not internal representation). Encapsulation is the keystone because it lets you change implementation details without forcing changes onto every caller. When the internal representation is hidden, you can evolve a class safely — add caching, switch containers, change validation rules, or tighten invariants — without breaking the rest of the program.
Lesson 4 made the mechanism concrete: C++ provides the encapsulation mechanism to implement ADTs by packaging both the internal implementation details of a type and the externally available operations into a single unit — the class. Code that uses the ADT is called the client code. The critical property is that client code depends only on the public interface. A stack could be implemented as a fixed-length array; the publicly available operations are push() and pop(). Changing the internal implementation to a linked list does not affect how those operations are used externally — client code using the stack does not need to be rewritten when the implementation changes.
What Classes and Objects Are
Lesson 2 established the four pillars of OOP — encapsulation, inheritance, polymorphism, and abstraction — and introduced the extra design step that OOP requires: before coding algorithms, you must design types appropriate for the problem. A well-designed Account class can serve a savings account program, a checking account program, and a loan management program without modification, because the design captured the essential properties of an account rather than the specific needs of one program.
Lesson 5 defined what a class is: an extension of the C struct concept that allows functions, as well as data, to be members. A class is an expanded concept of a data structure — instead of holding only data, it can hold both data and functions. Lesson 6 defined what an object is: a region of storage with associated semantics — a specific realization of the class blueprint with its own state. If one were to compare this with a procedural language, a class would be the type and an object would be the variable. Each object of the class receives its own copy of all non-static data members; all objects of the class share the member functions. The this pointer — passed as a hidden parameter to every non-static member function — connects the shared function code to the specific object being operated on.
The module's running ch_stack example illustrated these concepts throughout: one ch_stack class definition served as the blueprint for both data and operands objects in lesson 8 — two independent instances with their own top and s[] storage, both sharing the same push(), pop(), and reset() member function code. In the game development context explored in lessons 5 and 6, this same relationship scales to sixty independent Tank objects in a World of Tanks battle — each with its own health, position, and ammunition, all sharing the same movement and targeting logic.
How to Write Member Functions as Part of an ADT
Lesson 3 established what makes a class a true ADT: all data members private, the public interface expressing operations that make sense for the abstraction, and every public function maintaining the class invariant. The predefined type int is the model: you use +, -, *, and / without knowing how the CPU implements integer arithmetic. A well-designed user-defined type aspires to the same quality — an interface so natural that callers never need to think about the implementation.
Lesson 7 covered the declaration syntax for member functions — return type, function name, parameter list, and body — and the ch_stack ADT with its six member functions: reset(), push(), pop(), top_of(), empty(), and full(). Three of these are read-only operations that return values without modifying the stack's state. Lesson 8 showed how those functions are invoked through objects using the dot operator (data.reset()) and through pointers using the arrow operator (ptr_operands->push('A')), and established that member functions defined within the struct body are implicitly inline — appropriate for short, heavily used functions like empty() and full().
The member function declaration is included in the struct declaration and invoked using access methods for structure members. The idea is that the functionality required by the struct data type should be directly included in the struct declaration — this improves encapsulation by packaging operations directly with the data representation they operate on.
How to Limit Access to an ADT's Member Data and Functions
Lesson 9 applied access specifiers to the ch_stack running example, transforming it from a transparent struct into a fully encapsulated class. Three access specifiers are available. public members are accessible from anywhere — they form the type's contract with callers. private members are accessible only from within the class itself — external code cannot read or modify them directly. protected members are accessible within the class and within any derived class — the access level designed for inheritance hierarchies.
Lesson 10 extended this to a complete ADT interface design for C++23: a strong interface includes not just public operations but semantic guarantees (what each operation means), invariants (conditions that always hold for a valid object), error behavior (what happens on misuse), const-correctness (read-only queries marked const), and ownership rules. The CharStack example demonstrated safe error signaling through try_pop() — returning false on empty rather than producing undefined behavior — and RAII-based resource management through std::vector.
The modernized ch_stack from lesson 9 applies four C++23 best practices: [[nodiscard]] on functions whose return values should not be discarded, const on read-only member functions, enum class Sentinel instead of a plain enum to prevent name leakage and implicit integer conversion, and static constexpr MAX_LEN instead of a namespace-scope constant to scope the capacity value to the class where it belongs.
How a Class Differs from a struct
Lesson 11 applied the complete module to design a Person class and established the course style rule: prefer class to struct unless all members are data members with public access. The "need to know" style places public members first and private members last — everyone needs to know the public interface, but only the class provider needs to know the private implementation details. The Person class enforces three invariants through its constructor: first name non-empty, last name non-empty, and age in [0, 150]. Because the data members are private, no external code can bypass these checks.
The only syntactic difference between struct and class is the default access level: struct members default to public; class members default to private. The lesson showed both versions of the ch_stack side by side — identical in every member declaration, function signature, and access specifier, differing only in the keyword that introduces the type. The practical rule: use struct for plain data aggregates that carry no invariants; use class for types that enforce invariants through private data and a controlled public interface.
Lesson 11 also introduced inheritance — the relationship between base classes and derived classes. The "is-a" relationship is the fundamental test: a Rectangle is-a Quadrilateral ✓; a Stack is-a Array ✗ (a stack uses an array but is not one). The university community hierarchy — five levels from CommunityMember through Employee, Faculty, and Teacher to AdministratorTeacher — demonstrated single inheritance at each level and multiple inheritance at the leaf node where an administrator who also teaches genuinely is-a both an Administrator and a Teacher.
Encapsulation Review
Encapsulation is a technique that extends the concept of abstraction from a strictly data phenomenon to both data and functions. If you think of the
data abstraction used in C — the
structure — then it is easy to grasp the idea that a class encompasses what a structure did and extends the concept to include the binding of functions into the single entity.
Abstraction is another object-oriented concept related to encapsulation and information hiding. Simply put, abstraction means dealing with the level of detail that is most appropriate to a given task — it is the process of extracting a public interface from the inner details. The driver of a car needs to interact with:
- steering,
- gas pedal, and
- brakes.
The workings of the motor, drive train, and brake subsystem do not matter to the driver. A mechanic works at a different level of abstraction, tuning the engine and bleeding the brakes. Abstraction is the process of encapsulating information with separate public and private interfaces — the private interfaces can be subject to information hiding. The important lesson is to make models understandable to the objects that have to interact with them. Ensure methods and properties have sensible names. When analyzing a system, objects typically represent nouns in the original problem, methods are normally verbs, and attributes can often be picked up as adjectives. Name classes, attributes, and methods accordingly.
C++ Programs Are Composed of Two Fundamental Elements
All C++ programs are composed of the following two fundamental elements:
- Program statements (code): the part of a program that performs actions — called functions.
- Program data: the information of the program that is affected by the program functions.
Encapsulation is an OOP concept that binds together the data and the functions that manipulate the data, keeping both safe from outside interference and misuse. Data encapsulation led to the important OOP concept of data hiding. Data encapsulation is a mechanism of bundling data and the functions that use it; data abstraction is a mechanism of exposing only the interfaces and hiding the implementation details from the user.
C++ supports the properties of encapsulation and data hiding through the creation of user-defined types — classes. A class can contain
private,
protected, and
public members. By default, all items defined in a class are private. This default enforces the encapsulation discipline automatically: unless a member is explicitly declared
public, it is hidden from client code, and client code that tries to access it will receive a compile-time error before the program ever runs.
OOP Encapsulation Quiz
Click the Quiz link below to take a multiple-choice quiz covering the topics presented in this module.
OOP Encapsulation - Quiz
