Lesson 49 of 60 – Encapsulation in C++
82%

Encapsulation in C++

Encapsulation is one of the important concepts of Object-Oriented Programming (OOP). It means combining data and the functions that operate on that data inside a class and controlling how the data can be accessed from outside the class.

Note: In C++, encapsulation is commonly implemented using classes, private data members, and public member functions such as getters and setters.

1. What is Encapsulation?

Encapsulation means wrapping data and related functions together inside a class.

class Student {

private:

    int marks;

public:

    void setMarks(int m) {

        marks = m;
    }

    int getMarks() {

        return marks;
    }
};

Here, the data member marks and the functions that work with it are contained inside the Student class.

2. Why is Encapsulation Important?

Encapsulation helps control access to the internal data of an object. It can prevent outside code from directly changing data in unwanted ways.

For example, instead of allowing direct access to a bank account balance, a class can provide controlled functions such as deposit() and withdraw().

3. Encapsulation and Classes

A class provides a natural way to implement encapsulation.

class Employee {

private:

    int salary;

public:

    void setSalary(int amount) {

        salary = amount;
    }

    int getSalary() {

        return salary;
    }
};

The class keeps the data and related operations together.

4. private Data Members

A private data member cannot normally be accessed directly from outside the class.

class Student {

private:

    int marks;

};

This prevents code outside the class from directly writing:

// student.marks = 90;

when marks is private.

5. public Member Functions

Public member functions can be called from outside the class. They can provide controlled access to private data.

class Student {

private:

    int marks;

public:

    void setMarks(int m) {

        marks = m;
    }
};

The function setMarks() is accessible from outside the class.

6. Getter Function

A getter is a member function used to read the value of a private data member.

class Student {

private:

    int marks;

public:

    int getMarks() {

        return marks;
    }
};

The getter provides controlled read access to marks.

7. Setter Function

A setter is a member function used to assign or change the value of a private data member.

class Student {

private:

    int marks;

public:

    void setMarks(int m) {

        marks = m;
    }
};

A setter can also validate the supplied value before storing it.

8. Getter and Setter Together

class Student {

private:

    int marks;

public:

    void setMarks(int m) {

        marks = m;
    }

    int getMarks() {

        return marks;
    }
};

int main() {

    Student student;

    student.setMarks(85);

    std::cout <<
        student.getMarks();

    return 0;
}

The private variable is accessed through public functions.

9. Encapsulation with Validation

One benefit of a setter is that it can validate data before storing it.

class Student {

private:

    int marks;

public:

    void setMarks(int m) {

        if (m >= 0 && m <= 100) {

            marks = m;
        }
    }

    int getMarks() {

        return marks;
    }
};

Only values within the specified range are accepted by this setter.

10. Encapsulation in a Bank Account

class BankAccount {

private:

    double balance;

public:

    void deposit(double amount) {

        if (amount > 0) {

            balance += amount;
        }
    }

    double getBalance() {

        return balance;
    }
};

The balance is kept private, while public functions control how it is changed or read.

11. Preventing Invalid Data

Encapsulation can help prevent invalid values from being assigned directly.

class Product {

private:

    double price;

public:

    void setPrice(double p) {

        if (p >= 0) {

            price = p;
        }
    }

    double getPrice() {

        return price;
    }
};

A negative price can be rejected by the setter.

12. Read-Only Data with Encapsulation

A class can provide a getter without providing a setter. This allows outside code to read a value but not directly change it.

class Student {

private:

    int rollNumber;

public:

    int getRollNumber() const {

        return rollNumber;
    }
};

If no public setter exists, outside code cannot use a setter to change the value.

13. Write-Only Style Access

A class can provide a setter without providing a getter. This can be useful in specific designs where outside code should provide a value but should not directly read it back.

class SecurityCode {

private:

    int code;

public:

    void setCode(int value) {

        code = value;
    }
};

14. Encapsulation and Access Specifiers

