In this blog, we will explore the concept of polymorphism in C++ along with its types, needs, and key differences between compile time and run time polymorphism.
Let us discuss the following topics:
Check out our YouTube video on C programming language for absolute beginners!
What is Polymorphism in C++?
Polymorphism is a term that is derived from a Greek word that means many forms. In programming languages, if a function or object acts in different ways in different situations, that behavior of the object is referred to as polymorphism in C++.
For example, we have a function named Sound(). When a cat calls this function, it will produce a meow sound, but when the same function is called by a lion, it will produce a roar sound. Through this example, we can understand that the same function can perform various tasks in different scenarios. As the function has many forms, we have achieved polymorphism.
Necessity of Polymorphism in C++
Polymorphism in C++ is like a superhero with different powers in the same costume. It allows you to use the same function or method name, but it can behave differently based on the context or the type of data it’s working with. This makes your code more flexible and easier to understand because you can write functions that work with a variety of data types without knowing their specific details.
Imagine you have a universal remote control. It has one button labeled “turn on.” Depending on the device you point it at, that button might turn on a TV, a fan, or even a light. In the programming world, this is similar to polymorphism. Without needing a separate function for each type, you can have a function. Let’s say “displayInfo,” and it can display information about different objects, like a car or a cat.
In C++, polymorphism helps you write cleaner, more reusable code. It enables you to create generic functions and classes that can adapt and work with various types, promoting code efficiency and reducing redundancy.
Types of Polymorphism in C++
There are two types of polymorphism in C++:
- Compile Time Polymorphism
- Runtime Polymorphism
Let us discuss each of them in detail:
1. Compile Time Polymorphism
Compile time polymorphism is a type of polymorphism where the function is called at the compile time. This key principle of OOPs is also termed early or static binding. This type is again categorized into two parts: function overloading and operator overloading. Let us discuss each subpart with its respective code and explanation for better understanding.
Function Overloading
Function overloading is a subcategory of compile time polymorphism where one function can perform various tasks with the same name and different types of arguments. In this type, the function will be called at the time of program compilation. By using function overloading, we can improve the performance and readability of the program. Here is the respective code for function overloading with its output:
#include <iostream>
// Function to add two integers
int add(int a, int b) {
return a + b;
}
// Function to add three integers
int add(int a, int b, int c) {
return a + b + c;
}
// Function to concatenate two strings
std::string add(const std::string& str1, const std::string& str2) {
return str1 + str2;
}
int main() {
// Using the overloaded functions
std::cout << "Sum of 2 and 3: " << add(2, 3) << std::endl;
std::cout << "Sum of 2, 3, and 4: " << add(2, 3, 4) << std::endl;
std::string result = add("Hello, ", "Intellipaat!");
std::cout << "Concatenated string: " << result << std::endl;
return 0;
}
Output:
Explanation:
In this example, we have two add functions for integers with different parameter lists and another add function for strings. The compiler selects the appropriate function based on the arguments provided when calling the function.
Operator Overloading
Operator overloading is another subcategory of compile time polymorphism where we define additional tasks to an operator without editing its actual meaning by implementing the operator function. The main benefit of using operator overloading is to do various operations on the same operand. Here is the code for operator overloading with its output:
#include <iostream>
class Fraction {
private:
int numerator;
int denominator;
public:
// Constructor
Fraction(int num = 0, int denom = 1) : numerator(num), denominator(denom) {
// Ensure the denominator is not zero
if (denominator == 0) {
std::cerr << "Error: Denominator cannot be zero." << std::endl;
exit(1);
}
}
// Overloading the equality (==) operator
bool operator==(const Fraction& other) const {
return (numerator * other.denominator == other.numerator * denominator);
}
// Overloading the inequality (!=) operator
bool operator!=(const Fraction& other) const {
return !(*this == other);
}
// Displaying the fraction
void display() const {
std::cout << numerator << "/" << denominator;
}
};
int main() {
// Creating fraction objects
Fraction frac1(2, 3);
Fraction frac2(4, 6);
Fraction frac3(5, 7);
// Using the overloaded equality (==) and inequality (!=) operators
std::cout << "Fraction 1: ";
frac1.display();
std::cout << std::endl;
std::cout << "Fraction 2: ";
frac2.display();
std::cout << std::endl;
std::cout << "Fraction 3: ";
frac3.display();
std::cout << std::endl;
if (frac1 == frac2) {
std::cout << "Fraction 1 is equal to Fraction 2." << std::endl;
} else {
std::cout << "Fraction 1 is not equal to Fraction 2." << std::endl;
}
if (frac1 != frac3) {
std::cout << "Fraction 1 is not equal to Fraction 3." << std::endl;
} else {
std::cout << "Fraction 1 is equal to Fraction 3." << std::endl;
}
return 0;
}
Output:
Explanation:
In this example, the Fraction class represents a fraction with a numerator and denominator. The == and != operators are overloaded to compare two fractions for equality and inequality. The display function is used to print the fractions in a readable format. The program then demonstrates the use of these operators in comparing fractions.
Get 100% Hike!
Master Most in Demand Skills Now!
2. Runtime Polymorphism
In runtime polymorphism, functions are called at the time of execution; hence, they are referred to as late binding or dynamic binding. It is also segregated into two parts: function overriding and virtual functions. Let us understand each sub-category in detail, with its code and explanation.
Function Overriding
Function overriding is a subcategory of runtime polymorphism that occurs when derived classes and base classes both consist of a function with the same name. Also, both functions should have the same number of arguments as well as the same return type. Function overriding can take place during the runtime to achieve polymorphism. Now, let us see how we can implement function overriding through the respective code:
#include <iostream>
// Base class
class Vehicle {
public:
// Virtual function for displaying information
virtual void displayInfo() const {
std::cout << "Generic Vehicle" << std::endl;
}
};
// Derived class - Car
class Car : public Vehicle {
public:
// Overriding the displayInfo function for cars
void displayInfo() const override {
std::cout << "Car: Four wheels, doors, and a roof" << std::endl;
}
};
// Derived class - Motorcycle
class Motorcycle : public Vehicle {
public:
// Overriding the displayInfo function for motorcycles
void displayInfo() const override {
std::cout << "Motorcycle: Two wheels and a handlebar" << std::endl;
}
};
int main() {
// Creating objects of the derived classes
Vehicle genericVehicle;
Car myCar;
Motorcycle myMotorcycle;
// Using overridden displayInfo functions
std::cout << "Generic Vehicle Info: ";
genericVehicle.displayInfo();
std::cout << "Car Info: ";
myCar.displayInfo();
std::cout << "Motorcycle Info: ";
myMotorcycle.displayInfo();
// Using base class pointers to call overridden functions
Vehicle* vehicle1 = &myCar;
Vehicle* vehicle2 = &myMotorcycle;
std::cout << "Car Info using base class pointer: ";
vehicle1->displayInfo();
std::cout << "Motorcycle Info using base class pointer: ";
vehicle2->displayInfo();
return 0;
}
Output:
Explanation:
In this example, the Vehicle base class has a virtual function displayInfo, and the Car and Motorcycle derived classes override this function to provide specific information about each type of vehicle. The main function demonstrates how to use overridden functions both directly and through base class pointers.
Virtual Function
A virtual function is a member function that is present in the base class. It is a subcategory of run time polymorphism that can be redefined in a derived class. Additionally, if we want to declare a virtual function, it must be declared in the base class by using the virtual keyword. This function assists the compiler in performing dynamic binding or late binding on the function. Here is the code for the virtual function:
#include <iostream>
// Base class
class Animal {
public:
// Virtual function for making a sound
virtual void makeSound() const {
std::cout << "Generic Animal Sound" << std::endl;
}
};
// Derived class - Dog
class Dog : public Animal {
public:
// Override base class function for dogs
void makeSound() const override {
std::cout << "Woof! Woof!" << std::endl;
}
};
// Derived class - Cat
class Cat : public Animal {
public:
// Override base class function for cats
void makeSound() const override {
std::cout << "Meow! Meow!" << std::endl;
}
};
int main() {
// Creating objects of the derived classes
Dog myDog;
Cat myCat;
// Using virtual functions for polymorphism
Animal* animal1 = &myDog;
Animal* animal2 = &myCat;
// Making sounds using virtual functions
animal1->makeSound();
animal2->makeSound();
return 0;
}
Output:
Explanation:
In this example, the Animal class has a virtual function makeSound, and the Dog and Cat classes override this function to provide specific sounds for dogs and cats. The main function demonstrates polymorphism by using base class pointers to access the overridden makeSound function for both Dog and Cat objects.
Difference Between Compile Time Vs. Run Time Polymorphism in C++
Here’s a comparison between compile time (static) and run time (dynamic) polymorphism in C++ presented in tabular form:
Feature | Compile Time Polymorphism (Static Binding) | Run Time Polymorphism (Dynamic Binding) |
Type of Polymorphism | Static polymorphism | Dynamic polymorphism |
Mechanism | Function overloading and operator overloading | Virtual functions and inheritance |
Flexibility and Extensibility | Less flexible and extensible | More flexible and extensible, as it supports late binding |
Implementation Decisions | Made by the compiler | Made during program execution by the virtual table |
Timing of Binding | Occurs at compile time | Occurs at run time |
Advantages of Polymorphism in C++
Below are the advantages of polymorphism in C++ that are summarized in points:
- Adaptability: Polymorphism allows a single function or operator to work with different data types, making your code adaptable to a variety of situations.
- Simplified Interface: Polymorphism provides a unified interface for different classes, making the code more intuitive and easier to understand.
- Improved Readability: By using a common interface for various objects, the code becomes more readable and maintains a clear structure.
- Polymorphic Classes: Inheritance and polymorphism together enable the creation of polymorphic classes, which can be used to model and manage objects with shared behaviors and properties.
Conclusion
As software development continues to advance, polymorphism remains a fundamental concept that contributes to the creation of robust and efficient applications in various domains. Its versatility and ability to address the evolving needs of the programming landscape make it a valuable asset for developers striving to build efficient, maintainable, and adaptable software systems.
FAQ’s
What are different types of polymorphism in C++?
In C++, there are two main types of polymorphism: compile-time (or static) polymorphism and runtime (or dynamic) polymorphism.
What is compile time polymorphism in C++?
Compile-time polymorphism in C++ is also known as static polymorphism or early binding. It occurs when the method or function that needs to be executed is determined at compile time.
What is a real life example of polymorphism?
A real-life example of polymorphism can be found in the concept of a “Shape” in a graphics application. Consider a graphics software where various shapes such as circles, rectangles, and triangles need to be drawn on a canvas. Each shape can be considered an object, and they all share a common interface, for example, a draw
method.
What are the advantages of polymorphism?
Here are the advantages of polymorphism in object-oriented programming:
- Supports Design Patterns
- Enhances Code Readability and Maintainability
- Enhances Readability