Skip to main content

3. Practical Applications and Best Practices

3.1 Complete Example: University Management System

#include <iostream>
#include <vector>
#include <string>
using namespace std;

// Base class
class Person {
protected:
    string name;
    int id;
    int age;
    
public:
    Person(string n, int i, int a) : name(n), id(i), age(a) {}
    
    virtual void displayInfo() {
        cout << "Name: " << name << endl;
        cout << "ID: " << id << endl;
        cout << "Age: " << age << endl;
    }
    
    virtual string getRole() = 0;  // Pure virtual
    
    string getName() { return name; }
    int getId() { return id; }
    
    virtual ~Person() {}
};

// Derived class: Student
class Student : public Person {
private:
    string major;
    double gpa;
    vector<string> courses;
    
public:
    Student(string n, int i, int a, string m, double g)
        : Person(n, i, a), major(m), gpa(g) {}
    
    void enrollCourse(string course) {
        courses.push_back(course);
        cout << name << " enrolled in " << course << endl;
    }
    
    void displayInfo() override {
        cout << "=== Student Information ===" << endl;
        Person::displayInfo();
        cout << "Major: " << major << endl;
        cout << "GPA: " << gpa << endl;
        cout << "Enrolled Courses: ";
        if (courses.empty()) {
            cout << "None" << endl;
        } else {
            cout << endl;
            for (const string& course : courses) {
                cout << "  - " << course << endl;
            }
        }
    }
    
    string getRole() override {
        return "Student";
    }
    
    void study() {
        cout << name << " is studying..." << endl;
    }
};

// Derived class: Faculty
class Faculty : public Person {
private:
    string department;
    double salary;
    vector<string> coursesTeaching;
    
public:
    Faculty(string n, int i, int a, string dept, double sal)
        : Person(n, i, a), department(dept), salary(sal) {}
    
    void assignCourse(string course) {
        coursesTeaching.push_back(course);
        cout << name << " assigned to teach " << course << endl;
    }
    
    void displayInfo() override {
        cout << "=== Faculty Information ===" << endl;
        Person::displayInfo();
        cout << "Department: " << department << endl;
        cout << "Salary: $" << salary << endl;
        cout << "Courses Teaching: ";
        if (coursesTeaching.empty()) {
            cout << "None" << endl;
        } else {
            cout << endl;
            for (const string& course : coursesTeaching) {
                cout << "  - " << course << endl;
            }
        }
    }
    
    string getRole() override {
        return "Faculty";
    }
    
    void teach() {
        cout << name << " is teaching..." << endl;
    }
};

// Derived class: Staff
class Staff : public Person {
private:
    string position;
    string department;
    double salary;
    
public:
    Staff(string n, int i, int a, string pos, string dept, double sal)
        : Person(n, i, a), position(pos), department(dept), salary(sal) {}
    
    void displayInfo() override {
        cout << "=== Staff Information ===" << endl;
        Person::displayInfo();
        cout << "Position: " << position << endl;
        cout << "Department: " << department << endl;
        cout << "Salary: $" << salary << endl;
    }
    
    string getRole() override {
        return "Staff";
    }
    
    void work() {
        cout << name << " (" << position << ") is working..." << endl;
    }
};

// University class to manage all persons
class University {
private:
    string universityName;
    vector<Person*> members;
    
public:
    University(string name) : universityName(name) {
        cout << "University '" << universityName << "' initialized." << endl;
    }
    
    void addMember(Person* person) {
        members.push_back(person);
        cout << person->getRole() << " " << person->getName() 
             << " added to " << universityName << endl;
    }
    
    void displayAllMembers() {
        cout << "\n========== " << universityName << " - All Members ==========" << endl;
        
        for (Person* member : members) {
            member->displayInfo();
            cout << "-------------------------------------------" << endl;
        }
    }
    
    void displayByRole(string role) {
        cout << "\n========== " << role << "s at " << universityName << " ==========" << endl;
        
        bool found = false;
        for (Person* member : members) {
            if (member->getRole() == role) {
                member->displayInfo();
                cout << "-------------------------------------------" << endl;
                found = true;
            }
        }
        
        if (!found) {
            cout << "No " << role << "s found." << endl;
        }
    }
    
    Person* findMember(int id) {
        for (Person* member : members) {
            if (member->getId() == id) {
                return member;
            }
        }
        return nullptr;
    }
    
    ~University() {
        for (Person* member : members) {
            delete member;
        }
    }
};

