cpp
Encapsulation
02_Encapsulation⚙️cpp
/**
* ============================================================
* C++ ENCAPSULATION
* ============================================================
*
* This file covers:
* - What is encapsulation
* - Data hiding
* - Getter and setter methods
* - Validation in setters
* - Benefits of encapsulation
*
* Compile: g++ -std=c++17 -Wall 01_encapsulation.cpp -o encapsulation
* Run: ./encapsulation
*
* ============================================================
*/
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// ============================================================
// BAD EXAMPLE: NO ENCAPSULATION
// ============================================================
struct BadDate {
int day;
int month;
int year;
// Problems:
// - Can set invalid values: day = 50, month = 15
// - No validation
// - Hard to change internal representation
};
// ============================================================
// GOOD EXAMPLE: PROPER ENCAPSULATION
// ============================================================
class Date {
private:
int day;
int month;
int year;
// Private helper method
bool isValidDate(int d, int m, int y) const {
if (y < 1 || m < 1 || m > 12 || d < 1) return false;
int daysInMonth[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// Leap year check
if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) {
daysInMonth[2] = 29;
}
return d <= daysInMonth[m];
}
public:
// Constructor with validation
Date(int d = 1, int m = 1, int y = 2000) {
if (isValidDate(d, m, y)) {
day = d;
month = m;
year = y;
} else {
// Set to default valid date
day = 1;
month = 1;
year = 2000;
cout << "Invalid date, set to default 01/01/2000" << endl;
}
}
// Getters
int getDay() const { return day; }
int getMonth() const { return month; }
int getYear() const { return year; }
// Setters with validation
bool setDay(int d) {
if (isValidDate(d, month, year)) {
day = d;
return true;
}
return false;
}
bool setMonth(int m) {
if (isValidDate(day, m, year)) {
month = m;
return true;
}
return false;
}
bool setYear(int y) {
if (isValidDate(day, month, y)) {
year = y;
return true;
}
return false;
}
bool setDate(int d, int m, int y) {
if (isValidDate(d, m, y)) {
day = d;
month = m;
year = y;
return true;
}
return false;
}
// Display
void display() const {
cout << (day < 10 ? "0" : "") << day << "/"
<< (month < 10 ? "0" : "") << month << "/"
<< year;
}
};
// ============================================================
// EXAMPLE: EMPLOYEE CLASS WITH ENCAPSULATION
// ============================================================
class Employee {
private:
int id;
string name;
double salary;
string department;
static int nextId; // Auto-increment ID
public:
// Constructor
Employee(const string& name, double salary, const string& dept)
: id(nextId++), name(name), salary(salary > 0 ? salary : 0), department(dept) {}
// Getters
int getId() const { return id; }
string getName() const { return name; }
double getSalary() const { return salary; }
string getDepartment() const { return department; }
// Setters with validation
void setName(const string& newName) {
if (!newName.empty()) {
name = newName;
}
}
bool setSalary(double newSalary) {
if (newSalary >= 0) {
salary = newSalary;
return true;
}
return false;
}
void setDepartment(const string& dept) {
department = dept;
}
// Business methods
void giveRaise(double percentage) {
if (percentage > 0 && percentage <= 100) {
salary += salary * (percentage / 100);
}
}
void display() const {
cout << "ID: " << id << endl;
cout << "Name: " << name << endl;
cout << "Department: " << department << endl;
cout << "Salary: $" << salary << endl;
}
};
int Employee::nextId = 1001;
// ============================================================
// EXAMPLE: TEMPERATURE CLASS WITH UNIT CONVERSION
// ============================================================
class Temperature {
private:
double kelvin; // Store internally in Kelvin
// Conversion helpers
static double celsiusToKelvin(double c) { return c + 273.15; }
static double fahrenheitToKelvin(double f) { return (f - 32) * 5/9 + 273.15; }
static double kelvinToCelsius(double k) { return k - 273.15; }
static double kelvinToFahrenheit(double k) { return (k - 273.15) * 9/5 + 32; }
public:
// Constructors for different units
static Temperature fromCelsius(double c) {
Temperature t;
t.kelvin = celsiusToKelvin(c);
return t;
}
static Temperature fromFahrenheit(double f) {
Temperature t;
t.kelvin = fahrenheitToKelvin(f);
return t;
}
static Temperature fromKelvin(double k) {
Temperature t;
t.kelvin = k;
return t;
}
// Getters in different units
double getCelsius() const { return kelvinToCelsius(kelvin); }
double getFahrenheit() const { return kelvinToFahrenheit(kelvin); }
double getKelvin() const { return kelvin; }
// Setters in different units
void setCelsius(double c) { kelvin = celsiusToKelvin(c); }
void setFahrenheit(double f) { kelvin = fahrenheitToKelvin(f); }
void setKelvin(double k) { kelvin = k; }
// Display all units
void displayAll() const {
cout << getCelsius() << "°C = "
<< getFahrenheit() << "°F = "
<< getKelvin() << "K" << endl;
}
private:
Temperature() : kelvin(0) {} // Private default constructor
};
// ============================================================
// EXAMPLE: BANK ACCOUNT WITH TRANSACTION LOGGING
// ============================================================
class SecureBankAccount {
private:
string accountNumber;
string ownerName;
double balance;
string pin;
vector<string> transactionHistory;
bool locked;
int failedAttempts;
void log(const string& message) {
transactionHistory.push_back(message);
}
public:
SecureBankAccount(const string& accNum, const string& owner, const string& pinCode)
: accountNumber(accNum), ownerName(owner), balance(0),
pin(pinCode), locked(false), failedAttempts(0) {
log("Account created");
}
// Getters (no setter for account number - immutable)
string getAccountNumber() const { return accountNumber; }
string getOwnerName() const { return ownerName; }
double getBalance() const { return balance; }
bool isLocked() const { return locked; }
// Operations with PIN verification
bool deposit(double amount, const string& inputPin) {
if (locked) {
cout << "Account is locked!" << endl;
return false;
}
if (inputPin != pin) {
failedAttempts++;
log("Failed deposit attempt - wrong PIN");
if (failedAttempts >= 3) {
locked = true;
log("Account locked due to too many failed attempts");
}
return false;
}
failedAttempts = 0;
if (amount > 0) {
balance += amount;
log("Deposited: $" + to_string(amount));
return true;
}
return false;
}
bool withdraw(double amount, const string& inputPin) {
if (locked) {
cout << "Account is locked!" << endl;
return false;
}
if (inputPin != pin) {
failedAttempts++;
log("Failed withdrawal attempt - wrong PIN");
if (failedAttempts >= 3) {
locked = true;
log("Account locked due to too many failed attempts");
}
return false;
}
failedAttempts = 0;
if (amount > 0 && amount <= balance) {
balance -= amount;
log("Withdrawn: $" + to_string(amount));
return true;
}
return false;
}
bool changePin(const string& oldPin, const string& newPin) {
if (oldPin != pin) {
log("Failed PIN change attempt");
return false;
}
if (newPin.length() >= 4) {
pin = newPin;
log("PIN changed successfully");
return true;
}
return false;
}
void printStatement() const {
cout << "\n=== Account Statement ===" << endl;
cout << "Account: " << accountNumber << endl;
cout << "Owner: " << ownerName << endl;
cout << "Balance: $" << balance << endl;
cout << "\nTransaction History:" << endl;
for (const auto& entry : transactionHistory) {
cout << " - " << entry << endl;
}
}
};
// ============================================================
// MAIN FUNCTION
// ============================================================
int main() {
cout << "============================================" << endl;
cout << " C++ ENCAPSULATION" << endl;
cout << "============================================" << endl << endl;
// ========================================================
// DEMO 1: Bad vs Good Encapsulation
// ========================================================
cout << "--- DEMO 1: BAD VS GOOD ENCAPSULATION ---" << endl << endl;
cout << "Bad (no encapsulation):" << endl;
BadDate badDate;
badDate.day = 50; // Invalid but allowed!
badDate.month = 15; // Invalid but allowed!
cout << " BadDate: " << badDate.day << "/" << badDate.month << "/" << badDate.year << endl;
cout << "\nGood (with encapsulation):" << endl;
Date goodDate(50, 15, 2024); // Invalid values rejected
cout << " GoodDate: ";
goodDate.display();
cout << endl;
cout << "\nSetting valid date:" << endl;
goodDate.setDate(15, 8, 2024);
cout << " GoodDate: ";
goodDate.display();
cout << endl;
cout << "\nTrying to set invalid day:" << endl;
bool success = goodDate.setDay(35);
cout << " setDay(35) returned: " << (success ? "true" : "false") << endl;
cout << " Date unchanged: ";
goodDate.display();
cout << endl;
cout << endl;
// ========================================================
// DEMO 2: Employee with Auto-ID
// ========================================================
cout << "--- DEMO 2: EMPLOYEE ENCAPSULATION ---" << endl << endl;
Employee emp1("Alice Smith", 50000, "Engineering");
Employee emp2("Bob Jones", 45000, "Marketing");
Employee emp3("Charlie Brown", -1000, "Sales"); // Negative salary
cout << "Employee 1:" << endl;
emp1.display();
cout << "\nEmployee 2:" << endl;
emp2.display();
cout << "\nEmployee 3 (tried negative salary):" << endl;
emp3.display();
cout << "\nGiving 10% raise to Employee 1:" << endl;
emp1.giveRaise(10);
cout << "New salary: $" << emp1.getSalary() << endl;
cout << endl;
// ========================================================
// DEMO 3: Temperature Conversion
// ========================================================
cout << "--- DEMO 3: TEMPERATURE (INTERNAL REPRESENTATION) ---" << endl << endl;
cout << "Creating temperature from 100°C:" << endl;
Temperature temp = Temperature::fromCelsius(100);
temp.displayAll();
cout << "\nCreating temperature from 32°F:" << endl;
temp = Temperature::fromFahrenheit(32);
temp.displayAll();
cout << "\nNote: Internal storage is in Kelvin, but user" << endl;
cout << " can work with any unit transparently!" << endl;
cout << endl;
// ========================================================
// DEMO 4: Secure Bank Account
// ========================================================
cout << "--- DEMO 4: SECURE BANK ACCOUNT ---" << endl << endl;
SecureBankAccount account("ACC-12345", "John Doe", "1234");
cout << "Depositing $1000 with correct PIN:" << endl;
account.deposit(1000, "1234");
cout << "Balance: $" << account.getBalance() << endl;
cout << "\nWithdrawing $300 with correct PIN:" << endl;
account.withdraw(300, "1234");
cout << "Balance: $" << account.getBalance() << endl;
cout << "\nTrying to withdraw with wrong PIN (3 times):" << endl;
account.withdraw(100, "0000");
account.withdraw(100, "0000");
account.withdraw(100, "0000"); // Account gets locked
cout << "\nAccount locked: " << (account.isLocked() ? "Yes" : "No") << endl;
account.printStatement();
cout << endl;
// ========================================================
// ENCAPSULATION BENEFITS
// ========================================================
cout << "--- ENCAPSULATION BENEFITS ---" << endl << endl;
cout << "1. DATA HIDING" << endl;
cout << " Internal representation hidden from users" << endl;
cout << " Can change implementation without affecting users" << endl;
cout << endl;
cout << "2. VALIDATION" << endl;
cout << " Setters can validate data before accepting" << endl;
cout << " Maintains object invariants" << endl;
cout << endl;
cout << "3. CONTROL" << endl;
cout << " Control how data is accessed and modified" << endl;
cout << " Can add logging, security checks, etc." << endl;
cout << endl;
cout << "4. ABSTRACTION" << endl;
cout << " Users work with high-level interface" << endl;
cout << " Complexity hidden behind simple methods" << endl;
cout << endl;
cout << "5. MAINTAINABILITY" << endl;
cout << " Easy to modify internal logic" << endl;
cout << " Less impact on dependent code" << endl;
cout << endl;
cout << "============================================" << endl;
cout << "ENCAPSULATION SUMMARY:" << endl;
cout << "============================================" << endl;
cout << "• Hide data with private members" << endl;
cout << "• Expose through public methods" << endl;
cout << "• Validate in setters" << endl;
cout << "• Getters for read access" << endl;
cout << "• Keep internals flexible" << endl;
cout << "============================================" << endl;
return 0;
}
// ============================================================
// EXERCISES:
// ============================================================
/*
* 1. Create an 'Email' class that:
* - Validates email format in setter
* - Stores domain separately (derived from email)
* - Masks email for display (j***@example.com)
*
* 2. Create a 'Password' class that:
* - Never exposes the actual password
* - Validates strength (min length, etc.)
* - Stores hashed version internally
* - Has verify() method
*
* 3. Create a 'Rectangle' class that:
* - Ensures width and height are positive
* - Maintains area and perimeter automatically
* - Has scale() method
*
* 4. Create a 'GradeBook' class that:
* - Stores grades privately
* - Validates grades (0-100)
* - Calculates average, min, max
* - Cannot modify grades after adding
*/