cpp
exercises
exercises.cpp⚙️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;
}
*/