int main() {
    University university("Tech University");
    
    cout << "\n=== Adding Members ===" << endl;
    
    // Add students
    Student* s1 = new Student("Alice Johnson", 1001, 20, "Computer Science", 3.8);
    Student* s2 = new Student("Bob Smith", 1002, 21, "Electrical Engineering", 3.6);
    
    university.addMember(s1);
    university.addMember(s2);
    
    // Add faculty
    Faculty* f1 = new Faculty("Dr. Carol White", 2001, 45, "Computer Science", 85000);
    Faculty* f2 = new Faculty("Dr. David Brown", 2002, 50, "Electrical Engineering", 90000);
    
    university.addMember(f1);
    university.addMember(f2);
    
    // Add staff
    Staff* st1 = new Staff("Eve Davis", 3001, 35, "Administrator", "Administration", 50000);
    
    university.addMember(st1);
    
    // Assign courses
    cout << "\n=== Assigning Courses ===" << endl;
    f1->assignCourse("Data Structures");
    f1->assignCourse("Algorithms");
    f2->assignCourse("Circuit Analysis");
    
    // Enroll students
    cout << "\n=== Enrolling Students ===" << endl;
    s1->enrollCourse("Data Structures");
    s1->enrollCourse("Algorithms");
    s2->enrollCourse("Circuit Analysis");
    
    // Display all members
    university.displayAllMembers();
    
    // Display by role
    university.displayByRole("Student");
    university.displayByRole("Faculty");
    
    // Polymorphic behavior
    cout << "\n=== Daily Activities ===" << endl;
    s1->study();
    f1->teach();
    st1->work();
    
    return 0;
}

3.2 Best Practices

1. Use virtual Destructors in Base Classes

class Base {
public:
    virtual ~Base() {}  // IMPORTANT for polymorphism
};

2. Use override Keyword

class Derived : public Base {
public:
    void someMethod() override {  // Explicit override
        // Implementation
    }
};

3. Prefer Composition Over Inheritance When Appropriate

// Don't do: class Car : public Engine
// Do this:
class Car {
private:
    Engine engine;  // Has-a relationship
public:
    Car() : engine() {}
};

4. Keep Base Class Interface Stable

// Good: Minimal, stable base class
class Shape {
public:
    virtual double getArea() = 0;
    virtual void draw() = 0;
    virtual ~Shape() {}
};

5. Use protected for Members Needed by Derived Classes

class Base {
protected:
    int valueNeededByDerived;  // Accessible in derived classes
private:
    int internalImplementation;  // Not accessible in derived classes
};

3.3 Common Pitfalls and Solutions

Problem 1: Forgetting Virtual Destructor

// BAD: No virtual destructor
class Base {
public:
    ~Base() { cout << "Base destructor" << endl; }
};

class Derived : public Base {
private:
    int* data;
public:
    Derived() { data = new int[100]; }
    ~Derived() { 
        delete[] data; 
        cout << "Derived destructor" << endl; 
    }
};

int main() {
    Base* obj = new Derived();
    delete obj;  // MEMORY LEAK! Only Base destructor called
    return 0;
}

// GOOD: Virtual destructor
class Base {
public:
    virtual ~Base() { cout << "Base destructor" << endl; }
};

class Derived : public Base {
private:
    int* data;
public:
    Derived() { data = new int[100]; }
    ~Derived() override { 
        delete[] data; 
        cout << "Derived destructor" << endl; 
    }
};

int main() {
    Base* obj = new Derived();
    delete obj;  // Correct! Both destructors called
    return 0;
}

Problem 2: Object Slicing

#include <iostream>
#include <string>
using namespace std;

class Animal {
public:
    string type;
    Animal() : type("Animal") {}
    virtual void makeSound() { cout << "Generic sound" << endl; }
};

class Dog : public Animal {
public:
    string breed;
    Dog() : breed("Unknown") { type = "Dog"; }
    void makeSound() override { cout << "Woof!" << endl; }
};

// BAD: Pass by value causes slicing
void processAnimal(Animal animal) {
    animal.makeSound();  // Always calls Animal::makeSound()!
}

// GOOD: Pass by reference or pointer
void processAnimalCorrect(Animal& animal) {
    animal.makeSound();  // Calls correct method
}

void processAnimalPointer(Animal* animal) {
    animal->makeSound();  // Calls correct method
}

int main() {
    Dog myDog;
    
    cout << "=== Object Slicing (BAD) ===" << endl;
    processAnimal(myDog);  // Slicing occurs! Outputs: Generic sound
    
    cout << "\n=== Using Reference (GOOD) ===" << endl;
    processAnimalCorrect(myDog);  // Outputs: Woof!
    
    cout << "\n=== Using Pointer (GOOD) ===" << endl;
    processAnimalPointer(&myDog);  // Outputs: Woof!
    
    return 0;
}

Problem 3: Calling Virtual Functions in Constructor

#include <iostream>
using namespace std;

class Base {
public:
    Base() {
        init();  // Calls Base::init(), not Derived::init()!
    }
    
    virtual void init() {
        cout << "Base initialization" << endl;
    }
};

class Derived : public Base {
public:
    Derived() {
        // Base constructor already called
    }
    