Access Specifier Purpose
private Restricts normal direct access from outside the class.
public Provides an interface that outside code can use.
protected Allows access within the class and derived classes.

Using these access specifiers helps define which parts of a class are exposed and which parts are hidden.

15. Data Hiding

Data hiding means restricting direct access to internal data. Private members are commonly used to achieve this.

class Employee {

private:

    double salary;

public:

    void setSalary(double amount) {

        salary = amount;
    }

    double getSalary() const {

        return salary;
    }
};

The implementation details of salary are hidden from code that uses the class.

16. Encapsulation vs Data Hiding

These terms are related but describe different ideas.

  • Encapsulation: Combining data and related functions into a single unit such as a class.
  • Data Hiding: Restricting direct access to internal implementation details.

Private members are one common mechanism for supporting both concepts.

17. Encapsulation with Constructor

A constructor can initialize private members when an object is created.

class Student {

private:

    std::string name;
    int marks;

public:

    Student(
        std::string n,
        int m
    )
        : name(n),
          marks(m) {
    }

    void display() const {

        std::cout <<
            name << " "
            << marks;
    }
};

18. Encapsulation with Member Functions

A class can provide meaningful operations instead of exposing raw data.

class BankAccount {

private:

    double balance;

public:

    void deposit(double amount) {

        if (amount > 0) {

            balance += amount;
        }
    }

    bool withdraw(double amount) {

        if (amount > 0 &&
            amount <= balance) {

            balance -= amount;

            return true;
        }

        return false;
    }

    double getBalance() const {

        return balance;
    }
};

The class controls how the balance can change.

19. Why Direct Data Access Can Be a Problem

Suppose a class exposes an account balance directly:

account.balance = -5000;

This may allow an invalid state if negative balances are not permitted by the application's rules.

Encapsulation can instead provide a controlled operation:

account.withdraw(500);

The class can validate the operation before changing its internal state.

20. Encapsulation and const Functions

Getter functions are often declared const because reading data should not modify the object.

class Student {

private:

    int marks;

public:

    int getMarks() const {

        return marks;
    }
};

The const qualifier helps communicate that the function does not modify the object's state.

21. Encapsulation and Abstraction

Encapsulation and abstraction are related OOP concepts, but they are not exactly the same.

  • Encapsulation: Groups data and behavior together and controls access.
  • Abstraction: Focuses on exposing essential behavior while hiding unnecessary implementation details.

A well-designed class can use both concepts.

22. Practical Employee Example

#include <iostream>
#include <string>

class Employee {

private:

    int id;
    std::string name;
    double salary;

public:

    void setData(
        int employeeId,
        std::string employeeName,
        double employeeSalary
    ) {

        if (employeeSalary >= 0) {

            id = employeeId;
            name = employeeName;
            salary = employeeSalary;
        }
    }

    void display() const {

        std::cout << "ID: "
                  << id
                  << std::endl;

        std::cout << "Name: "
                  << name
                  << std::endl;

        std::cout << "Salary: "
                  << salary;
    }
};

int main() {

    Employee employee;

    employee.setData(
        101,
        "Rahul",
        35000
    );

    employee.display();

    return 0;
}

23. Practical Bank Account Example

#include <iostream>

class BankAccount {

private:

    double balance;

public:

    BankAccount(double amount)
        : balance(amount) {
    }

    void deposit(double amount) {

        if (amount > 0) {

            balance += amount;
        }
    }

    bool withdraw(double amount) {

        if (amount > 0 &&
            amount <= balance) {

            balance -= amount;

            return true;
        }

        return false;
    }

    double getBalance() const {

        return balance;
    }
};

int main() {

    BankAccount account(1000);

    account.deposit(500);

    account.withdraw(300);

    std::cout <<
        "Balance: "
        << account.getBalance();

    return 0;
}

24. Encapsulation in a Product Class

class Product {

private:

    std::string name;
    double price;
    int quantity;

public:

    void setName(
        std::string productName
    ) {

        name = productName;
    }

