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
Print
class has three overloadeddisplay
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 theComplex
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 functionsound
. - The
Dog
andCat
classes override thesound
function. - The
makeSound
function calls the appropriatesound
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 functiondraw
. - The
Circle
andRectangle
classes override thedraw
function. - The
Shape
pointer calls the appropriatedraw
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 functionstart
. - The
Car
andBike
classes override thestart
function. - The
Vehicle
pointer calls the appropriatestart
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 functioncalculateInterest
. - The
SavingsAccount
andCheckingAccount
classes override thecalculateInterest
function. - The
Account
pointer calls the appropriatecalculateInterest
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.