cpp

exercises

exercises.cpp⚙️
/**
 * Encapsulation in C++ - Exercises
 * 
 * Practice problems for understanding encapsulation principles.
 * Each exercise includes TODO sections to complete.
 * 
 * Compile: g++ -std=c++17 -Wall -Wextra exercises.cpp -o exercises
 * Run: ./exercises
 */

#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>

using namespace std;

// ============================================================
// Exercise 1: Basic Encapsulation ⭐
// ============================================================
/**
 * Convert this struct with public data to a properly encapsulated class.
 */

// Before (bad):
struct PersonStruct {
    string name;
    int age;
};

// TODO: Create encapsulated version
class Person {
    // TODO: Make these private
    string name;
    int age;
    
public:
    // TODO: Add constructor
    
    // TODO: Add getter for name
    
    // TODO: Add getter for age
    
    // TODO: Add setter for age with validation (0-150)
    
};

void exercise1() {
    // TODO: Uncomment when implemented
    /*
    Person p("Alice", 25);
    cout << "Name: " << p.getName() << endl;
    cout << "Age: " << p.getAge() << endl;
    
    p.setAge(30);
    cout << "Updated age: " << p.getAge() << endl;
    
    p.setAge(-5);  // Should be rejected
    cout << "After invalid age: " << p.getAge() << endl;
    */
    cout << "(Implement Person class)" << endl;
}

// ============================================================
// Exercise 2: Validation in Setters ⭐⭐
// ============================================================
/**
 * Create a Temperature class that stores temp in Kelvin internally
 * but allows getting/setting in any unit.
 */

class Temperature {
private:
    double kelvin;  // Always store internally as Kelvin
    
public:
    // TODO: Constructor that takes Celsius
    Temperature(double celsius) : kelvin(0) {
        // Convert celsius to kelvin
    }
    
    // TODO: Implement setters (validate: Kelvin >= 0)
    void setKelvin(double k) {
        // Validate and set
    }
    
    void setCelsius(double c) {
        // Convert and validate
    }
    
    void setFahrenheit(double f) {
        // Convert and validate
    }
    
    // TODO: Implement getters
    double getKelvin() const { return 0; }     // Fix
    double getCelsius() const { return 0; }    // Fix: K - 273.15
    double getFahrenheit() const { return 0; } // Fix: (K - 273.15) * 9/5 + 32
    
    void display() const {
        cout << getCelsius() << "°C = " << getFahrenheit() << "°F = " << getKelvin() << "K" << endl;
    }
};

void exercise2() {
    Temperature t(25.0);  // 25 Celsius
    cout << "Initial: ";
    t.display();
    
    t.setFahrenheit(212);  // Boiling point
    cout << "After setF(212): ";
    t.display();
    
    t.setKelvin(0);  // Absolute zero
    cout << "Absolute zero: ";
    t.display();
    
    t.setKelvin(-10);  // Invalid!
    cout << "After invalid setK(-10): ";
    t.display();
}

// ============================================================
// Exercise 3: Read-Only Properties ⭐⭐
// ============================================================
/**
 * Create a Circle class where radius can be set,
 * but area and circumference are computed (read-only).
 */

class Circle {
private:
    double radius;
    
public:
    static constexpr double PI = 3.14159265359;
    
    // TODO: Constructor with validation (radius > 0)
    Circle(double r) : radius(1.0) {
        // Validate and set
    }
    
    // TODO: Getter and setter for radius
    double getRadius() const { return radius; }
    
    void setRadius(double r) {
        // Validate r > 0
    }
    
    // TODO: Computed read-only properties
    double getArea() const {
        return 0;  // Fix: PI * r^2
    }
    
    double getCircumference() const {
        return 0;  // Fix: 2 * PI * r
    }
    
    double getDiameter() const {
        return 0;  // Fix: 2 * r
    }
};

void exercise3() {
    Circle c(5.0);
    cout << "Radius: " << c.getRadius() << endl;
    cout << "Diameter: " << c.getDiameter() << endl;
    cout << "Area: " << c.getArea() << endl;
    cout << "Circumference: " << c.getCircumference() << endl;
    
    c.setRadius(10.0);
    cout << "\nAfter setRadius(10):" << endl;
    cout << "Area: " << c.getArea() << endl;
}

// ============================================================
// Exercise 4: Const Correctness ⭐⭐
// ============================================================
/**
 * Fix the const-correctness issues in this class.
 */

class Book {
private:
    string title;
    string author;
    int pages;
    bool available;
    
public:
    Book(const string& t, const string& a, int p)
        : title(t), author(a), pages(p), available(true) {}
    
