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++:
- Compile-time Polymorphism: Achieved through function overloading and operator overloading.
- 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
Printclass has three overloadeddisplaymethods, 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
Complexclass overloads the+operator to add two complex numbers. - The
operator +function defines the behavior of the+operator for theComplexclass.
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
Animalclass has a virtual functionsound. - The
DogandCatclasses override thesoundfunction. - The
makeSoundfunction calls the appropriatesoundfunction 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
Shapeclass has a pure virtual functiondraw. - The
CircleandRectangleclasses override thedrawfunction. - The
Shapepointer calls the appropriatedrawfunction 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
Vehicleclass has a virtual functionstart. - The
CarandBikeclasses override thestartfunction. - The
Vehiclepointer calls the appropriatestartfunction 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
Accountclass has a virtual functioncalculateInterest. - The
SavingsAccountandCheckingAccountclasses override thecalculateInterestfunction. - The
Accountpointer calls the appropriatecalculateInterestfunction 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.