cpp
polymorphism
01_polymorphism.cppāļøcpp
/**
* ============================================================
* C++ POLYMORPHISM
* ============================================================
*
* This file covers:
* - Compile-time polymorphism (overloading)
* - Runtime polymorphism (virtual functions)
* - Virtual functions and vtable
* - Pure virtual functions
* - Abstract classes
*
* Compile: g++ -std=c++17 -Wall 01_polymorphism.cpp -o polymorphism
* Run: ./polymorphism
*
* ============================================================
*/
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <cmath>
using namespace std;
// ============================================================
// PART 1: COMPILE-TIME POLYMORPHISM (FUNCTION OVERLOADING)
// ============================================================
class Calculator {
public:
// Same name, different parameters = overloading
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
string add(const string& a, const string& b) {
return a + b;
}
};
// ============================================================
// PART 2: RUNTIME POLYMORPHISM (VIRTUAL FUNCTIONS)
// ============================================================
class Shape {
protected:
string name;
public:
Shape(const string& n) : name(n) {}
virtual ~Shape() {
cout << " Shape destructor: " << name << endl;
}
// Virtual function - can be overridden
virtual double area() const {
return 0.0;
}
virtual double perimeter() const {
return 0.0;
}
// Non-virtual function
string getName() const {
return name;
}
virtual void draw() const {
cout << "Drawing " << name << endl;
}
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : Shape("Circle"), radius(r) {}
~Circle() {
cout << " Circle destructor" << endl;
}
// Override virtual functions
double area() const override {
return M_PI * radius * radius;
}
double perimeter() const override {
return 2 * M_PI * radius;
}
void draw() const override {
cout << "Drawing Circle with radius " << radius << endl;
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : Shape("Rectangle"), width(w), height(h) {}
~Rectangle() {
cout << " Rectangle destructor" << endl;
}
double area() const override {
return width * height;
}
double perimeter() const override {
return 2 * (width + height);
}
void draw() const override {
cout << "Drawing Rectangle " << width << "x" << height << endl;
}
};
class Triangle : public Shape {
private:
double a, b, c; // Three sides
public:
Triangle(double s1, double s2, double s3)
: Shape("Triangle"), a(s1), b(s2), c(s3) {}
~Triangle() {
cout << " Triangle destructor" << endl;
}
double area() const override {
// Heron's formula
double s = (a + b + c) / 2;
return sqrt(s * (s-a) * (s-b) * (s-c));
}
double perimeter() const override {
return a + b + c;
}
void draw() const override {
cout << "Drawing Triangle with sides " << a << ", " << b << ", " << c << endl;
}
};
// ============================================================
// PART 3: ABSTRACT CLASS (PURE VIRTUAL)
// ============================================================
// Abstract class - cannot be instantiated
class Animal {
protected:
string name;
public:
Animal(const string& n) : name(n) {}
virtual ~Animal() = default;
// Pure virtual function - MUST be overridden
virtual void speak() const = 0;
virtual void move() const = 0;
// Regular virtual function
virtual void eat() const {
cout << name << " is eating." << endl;
}
string getName() const { return name; }
};
class Dog : public Animal {
public:
Dog(const string& n) : Animal(n) {}
void speak() const override {
cout << name << " says: Woof!" << endl;
}
void move() const override {
cout << name << " runs on four legs." << endl;
}
};
class Cat : public Animal {
public:
Cat(const string& n) : Animal(n) {}
void speak() const override {
cout << name << " says: Meow!" << endl;
}
void move() const override {
cout << name << " walks gracefully." << endl;
}
};
class Bird : public Animal {
public:
Bird(const string& n) : Animal(n) {}
void speak() const override {
cout << name << " says: Tweet!" << endl;
}
void move() const override {
cout << name << " flies in the sky." << endl;
}
};
// ============================================================
// PART 4: INTERFACE PATTERN
// ============================================================
// Interface - all pure virtual functions
class Drawable {
public:
virtual ~Drawable() = default;
virtual void draw() const = 0;
virtual void resize(double factor) = 0;
};
class Serializable {
public:
virtual ~Serializable() = default;
virtual string serialize() const = 0;
virtual void deserialize(const string& data) = 0;
};
// Class implementing multiple interfaces
class GameSprite : public Drawable, public Serializable {
private:
int x, y;
int width, height;
string texture;
public:
GameSprite(int x, int y, int w, int h, const string& tex)
: x(x), y(y), width(w), height(h), texture(tex) {}
// Drawable interface
void draw() const override {
cout << "Drawing sprite '" << texture << "' at (" << x << "," << y << ")" << endl;
}
void resize(double factor) override {
width = static_cast<int>(width * factor);
height = static_cast<int>(height * factor);
cout << "Resized to " << width << "x" << height << endl;
}
// Serializable interface
string serialize() const override {
return "Sprite:" + texture + "," + to_string(x) + "," + to_string(y);
}
void deserialize(const string& data) override {
cout << "Deserializing: " << data << endl;
// Parse data and set values...
}
};
// ============================================================
// PART 5: POLYMORPHISM WITH POINTERS/REFERENCES
// ============================================================
void makeAnimalSpeak(const Animal& animal) {
// Works with any Animal subclass!
animal.speak();
}
void processShapes(const vector<unique_ptr<Shape>>& shapes) {
double totalArea = 0;
for (const auto& shape : shapes) {
shape->draw();
cout << " Area: " << shape->area() << endl;
totalArea += shape->area();
}
cout << "Total area: " << totalArea << endl;
}
// ============================================================
// MAIN FUNCTION
// ============================================================
int main() {
cout << "============================================" << endl;
cout << " C++ POLYMORPHISM" << endl;
cout << "============================================" << endl << endl;
// ========================================================
// DEMO 1: Compile-Time Polymorphism (Overloading)
// ========================================================
cout << "--- DEMO 1: FUNCTION OVERLOADING ---" << endl << endl;
Calculator calc;
cout << "calc.add(5, 3) = " << calc.add(5, 3) << endl;
cout << "calc.add(2.5, 3.5) = " << calc.add(2.5, 3.5) << endl;
cout << "calc.add(1, 2, 3) = " << calc.add(1, 2, 3) << endl;
cout << "calc.add(\"Hello \", \"World\") = " << calc.add("Hello ", "World") << endl;
cout << "\nš” Resolved at compile time based on parameter types" << endl;
cout << endl;
// ========================================================
// DEMO 2: Runtime Polymorphism (Virtual Functions)
// ========================================================
cout << "--- DEMO 2: VIRTUAL FUNCTIONS ---" << endl << endl;
Circle circle(5.0);
Rectangle rectangle(4.0, 6.0);
Triangle triangle(3.0, 4.0, 5.0);
// Using base class pointer
Shape* shapes[] = {&circle, &rectangle, &triangle};
cout << "Processing shapes through Shape* pointer:" << endl;
for (Shape* s : shapes) {
s->draw();
cout << " Area: " << s->area() << endl;
cout << " Perimeter: " << s->perimeter() << endl;
cout << endl;
}
cout << "š” Correct method called based on actual object type!" << endl;
cout << endl;
// ========================================================
// DEMO 3: Without Virtual (Static Binding)
// ========================================================
cout << "--- DEMO 3: VIRTUAL VS NON-VIRTUAL ---" << endl << endl;
class Base {
public:
void nonVirtual() { cout << "Base::nonVirtual" << endl; }
virtual void isVirtual() { cout << "Base::isVirtual" << endl; }
};
class Derived : public Base {
public:
void nonVirtual() { cout << "Derived::nonVirtual" << endl; }
void isVirtual() override { cout << "Derived::isVirtual" << endl; }
};
Derived d;
Base* ptr = &d;
cout << "Calling through Base* ptr to Derived object:" << endl;
ptr->nonVirtual(); // Calls Base version (static binding)
ptr->isVirtual(); // Calls Derived version (dynamic binding)
cout << endl;
// ========================================================
// DEMO 4: Abstract Class
// ========================================================
cout << "--- DEMO 4: ABSTRACT CLASS ---" << endl << endl;
// Animal animal("Test"); // ERROR: can't instantiate abstract class
Dog dog("Buddy");
Cat cat("Whiskers");
Bird bird("Tweety");
// Array of Animal pointers
Animal* animals[] = {&dog, &cat, &bird};
cout << "Making all animals speak and move:" << endl;
for (Animal* a : animals) {
a->speak();
a->move();
a->eat();
cout << endl;
}
// Using reference
cout << "Using reference parameter:" << endl;
makeAnimalSpeak(dog);
makeAnimalSpeak(cat);
makeAnimalSpeak(bird);
cout << endl;
// ========================================================
// DEMO 5: Smart Pointers and Polymorphism
// ========================================================
cout << "--- DEMO 5: SMART POINTERS ---" << endl << endl;
vector<unique_ptr<Shape>> shapeCollection;
shapeCollection.push_back(make_unique<Circle>(3.0));
shapeCollection.push_back(make_unique<Rectangle>(2.0, 4.0));
shapeCollection.push_back(make_unique<Triangle>(3.0, 4.0, 5.0));
shapeCollection.push_back(make_unique<Circle>(1.5));
processShapes(shapeCollection);
cout << "\nShapes being destroyed:" << endl;
shapeCollection.clear(); // Proper cleanup with virtual destructor
cout << endl;
// ========================================================
// DEMO 6: Interface Implementation
// ========================================================
cout << "--- DEMO 6: INTERFACES ---" << endl << endl;
GameSprite sprite(100, 200, 64, 64, "player.png");
// Use as Drawable
Drawable* drawable = &sprite;
drawable->draw();
drawable->resize(2.0);
// Use as Serializable
Serializable* serializable = &sprite;
cout << "Serialized: " << serializable->serialize() << endl;
cout << endl;
// ========================================================
// POLYMORPHISM SUMMARY
// ========================================================
cout << "--- POLYMORPHISM CONCEPTS ---" << endl << endl;
cout << "Compile-Time (Static) Polymorphism:" << endl;
cout << "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā" << endl;
cout << "⢠Function overloading" << endl;
cout << "⢠Operator overloading" << endl;
cout << "⢠Templates" << endl;
cout << "⢠Resolved at compile time" << endl;
cout << "\nRuntime (Dynamic) Polymorphism:" << endl;
cout << "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā" << endl;
cout << "⢠Virtual functions" << endl;
cout << "⢠Pure virtual (= 0)" << endl;
cout << "⢠Resolved at runtime via vtable" << endl;
cout << "\nKey Points:" << endl;
cout << "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā" << endl;
cout << "⢠Use 'virtual' for runtime polymorphism" << endl;
cout << "⢠Use 'override' to catch errors" << endl;
cout << "⢠Always virtual destructor in base class" << endl;
cout << "⢠Pure virtual (= 0) makes class abstract" << endl;
cout << "⢠Access through pointer/reference" << endl;
cout << endl;
cout << "============================================" << endl;
cout << "POLYMORPHISM SUMMARY:" << endl;
cout << "============================================" << endl;
cout << "⢠virtual: enable runtime polymorphism" << endl;
cout << "⢠override: mark overridden functions" << endl;
cout << "⢠= 0: pure virtual (abstract)" << endl;
cout << "⢠Use base class pointers/references" << endl;
cout << "⢠Virtual destructor is essential" << endl;
cout << "============================================" << endl;
return 0;
}
// ============================================================
// EXERCISES:
// ============================================================
/*
* 1. Create a Payment hierarchy:
* - Abstract PaymentMethod with processPayment()
* - Derived: CreditCard, DebitCard, PayPal, Bitcoin
* - Each with different processing logic
*
* 2. Create a Media Player:
* - Abstract MediaFile with play(), pause(), stop()
* - Derived: AudioFile, VideoFile, StreamingMedia
* - Process a playlist of different media types
*
* 3. Create a GUI Widget system:
* - Abstract Widget with draw(), handleClick()
* - Derived: Button, TextBox, CheckBox, Slider
* - Container that holds multiple widgets
*
* 4. Create a Document system:
* - Interface: Printable, Saveable, Searchable
* - Classes implementing multiple interfaces
* - Use polymorphism to process documents
*/