    // TODO: Add 'const' to methods that don't modify the object
    
    string getTitle() { return title; }
    string getAuthor() { return author; }
    int getPages() { return pages; }
    bool isAvailable() { return available; }
    
    void checkout() { available = false; }
    void returnBook() { available = true; }
    
    void display() {
        cout << "\"" << title << "\" by " << author 
             << " (" << pages << " pages) - " 
             << (available ? "Available" : "Checked out") << endl;
    }
};

void exercise4() {
    Book book("1984", "George Orwell", 328);
    book.display();
    
    // This should work with const object after fixing
    const Book constBook("Brave New World", "Aldous Huxley", 311);
    // TODO: Uncomment after adding const
    // constBook.display();
    // cout << "Title: " << constBook.getTitle() << endl;
}

// ============================================================
// Exercise 5: Reference Getters ⭐⭐
// ============================================================
/**
 * Create a class that efficiently exposes a large internal collection.
 */

class Inventory {
private:
    vector<string> items;
    
public:
    void addItem(const string& item) {
        items.push_back(item);
    }
    
    // TODO: Return const reference (efficient, read-only access)
    const vector<string>& getItems() {
        return items;  // Fix: make this const
    }
    
    // TODO: Return by value (safe copy)
    vector<string> getItemsCopy() {
        return items;  // This is already correct
    }
    
    // TODO: Find item (return const reference to avoid copy)
    const string& getItem(size_t index) {
        static string empty;
        if (index < items.size()) {
            return items[index];
        }
        return empty;
    }
    
    size_t count() const { return items.size(); }
};

void exercise5() {
    Inventory inv;
    inv.addItem("Laptop");
    inv.addItem("Mouse");
    inv.addItem("Keyboard");
    
    cout << "Items (via const ref):" << endl;
    for (const string& item : inv.getItems()) {
        cout << "  - " << item << endl;
    }
    
    cout << "Item at index 1: " << inv.getItem(1) << endl;
    
    // Getting a copy
    vector<string> copy = inv.getItemsCopy();
    copy.push_back("Monitor");  // Modifying copy
    
    cout << "Copy size: " << copy.size() << endl;
    cout << "Original size: " << inv.count() << " (unchanged)" << endl;
}

// ============================================================
// Exercise 6: Friend Function ⭐⭐
// ============================================================
/**
 * Create a Point class with a friend function for distance calculation.
 */

class Point {
private:
    double x, y;
    
public:
    Point(double xVal = 0, double yVal = 0) : x(xVal), y(yVal) {}
    
    double getX() const { return x; }
    double getY() const { return y; }
    
    // TODO: Declare distance as friend function
    // friend double distance(const Point& a, const Point& b);
    
    // TODO: Declare operator<< as friend
    // friend ostream& operator<<(ostream& os, const Point& p);
};

// TODO: Implement distance function (access private x, y directly)
double distance(const Point& a, const Point& b) {
    // Calculate distance using private members
    return 0;  // Fix
}

// TODO: Implement operator<<
ostream& operator<<(ostream& os, const Point& p) {
    // Print (x, y) format
    os << "(" << p.getX() << ", " << p.getY() << ")";  // Use getters for now
    return os;
}

void exercise6() {
    Point p1(3, 4);
    Point p2(6, 8);
    
    cout << "Point 1: " << p1 << endl;
    cout << "Point 2: " << p2 << endl;
    cout << "Distance: " << distance(p1, p2) << endl;
}

// ============================================================
// Exercise 7: Friend Class ⭐⭐⭐
// ============================================================
/**
 * Create an Engine class that only Mechanic can diagnose/repair.
 */

class Engine {
private:
    int horsepower;
    int mileage;
    bool needsService;
    
    // TODO: Declare Mechanic as friend
    
public:
    Engine(int hp) : horsepower(hp), mileage(0), needsService(false) {}
    
    void drive(int miles) {
        mileage += miles;
        if (mileage > 5000) {
            needsService = true;
        }
    }
    
    int getMileage() const { return mileage; }
    
    void display() const {
        cout << horsepower << "hp, " << mileage << " miles";
        if (needsService) cout << " (needs service)";
        cout << endl;
    }
};

class Mechanic {
public:
    // TODO: Implement diagnose - access private members
    void diagnose(const Engine& e) {
        cout << "Diagnosis:" << endl;
        // Print horsepower, mileage, needsService
        cout << "(Implement friend access)" << endl;
    }
    
    // TODO: Implement service - reset needsService and upgrade HP
    void service(Engine& e, int hpBoost = 0) {
        cout << "Servicing engine..." << endl;
        // Reset mileage counter, needsService flag
        // Optionally increase horsepower
    }
};

