Skip to main content

4. Practical Applications

4.1 Complete Example: Drawing Application

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

// Abstract base class
class Shape {
protected:
    string color;
    double x, y;  // Position
    
public:
    Shape(string c, double px, double py) : color(c), x(px), y(py) {}
    
    // Pure virtual functions
    virtual double getArea() = 0;
    virtual double getPerimeter() = 0;
    virtual void draw() = 0;
    virtual string getType() = 0;
    
    // Concrete functions
    void move(double dx, double dy) {
        x += dx;
        y += dy;
        cout << getType() << " moved to (" << x << ", " << y << ")" << endl;
    }
    
    void setColor(string c) {
        color = c;
        cout << getType() << " color changed to " << color << endl;
    }
    
    string getColor() { return color; }
    
    virtual void displayInfo() {
        cout << getType() << " at (" << x << ", " << y << ")" << endl;
        cout << "Color: " << color << endl;
        cout << "Area: " << getArea() << endl;
        cout << "Perimeter: " << getPerimeter() << endl;
    }
    
    virtual ~Shape() {}
};

class Circle : public Shape {
private:
    double radius;
    
public:
    Circle(string c, double px, double py, double r)
        : Shape(c, px, py), radius(r) {}
    
    double getArea() override {
        return 3.14159 * radius * radius;
    }
    
    double getPerimeter() override {
        return 2 * 3.14159 * radius;
    }
    
    void draw() override {
        cout << "Drawing " << color << " circle at (" << x << ", " << y 
             << ") with radius " << radius << endl;
    }
    
    string getType() override {
        return "Circle";
    }
    
    void displayInfo() override {
        Shape::displayInfo();
        cout << "Radius: " << radius << endl;
    }
};

class Rectangle : public Shape {
private:
    double width, height;
    
public:
    Rectangle(string c, double px, double py, double w, double h)
        : Shape(c, px, py), width(w), height(h) {}
    
    double getArea() override {
        return width * height;
    }
    
    double getPerimeter() override {
        return 2 * (width + height);
    }
    
    void draw() override {
        cout << "Drawing " << color << " rectangle at (" << x << ", " << y 
             << ") with width " << width << " and height " << height << endl;
    }
    
    string getType() override {
        return "Rectangle";
    }
    
    void displayInfo() override {
        Shape::displayInfo();
        cout << "Width: " << width << ", Height: " << height << endl;
    }
};

class Triangle : public Shape {
private:
    double base, height;
    
public:
    Triangle(string c, double px, double py, double b, double h)
        : Shape(c, px, py), base(b), height(h) {}
    
    double getArea() override {
        return 0.5 * base * height;
    }
    
    double getPerimeter() override {
        // Simplified: assumes isosceles triangle
        double side = sqrt((base/2)*(base/2) + height*height);
        return base + 2*side;
    }
    
    void draw() override {
        cout << "Drawing " << color << " triangle at (" << x << ", " << y 
             << ") with base " << base << " and height " << height << endl;
    }
    
    string getType() override {
        return "Triangle";
    }
    
    void displayInfo() override {
        Shape::displayInfo();
        cout << "Base: " << base << ", Height: " << height << endl;
    }
};

// Canvas class to manage shapes
class Canvas {
private:
    vector<Shape*> shapes;
    
public:
    void addShape(Shape* shape) {
        shapes.push_back(shape);
        cout << shape->getType() << " added to canvas" << endl;
    }
    
    void drawAll() {
        cout << "\n=== Drawing All Shapes ===" << endl;
        for (Shape* shape : shapes) {
            shape->draw();
        }
    }
    
    void displayAllInfo() {
        cout << "\n=== All Shapes Information ===" << endl;
        for (size_t i = 0; i < shapes.size(); i++) {
            cout << "\nShape " << (i+1) << ":" << endl;
            shapes[i]->displayInfo();
            cout << "-----------------------------------" << endl;
        }
    }
    
    double getTotalArea() {
        double total = 0;
        for (Shape* shape : shapes) {
            total += shape->getArea();
        }
        return total;
    }
    
    void removeShape(int index) {
        if (index >= 0 && index < shapes.size()) {
            delete shapes[index];
            shapes.erase(shapes.begin() + index);
            cout << "Shape removed" << endl;
        }
    }
    
    ~Canvas() {
        for (Shape* shape : shapes) {
            delete shape;
        }
    }
};

int main() {
    Canvas canvas;
    
    cout << "=== Creating Shapes ===" << endl;
    canvas.addShape(new Circle("Red", 10, 10, 5));
    canvas.addShape(new Rectangle("Blue", 20, 20, 10, 8));
    canvas.addShape(new Triangle("Green", 30, 30, 6, 4));
    canvas.addShape(new Circle("Yellow", 40, 40, 7));
    
    canvas.drawAll();
    canvas.displayAllInfo();
    
    cout << "\nTotal area of all shapes: " << canvas.getTotalArea() << endl;
    
    return 0;
}

4.2 Example: Game Character System

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

// Abstract base class
class GameCharacter {
protected:
    string name;
    int health;
    int maxHealth;
    int attackPower;
    
public:
    GameCharacter(string n, int hp, int atk)
        : name(n), health(hp), maxHealth(hp), attackPower(atk) {}
    
    // Pure virtual functions
    virtual void attack(GameCharacter* target) = 0;
    virtual void specialAbility() = 0;
    virtual string getClass() = 0;
    