    void init() override {
        cout << "Derived initialization" << endl;
    }
};

int main() {
    Derived d;  // Output: Base initialization (not what we want!)
    return 0;
}

// SOLUTION: Call init() explicitly after construction
class BaseSolution {
public:
    BaseSolution() {
        // Don't call virtual functions here
    }
    
    virtual void init() {
        cout << "Base initialization" << endl;
    }
};

class DerivedSolution : public BaseSolution {
public:
    DerivedSolution() {
        // Don't call init() in constructor
    }
    
    void init() override {
        cout << "Derived initialization" << endl;
    }
};

int main() {
    DerivedSolution d;
    d.init();  // Now calls Derived::init()
    return 0;
}

Problem 4: Ambiguity in Multiple Inheritance

#include <iostream>
using namespace std;

class ClassA {
public:
    void display() { cout << "ClassA" << endl; }
};

class ClassB {
public:
    void display() { cout << "ClassB" << endl; }
};

class ClassC : public ClassA, public ClassB {
    // Now ClassC has two display() methods!
};

int main() {
    ClassC obj;
    // obj.display();  // ERROR: Ambiguous! Which display()?
    
    // SOLUTION 1: Explicitly specify which class
    obj.ClassA::display();
    obj.ClassB::display();
    
    return 0;
}

// SOLUTION 2: Override in derived class
class ClassCSolution : public ClassA, public ClassB {
public:
    void display() {
        cout << "ClassC - calling both:" << endl;
        ClassA::display();
        ClassB::display();
    }
};

3.4 Real-World Example: Vehicle Rental System

#include <iostream>
#include <vector>
#include <string>
using namespace std;

// Base class
class Vehicle {
protected:
    string vehicleId;
    string brand;
    string model;
    double dailyRate;
    bool isRented;
    
public:
    Vehicle(string id, string b, string m, double rate)
        : vehicleId(id), brand(b), model(m), dailyRate(rate), isRented(false) {}
    
    virtual void displayInfo() {
        cout << "ID: " << vehicleId << endl;
        cout << "Brand: " << brand << endl;
        cout << "Model: " << model << endl;
        cout << "Daily Rate: $" << dailyRate << endl;
        cout << "Status: " << (isRented ? "Rented" : "Available") << endl;
    }
    
    virtual double calculateRentalCost(int days) {
        return dailyRate * days;
    }
    
    virtual string getVehicleType() = 0;
    
    void rent() {
        if (!isRented) {
            isRented = true;
            cout << "Vehicle " << vehicleId << " rented successfully." << endl;
        } else {
            cout << "Vehicle " << vehicleId << " is already rented." << endl;
        }
    }
    
    void returnVehicle() {
        if (isRented) {
            isRented = false;
            cout << "Vehicle " << vehicleId << " returned successfully." << endl;
        }
    }
    
    bool checkAvailability() { return !isRented; }
    string getId() { return vehicleId; }
    
    virtual ~Vehicle() {}
};

// Derived class: Car
class Car : public Vehicle {
private:
    int numDoors;
    string fuelType;
    
public:
    Car(string id, string b, string m, double rate, int doors, string fuel)
        : Vehicle(id, b, m, rate), numDoors(doors), fuelType(fuel) {}
    
    void displayInfo() override {
        cout << "\n=== CAR ===" << endl;
        Vehicle::displayInfo();
        cout << "Number of Doors: " << numDoors << endl;
        cout << "Fuel Type: " << fuelType << endl;
    }
    
    double calculateRentalCost(int days) override {
        double baseCost = Vehicle::calculateRentalCost(days);
        // Premium fuel adds 10% to cost
        if (fuelType == "Premium") {
            baseCost *= 1.1;
        }
        return baseCost;
    }
    
    string getVehicleType() override {
        return "Car";
    }
};

// Derived class: Motorcycle
class Motorcycle : public Vehicle {
private:
    int engineCC;
    bool hasABS;
    
public:
    Motorcycle(string id, string b, string m, double rate, int cc, bool abs)
        : Vehicle(id, b, m, rate), engineCC(cc), hasABS(abs) {}
    
    void displayInfo() override {
        cout << "\n=== MOTORCYCLE ===" << endl;
        Vehicle::displayInfo();
        cout << "Engine CC: " << engineCC << endl;
        cout << "ABS: " << (hasABS ? "Yes" : "No") << endl;
    }
    
    double calculateRentalCost(int days) override {
        double baseCost = Vehicle::calculateRentalCost(days);
        // Motorcycles get 20% discount for rentals over 7 days
        if (days > 7) {
            baseCost *= 0.8;
        }
        return baseCost;
    }
    
    string getVehicleType() override {
        return "Motorcycle";
    }
};

