Lesson 1
Encapsulation in C++: A Guide to Data Privacy
Introduction

Welcome! Today's subject is Encapsulation, a cornerstone of Object-Oriented Programming (OOP) in C++. Encapsulation is the process of bundling data and the operations that modify them into one unit — commonly an object — thereby protecting the data from unwanted alterations. This level of data protection ensures the creation of robust and maintainable software.

Prepare yourself for an exciting journey as we delve into how encapsulation functions in C++ and explore the critical role it plays in safeguarding data privacy.

Unraveling Encapsulation

Starting with the basics, encapsulation involves wrapping data and the methods that modify this data into a single compartment known as a class. It protects the internal state of an object from undesired external interference.

To illustrate, consider a C++ class representing a bank account. Without encapsulation, the account balance could be directly altered. With encapsulation, however, the balance can only change through specified methods such as deposit or withdraw.

C++
1class BankAccount { 2public: 3 void deposit(double amount); 4 void withdraw(double amount); 5 double checkBalance() const; 6 7private: 8 int accountNumber; 9 double balance; 10};
Encapsulation: Guardian of Data Privacy

Encapsulation restricts direct access to an object's data and prevents unwanted data alteration. This principle is comparable to window blinds, allowing you to look out while preventing others from peeping in.

In C++, encapsulation is achieved through access specifiers like private and public. By default, class members are private, restricting direct access. Data intended to be hidden from outside manipulation is placed under the private access specifier, while public members are accessible.

Consider a class Person with a private attribute name.

C++
1class Person { 2public: 3 Person(std::string name) : name(name) {} 4 std::string getName() const; 5 6private: 7 std::string name; // Private attribute 8}; 9 10std::string Person::getName() const { 11 return name; 12}

In this example, name is private, and getName() enables us to access it safely.

Getter and Setter Methods in Encapsulation

While encapsulating, C++ uses getter and setter methods to access or modify private attributes. A getter method retrieves an attribute's value, while a setter alters it. Let's illustrate this:

C++
1class Dog { 2public: 3 Dog(std::string name) : name(name) {} 4 void setName(std::string newName); 5 std::string getName() const; 6 7private: 8 std::string name; // Private attribute 9}; 10 11void Dog::setName(std::string newName) { 12 name = newName; 13} 14 15std::string Dog::getName() const { 16 return name; 17}

Here, setName() and getName() serve as the setter and getter methods, respectively, for the private attribute name.

Practical Application and Summary

Let's apply the principle of encapsulation to our BankAccount class, which includes private attributes like account number and balance, along with public methods for withdrawals, deposits, and balance checks.

C++
1#include <iostream> 2 3class BankAccount { 4public: 5 BankAccount(int account_no, double initial_balance) 6 : account_no(account_no), balance(initial_balance) {} 7 8 void withdraw(double amount) { 9 if (balance >= amount) { 10 balance -= amount; 11 } else { 12 std::cout << "Insufficient funds." << std::endl; 13 } 14 } 15 16 void deposit(double amount) { 17 balance += amount; 18 } 19 20 double checkBalance() const { 21 return balance; 22 } 23 24private: 25 int account_no; 26 double balance; 27}; 28 29int main() { 30 BankAccount account(1, 500.0); 31 account.withdraw(100.0); // Withdraws 100.0 from the balance 32 account.deposit(50.0); // Deposits 50.0 to the balance 33 std::cout << "Current balance: " << account.checkBalance() << std::endl; 34 // Output: Current balance: 450 35 return 0; 36}

In the above code, the BankAccount class encapsulates account details, while the public methods manage the balance in a controlled manner.

Separate Class Declaration and Implementation

In larger projects, it's common practice in C++ to separate class declarations and implementations into different files: a header file (.hpp) and a source file (.cpp). This separation improves code organization and maintainability.

Let's revisit the BankAccount class using this approach:

BankAccount.hpp

C++
1class BankAccount { 2public: 3 BankAccount(int account_no, double initial_balance); 4 void withdraw(double amount); 5 void deposit(double amount); 6 double checkBalance() const; 7 8private: 9 int account_no; 10 double balance; 11};

BankAccount.cpp

C++
1#include "BankAccount.hpp" 2#include <iostream> 3 4BankAccount::BankAccount(int account_no, double initial_balance) 5 : account_no(account_no), balance(initial_balance) {} 6 7void BankAccount::withdraw(double amount) { 8 if (balance >= amount) { 9 balance -= amount; 10 } else { 11 std::cout << "Insufficient funds." << std::endl; 12 } 13} 14 15void BankAccount::deposit(double amount) { 16 balance += amount; 17} 18 19double BankAccount::checkBalance() const { 20 return balance; 21} 22 23// Example usage in a main function 24int main() { 25 BankAccount account(1, 500.0); 26 account.withdraw(100.0); // Withdraws 100.0 from the balance 27 account.deposit(50.0); // Deposits 50.0 to the balance 28 std::cout << "Current balance: " << account.checkBalance() << std::endl; 29 // Output: Current balance: 450 30 return 0; 31}

By using separate files, you define your class interface in the header file and implement its functionality in the corresponding source file. This method helps manage complex projects by making your code more modular and easier to navigate.

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.