    void setPrice(double productPrice) {

        if (productPrice >= 0) {

            price = productPrice;
        }
    }

    void setQuantity(int productQuantity) {

        if (productQuantity >= 0) {

            quantity = productQuantity;
        }
    }

    double getPrice() const {

        return price;
    }
};

Each setter can apply rules before changing the private data.

25. Encapsulation with a Student Result

class Student {

private:

    int marks;

public:

    void setMarks(int m) {

        if (m >= 0 && m <= 100) {

            marks = m;
        }
    }

    int getMarks() const {

        return marks;
    }

    bool isPass() const {

        return marks >= 40;
    }
};

int main() {

    Student student;

    student.setMarks(75);

    std::cout <<
        "Marks: "
        << student.getMarks()
        << std::endl;

    if (student.isPass()) {

        std::cout << "Pass";

    } else {

        std::cout << "Fail";
    }

    return 0;
}

The class controls how marks are stored and provides meaningful operations for working with the student result.

26. Common Encapsulation Mistakes

  • Making every data member public without a design reason.
  • Providing setters that accept invalid values without validation.
  • Providing setters when data should be read-only.
  • Exposing implementation details unnecessarily.
  • Using getters and setters mechanically when a meaningful operation would be clearer.
  • Forgetting to use const for read-only member functions where appropriate.
  • Confusing encapsulation with inheritance or polymorphism.

27. Advantages of Encapsulation

  • Data Protection: Internal data can be protected from direct access.
  • Validation: Functions can validate values before storing them.
  • Maintainability: Internal implementation can change without changing the public interface.
  • Control: The class decides how its data can be accessed.
  • Readability: Meaningful functions can make object operations easier to understand.
  • Security: Sensitive internal state can be restricted from direct access.

28. Best Practices for Encapsulation

  • Keep implementation details private when outside code does not need direct access.
  • Expose a small and meaningful public interface.
  • Validate data before changing important object state.
  • Use constructors to establish valid initial state.
  • Use const member functions for operations that do not modify the object.
  • Prefer meaningful operations over unnecessary getter/setter pairs.
  • Keep each class focused on a clear responsibility.

29. Real-World Uses of Encapsulation

Encapsulation is used throughout software development.

  • Banking: Protecting account balance and controlling withdrawals.
  • Student Management: Validating marks and student information.
  • E-Commerce: Controlling product price, stock, and order status.
  • Library Systems: Managing book availability and borrowing rules.
  • Employee Systems: Controlling employee information and salary data.
  • Authentication: Controlling access to internal user-related state.

30. Encapsulation – Final Summary

Concept Meaning
Encapsulation Combining data and related functions inside a class while controlling access.
private Used to restrict normal direct access to class members.
public Used to provide an interface to outside code.
Getter Function used to read a private value.
Setter Function used to change a private value.
Data Hiding Restricting direct access to internal implementation details.
Validation Checking values before changing object state.
Abstraction Focusing on essential behavior while hiding unnecessary implementation details.
class BankAccount {

private:

    double balance;

public:

    BankAccount(double amount)
        : balance(amount) {
    }

    void deposit(double amount) {

        if (amount > 0) {

            balance += amount;
        }
    }

    double getBalance() const {

        return balance;
    }
};

int main() {

    BankAccount account(1000);

    account.deposit(500);

    std::cout <<
        account.getBalance();

    return 0;
}

📌 Key Points

  • Encapsulation combines data and related functions inside a class.
  • Private members help prevent direct external access to internal data.
  • Public functions can provide controlled access to private members.
  • Getters are commonly used to read private data.
  • Setters are commonly used to change private data.
  • Setters can validate values before changing object state.
  • A class can provide read-only access by providing a getter without a setter.
  • Constructors can establish a valid initial state.
  • Const member functions are useful for operations that do not modify an object.
  • Good encapsulation provides a clear and meaningful public interface.

🧠 Quick Quiz

Question: What is the main purpose of encapsulation in C++?