// Derived class: Truck
class Truck : public Vehicle {
private:
    double loadCapacity;  // in tons
    bool hasLiftGate;
    
public:
    Truck(string id, string b, string m, double rate, double capacity, bool liftGate)
        : Vehicle(id, b, m, rate), loadCapacity(capacity), hasLiftGate(liftGate) {}
    
    void displayInfo() override {
        cout << "\n=== TRUCK ===" << endl;
        Vehicle::displayInfo();
        cout << "Load Capacity: " << loadCapacity << " tons" << endl;
        cout << "Lift Gate: " << (hasLiftGate ? "Yes" : "No") << endl;
    }
    
    double calculateRentalCost(int days) override {
        double baseCost = Vehicle::calculateRentalCost(days);
        // Additional charge based on capacity
        baseCost += (loadCapacity * 5 * days);
        // Lift gate adds $10 per day
        if (hasLiftGate) {
            baseCost += (10 * days);
        }
        return baseCost;
    }
    
    string getVehicleType() override {
        return "Truck";
    }
};

// Rental system manager
class RentalSystem {
private:
    string companyName;
    vector<Vehicle*> fleet;
    
public:
    RentalSystem(string name) : companyName(name) {
        cout << "=== " << companyName << " Rental System Initialized ===" << endl;
    }
    
    void addVehicle(Vehicle* vehicle) {
        fleet.push_back(vehicle);
        cout << vehicle->getVehicleType() << " " << vehicle->getId() 
             << " added to fleet." << endl;
    }
    
    void displayFleet() {
        cout << "\n========== " << companyName << " - Fleet ==========" << endl;
        for (Vehicle* vehicle : fleet) {
            vehicle->displayInfo();
            cout << "----------------------------------------" << endl;
        }
    }
    
    void displayAvailableVehicles() {
        cout << "\n========== Available Vehicles ==========" << endl;
        bool found = false;
        for (Vehicle* vehicle : fleet) {
            if (vehicle->checkAvailability()) {
                vehicle->displayInfo();
                cout << "----------------------------------------" << endl;
                found = true;
            }
        }
        if (!found) {
            cout << "No vehicles available." << endl;
        }
    }
    
    Vehicle* findVehicle(string id) {
        for (Vehicle* vehicle : fleet) {
            if (vehicle->getId() == id) {
                return vehicle;
            }
        }
        return nullptr;
    }
    
    void rentVehicle(string id, int days) {
        Vehicle* vehicle = findVehicle(id);
        if (vehicle != nullptr) {
            if (vehicle->checkAvailability()) {
                vehicle->rent();
                double cost = vehicle->calculateRentalCost(days);
                cout << "Rental Duration: " << days << " days" << endl;
                cout << "Total Cost: $" << cost << endl;
            } else {
                cout << "Vehicle is not available." << endl;
            }
        } else {
            cout << "Vehicle not found." << endl;
        }
    }
    
    void returnVehicle(string id) {
        Vehicle* vehicle = findVehicle(id);
        if (vehicle != nullptr) {
            vehicle->returnVehicle();
        } else {
            cout << "Vehicle not found." << endl;
        }
    }
    
    ~RentalSystem() {
        for (Vehicle* vehicle : fleet) {
            delete vehicle;
        }
    }
};

int main() {
    RentalSystem rental("QuickRent");
    
    cout << "\n=== Adding Vehicles to Fleet ===" << endl;
    rental.addVehicle(new Car("C001", "Toyota", "Camry", 50, 4, "Regular"));
    rental.addVehicle(new Car("C002", "BMW", "M5", 120, 4, "Premium"));
    rental.addVehicle(new Motorcycle("M001", "Harley", "Sportster", 40, 883, true));
    rental.addVehicle(new Motorcycle("M002", "Honda", "CBR", 35, 600, false));
    rental.addVehicle(new Truck("T001", "Ford", "F-150", 80, 2.5, true));
    rental.addVehicle(new Truck("T002", "Chevrolet", "Silverado", 85, 3.0, false));
    
    // Display all vehicles
    rental.displayFleet();
    
    // Rent some vehicles
    cout << "\n=== Renting Vehicles ===" << endl;
    rental.rentVehicle("C001", 3);
    cout << endl;
    rental.rentVehicle("M001", 10);  // Gets discount (>7 days)
    cout << endl;
    rental.rentVehicle("T001", 5);
    
    // Display available vehicles
    rental.displayAvailableVehicles();
    
    // Try to rent already rented vehicle
    cout << "\n=== Trying to Rent Already Rented Vehicle ===" << endl;
    rental.rentVehicle("C001", 2);
    
    // Return vehicles
    cout << "\n=== Returning Vehicles ===" << endl;
    rental.returnVehicle("C001");
    rental.returnVehicle("M001");
    
    // Display available vehicles again
    rental.displayAvailableVehicles();
    
    return 0;
}