C++ Polymorphism

Introduction

Polymorphism is one of the core concepts of Object-Oriented Programming (OOP) in C++. It allows objects of different classes to be treated as objects of a common base class. Polymorphism enables the same function to be used for different types, enhancing code reusability and flexibility. There are two types of polymorphism in C++:

  1. Compile-time Polymorphism: Achieved through function overloading and operator overloading.
  2. Run-time Polymorphism: Achieved through inheritance and virtual functions.

Compile-time Polymorphism

Function Overloading

Function overloading allows multiple functions to have the same name but different parameters. The compiler determines which function to call based on the number and types of arguments.

Example: Function Overloading

#include <iostream>
using namespace std;

class Print {
public:
    void display(int i) {
        cout << "Integer: " << i << endl;
    }

    void display(double d) {
        cout << "Double: " << d << endl;
    }

    void display(string s) {
        cout << "String: " << s << endl;
    }
};

int main() {
    Print print;

    print.display(5);
    print.display(3.14);
    print.display("Hello, World!");

    return 0;
}

Output

Integer: 5
Double: 3.14
String: Hello, World!

Explanation

  • The Print class has three overloaded display methods, each with a different parameter type.
  • The appropriate method is called based on the argument passed.

Operator Overloading

Operator overloading allows operators to be redefined and used with user-defined data types. It provides a way to implement custom behavior for operators in classes.

Example: Operator Overloading

#include <iostream>
using namespace std;

class Complex {
private:
    double real;
    double imag;

public:
    // Constructor
    Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}

    // Overload + operator
    Complex operator + (const Complex& c) {
        return Complex(real + c.real, imag + c.imag);
    }

    void display() {
        cout << "Complex number: " << real << " + " << imag << "i" << endl;
    }
};

int main() {
    Complex c1(3.3, 4.4);
    Complex c2(1.1, 2.2);
    Complex c3;

    c3 = c1 + c2;

    c3.display();

    return 0;
}

Output

Complex number: 4.4 + 6.6i

Explanation

  • The Complex class overloads the + operator to add two complex numbers.
  • The operator + function defines the behavior of the + operator for the Complex class.

Run-time Polymorphism

Virtual Functions

Virtual functions enable a function to be overridden in a derived class while preserving its base class interface. The function to be executed is determined at run-time based on the type of the object pointed to or referenced.

Example: Virtual Functions

#include <iostream>
using namespace std;

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

class Dog : public Animal {
public:
    void sound() override {
        cout << "Bark" << endl;
    }
};

class Cat : public Animal {
public:
    void sound() override {
        cout << "Meow" << endl;
    }
};

void makeSound(Animal* animal) {
    animal->sound();
}

int main() {
    Animal* animal;
    Dog dog;
    Cat cat;

    animal = &dog;
    makeSound(animal);

    animal = &cat;
    makeSound(animal);

    return 0;
}

Output

Bark
Meow

Explanation

  • The Animal class has a virtual function sound.
  • The Dog and Cat classes override the sound function.
  • The makeSound function calls the appropriate sound function based on the actual object type.

Pure Virtual Functions and Abstract Classes

A pure virtual function is a virtual function that has no implementation in the base class and must be overridden in derived classes. Classes containing pure virtual functions are called abstract classes and cannot be instantiated.

Example: Pure Virtual Functions

#include <iostream>
using namespace std;

class Shape {
public:
    virtual void draw() = 0; // Pure virtual function
};

class Circle : public Shape {
public:
    void draw() override {
        cout << "Drawing Circle" << endl;
    }
};

class Rectangle : public Shape {
public:
    void draw() override {
        cout << "Drawing Rectangle" << endl;
    }
};

int main() {
    Shape* shape;

    Circle circle;
    Rectangle rectangle;

    shape = &circle;
    shape->draw();

    shape = &rectangle;
    shape->draw();

    return 0;
}

Output

Drawing Circle
Drawing Rectangle

Explanation

  • The Shape class has a pure virtual function draw.
  • The Circle and Rectangle classes override the draw function.
  • The Shape pointer calls the appropriate draw function based on the actual object type.

Example Programs

Example 1: Vehicle Inheritance

#include <iostream>
using namespace std;

class Vehicle {
public:
    virtual void start() {
        cout << "Starting vehicle" << endl;
    }
};

class Car : public Vehicle {
public:
    void start() override {
        cout << "Starting car" << endl;
    }
};

class Bike : public Vehicle {
public:
    void start() override {
        cout << "Starting bike" << endl;
    }
};

int main() {
    Vehicle* vehicle;

    Car car;
    Bike bike;

    vehicle = &car;
    vehicle->start();

    vehicle = &bike;
    vehicle->start();

    return 0;
}

Output

Starting car
Starting bike

Explanation

  • The Vehicle class has a virtual function start.
  • The Car and Bike classes override the start function.
  • The Vehicle pointer calls the appropriate start function based on the actual object type.

Example 2: Account Inheritance

#include <iostream>
using namespace std;

class Account {
public:
    virtual void calculateInterest() {
        cout << "Calculating interest for generic account" << endl;
    }
};

class SavingsAccount : public Account {
public:
    void calculateInterest() override {
        cout << "Calculating interest for savings account" << endl;
    }
};

class CheckingAccount : public Account {
public:
    void calculateInterest() override {
        cout << "Calculating interest for checking account" << endl;
    }
};

int main() {
    Account* account;

    SavingsAccount savings;
    CheckingAccount checking;

    account = &savings;
    account->calculateInterest();

    account = &checking;
    account->calculateInterest();

    return 0;
}

Output

Calculating interest for savings account
Calculating interest for checking account

Explanation

  • The Account class has a virtual function calculateInterest.
  • The SavingsAccount and CheckingAccount classes override the calculateInterest function.
  • The Account pointer calls the appropriate calculateInterest function based on the actual object type.

Conclusion

Polymorphism in C++ allows for the use of a single interface to represent different underlying forms (data types). This chapter covered compile-time polymorphism through function and operator overloading, and run-time polymorphism through inheritance and virtual functions. Example programs demonstrated how to implement and use polymorphism in C++. Understanding polymorphism is crucial for creating flexible and reusable code in object-oriented programming.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top