void exercise7() {
    Engine engine(200);
    Mechanic mechanic;
    
    engine.drive(6000);  // Trigger service needed
    cout << "Engine: ";
    engine.display();
    
    mechanic.diagnose(engine);
    mechanic.service(engine, 10);
    
    cout << "After service: ";
    engine.display();
}

// ============================================================
// Exercise 8: Validation with Exceptions ⭐⭐⭐
// ============================================================
/**
 * Create a class that throws exceptions for invalid data.
 */

class Email {
private:
    string address;
    
    // TODO: Validate email format (must contain @ and .)
    bool isValidFormat(const string& email) const {
        return true;  // Fix: check for @ and .
    }
    
public:
    // TODO: Constructor that validates and throws if invalid
    Email(const string& addr) : address("") {
        // Validate and throw invalid_argument if bad
        address = addr;  // Only if valid
    }
    
    const string& getAddress() const { return address; }
    
    // TODO: Setter that validates
    void setAddress(const string& addr) {
        // Validate and throw if invalid
    }
};

void exercise8() {
    try {
        Email valid("user@example.com");
        cout << "Valid email: " << valid.getAddress() << endl;
        
        Email invalid("not-an-email");  // Should throw
        cout << "This shouldn't print" << endl;
        
    } catch (const invalid_argument& e) {
        cout << "Caught exception: " << e.what() << endl;
    }
}

// ============================================================
// Exercise 9: Immutable Class ⭐⭐⭐
// ============================================================
/**
 * Create an immutable Date class (no setters).
 */

class Date {
private:
    const int day;
    const int month;
    const int year;
    
public:
    // TODO: Constructor with validation
    Date(int d, int m, int y) : day(d), month(m), year(y) {
        // Validate: month 1-12, day 1-31, etc.
    }
    
    // TODO: Only getters (no setters - immutable)
    int getDay() const { return day; }
    int getMonth() const { return month; }
    int getYear() const { return year; }
    
    // TODO: Return new Date instead of modifying
    Date addDays(int days) const {
        // Simple implementation: just add to day
        // (Real implementation would handle month/year overflow)
        return Date(day + days, month, year);
    }
    
    Date addMonths(int months) const {
        return Date(day, month + months, year);
    }
    
    void display() const {
        cout << day << "/" << month << "/" << year << endl;
    }
};

void exercise9() {
    Date birthday(15, 6, 1990);
    cout << "Birthday: ";
    birthday.display();
    
    // Cannot modify:
    // birthday.day = 20;  // ERROR: const member
    
    // Create new date instead
    Date nextMonth = birthday.addMonths(1);
    cout << "Next month: ";
    nextMonth.display();
    
    cout << "Original unchanged: ";
    birthday.display();
}

// ============================================================
// Exercise 10: Complete Encapsulated Class ⭐⭐⭐
// ============================================================
/**
 * Create a fully encapsulated BankAccount class.
 */

class BankAccount {
private:
    static int nextAccountNumber;
    
    const int accountNumber;
    string ownerName;
    double balance;
    bool locked;
    
    // TODO: Private helper to validate transaction
    bool canWithdraw(double amount) const {
        return !locked && amount > 0 && amount <= balance;
    }
    
public:
    // TODO: Constructor
    BankAccount(const string& owner, double initial = 0.0)
        : accountNumber(nextAccountNumber++), ownerName(owner), 
          balance(initial >= 0 ? initial : 0), locked(false) {}
    
    // TODO: Getters
    int getAccountNumber() const { return accountNumber; }
    const string& getOwnerName() const { return ownerName; }
    double getBalance() const { return balance; }
    bool isLocked() const { return locked; }
    
    // TODO: Operations with validation
    bool deposit(double amount) {
        if (!locked && amount > 0) {
            balance += amount;
            return true;
        }
        return false;
    }
    
    bool withdraw(double amount) {
        if (canWithdraw(amount)) {
            balance -= amount;
            return true;
        }
        return false;
    }
    
    bool transfer(BankAccount& to, double amount) {
        if (withdraw(amount)) {
            to.deposit(amount);
            return true;
        }
        return false;
    }
    
    void lock() { locked = true; }
    void unlock() { locked = false; }
    
    void display() const {
        cout << "Account #" << accountNumber << " (" << ownerName << ")" << endl;
        cout << "  Balance: $" << balance;
        if (locked) cout << " [LOCKED]";
        cout << endl;
    }
};

int BankAccount::nextAccountNumber = 10000;