    // Concrete functions
    void takeDamage(int damage) {
        health -= damage;
        if (health < 0) health = 0;
        cout << name << " takes " << damage << " damage! Health: " << health << endl;
        
        if (health == 0) {
            cout << name << " has been defeated!" << endl;
        }
    }
    
    void heal(int amount) {
        health += amount;
        if (health > maxHealth) health = maxHealth;
        cout << name << " heals " << amount << " HP! Health: " << health << endl;
    }
    
    bool isAlive() {
        return health > 0;
    }
    
    void displayStatus() {
        cout << name << " (" << getClass() << ")" << endl;
        cout << "Health: " << health << "/" << maxHealth << endl;
        cout << "Attack Power: " << attackPower << endl;
    }
    
    string getName() { return name; }
    int getAttackPower() { return attackPower; }
    
    virtual ~GameCharacter() {}
};

class Warrior : public GameCharacter {
private:
    int armor;
    
public:
    Warrior(string n) : GameCharacter(n, 150, 25), armor(10) {}
    
    void attack(GameCharacter* target) override {
        cout << name << " swings sword at " << target->getName() << "!" << endl;
        target->takeDamage(attackPower);
    }
    
    void specialAbility() override {
        cout << name << " uses Shield Bash!" << endl;
        cout << "Defense increased temporarily!" << endl;
        armor += 5;
    }
    
    string getClass() override {
        return "Warrior";
    }
    
    void takeDamage(int damage) {
        int reducedDamage = damage - armor;
        if (reducedDamage < 0) reducedDamage = 0;
        cout << name << "'s armor blocks " << armor << " damage!" << endl;
        GameCharacter::takeDamage(reducedDamage);
    }
};

class Mage : public GameCharacter {
private:
    int mana;
    
public:
    Mage(string n) : GameCharacter(n, 80, 35), mana(100) {}
    
    void attack(GameCharacter* target) override {
        if (mana >= 10) {
            cout << name << " casts Fireball at " << target->getName() << "!" << endl;
            target->takeDamage(attackPower);
            mana -= 10;
        } else {
            cout << name << " is out of mana!" << endl;
        }
    }
    
    void specialAbility() override {
        if (mana >= 30) {
            cout << name << " casts Meteor Storm!" << endl;
            cout << "Massive area damage!" << endl;
            mana -= 30;
        } else {
            cout << name << " doesn't have enough mana!" << endl;
        }
    }
    
    string getClass() override {
        return "Mage";
    }
    
    void displayStatus() {
        GameCharacter::displayStatus();
        cout << "Mana: " << mana << endl;
    }
};

class Archer : public GameCharacter {
private:
    int arrows;
    
public:
    Archer(string n) : GameCharacter(n, 100, 30), arrows(50) {}
    
    void attack(GameCharacter* target) override {
        if (arrows > 0) {
            cout << name << " shoots arrow at " << target->getName() << "!" << endl;
            target->takeDamage(attackPower);
            arrows--;
        } else {
            cout << name << " is out of arrows!" << endl;
        }
    }
    
    void specialAbility() override {
        if (arrows >= 5) {
            cout << name << " uses Multi-Shot!" << endl;
            cout << "Fires multiple arrows!" << endl;
            arrows -= 5;
        } else {
            cout << name << " doesn't have enough arrows!" << endl;
        }
    }
    
    string getClass() override {
        return "Archer";
    }
    
    void displayStatus() {
        GameCharacter::displayStatus();
        cout << "Arrows: " << arrows << endl;
    }
};

int main() {
    cout << "=== Character Creation ===" << endl;
    vector<GameCharacter*> party;
    
    party.push_back(new Warrior("Thorin"));
    party.push_back(new Mage("Gandalf"));
    party.push_back(new Archer("Legolas"));
    
    cout << "\n=== Party Status ===" << endl;
    for (GameCharacter* character : party) {
        character->displayStatus();
        cout << endl;
    }
    
    cout << "=== Battle Simulation ===" << endl;
    GameCharacter* enemy = new Warrior("Orc");
    cout << "\nEnemy appears: ";
    enemy->displayStatus();
    
    cout << "\n--- Round 1 ---" << endl;
    party[0]->attack(enemy);
    party[1]->attack(enemy);
    party[2]->attack(enemy);
    
    cout << "\n--- Round 2 ---" << endl;
    party[0]->specialAbility();
    party[1]->specialAbility();
    party[2]->specialAbility();
    
    // Cleanup
    for (GameCharacter* character : party) {
        delete character;
    }
    delete enemy;
    
    return 0;
}

4.3 Best Practices

1. Always Use Virtual Destructors in Base Classes

class Base {
public:
    virtual ~Base() {}  // CRITICAL!
};

2. Use override Keyword

class Derived : public Base {
public:
    void func() override {  // Compiler checks
        // implementation
    }
};

3. Use Abstract Classes for Interfaces

class IDrawable {
public:
    virtual void draw() = 0;
    virtual ~IDrawable() {}
};

4. Prefer References or Pointers for Polymorphism

// GOOD: Using pointer or reference
void process(Shape* shape) {
    shape->draw();
}

void process(Shape& shape) {
    shape.draw();
}

// BAD: Pass by value causes slicing
void process(Shape shape) {  // DON'T DO THIS
    shape.draw();
}

5. Check for Null Pointers

Shape* shape = findShape(id);
if (shape != nullptr) {
    shape->draw();
}