void exercise10() {
    BankAccount acc1("Alice", 1000);
    BankAccount acc2("Bob", 500);
    
    acc1.display();
    acc2.display();
    
    cout << "\nAlice deposits $200:" << endl;
    acc1.deposit(200);
    cout << "Balance: $" << acc1.getBalance() << endl;
    
    cout << "\nAlice transfers $300 to Bob:" << endl;
    acc1.transfer(acc2, 300);
    acc1.display();
    acc2.display();
    
    cout << "\nLocking Alice's account:" << endl;
    acc1.lock();
    
    cout << "Trying to withdraw from locked account:" << endl;
    if (!acc1.withdraw(100)) {
        cout << "  Withdrawal failed (account locked)" << endl;
    }
    
    acc1.unlock();
    acc1.withdraw(100);
    cout << "After unlock and withdraw: $" << acc1.getBalance() << endl;
}

// ============================================================
// TEST RUNNER
// ============================================================

int main() {
    cout << "╔════════════════════════════════════════════════════════════╗" << endl;
    cout << "║                ENCAPSULATION - EXERCISES                   ║" << endl;
    cout << "╚════════════════════════════════════════════════════════════╝" << endl;
    
    cout << "\n=== Exercise 1: Basic Encapsulation ===" << endl;
    exercise1();
    
    cout << "\n=== Exercise 2: Validation in Setters ===" << endl;
    exercise2();
    
    cout << "\n=== Exercise 3: Read-Only Properties ===" << endl;
    exercise3();
    
    cout << "\n=== Exercise 4: Const Correctness ===" << endl;
    exercise4();
    
    cout << "\n=== Exercise 5: Reference Getters ===" << endl;
    exercise5();
    
    cout << "\n=== Exercise 6: Friend Function ===" << endl;
    exercise6();
    
    cout << "\n=== Exercise 7: Friend Class ===" << endl;
    exercise7();
    
    cout << "\n=== Exercise 8: Validation with Exceptions ===" << endl;
    exercise8();
    
    cout << "\n=== Exercise 9: Immutable Class ===" << endl;
    exercise9();
    
    cout << "\n=== Exercise 10: Complete Encapsulated Class ===" << endl;
    exercise10();
    
    cout << "\n╔════════════════════════════════════════════════════════════╗" << endl;
    cout << "║         Complete the TODO sections and re-run!             ║" << endl;
    cout << "╚════════════════════════════════════════════════════════════╝" << endl;
    
    return 0;
}

// ============================================================
// ANSWER KEY
// ============================================================
/*
// Exercise 1: Person
class Person {
private:
    string name;
    int age;
public:
    Person(const string& n, int a) : name(n), age(0) { setAge(a); }
    string getName() const { return name; }
    int getAge() const { return age; }
    void setAge(int a) { if (a >= 0 && a <= 150) age = a; }
};

// Exercise 2: Temperature
Temperature(double celsius) : kelvin(celsius + 273.15) {}
void setKelvin(double k) { if (k >= 0) kelvin = k; }
void setCelsius(double c) { setKelvin(c + 273.15); }
void setFahrenheit(double f) { setKelvin((f - 32) * 5.0/9.0 + 273.15); }
double getKelvin() const { return kelvin; }
double getCelsius() const { return kelvin - 273.15; }
double getFahrenheit() const { return getCelsius() * 9.0/5.0 + 32; }

// Exercise 3: Circle
Circle(double r) : radius(r > 0 ? r : 1.0) {}
void setRadius(double r) { if (r > 0) radius = r; }
double getArea() const { return PI * radius * radius; }
double getCircumference() const { return 2 * PI * radius; }
double getDiameter() const { return 2 * radius; }

// Exercise 4: Add 'const' to getTitle, getAuthor, getPages, isAvailable, display

// Exercise 5: Make getItems() const
const vector<string>& getItems() const { return items; }

// Exercise 6: Friend function
friend double distance(const Point& a, const Point& b);
double distance(const Point& a, const Point& b) {
    return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}

// Exercise 7: Friend class
friend class Mechanic;
void diagnose(const Engine& e) {
    cout << "HP: " << e.horsepower << ", Miles: " << e.mileage 
         << ", Needs service: " << e.needsService << endl;
}
void service(Engine& e, int hpBoost) {
    e.needsService = false;
    e.mileage = 0;
    e.horsepower += hpBoost;
}

// Exercise 8: Email validation
bool isValidFormat(const string& email) const {
    return email.find('@') != string::npos && email.find('.') != string::npos;
}
Email(const string& addr) {
    if (!isValidFormat(addr)) throw invalid_argument("Invalid email format");
    address = addr;
}
*/
Exercises - C++ Tutorial | DeepML