• Articles
  • Tutorials
  • Interview Questions

Types of Polymorphism in C++

Types of Polymorphism in C++

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!

Video Thumbnail

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:

Function Overloading

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:

Operator Overloading

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:

 Function Overriding

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:

 Virtual Function

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:

FeatureCompile Time Polymorphism (Static Binding)Run Time Polymorphism (Dynamic Binding)
Type of PolymorphismStatic polymorphismDynamic polymorphism
MechanismFunction overloading and operator overloadingVirtual functions and inheritance
Flexibility and ExtensibilityLess flexible and extensibleMore flexible and extensible, as it supports late binding
Implementation DecisionsMade by the compilerMade during program execution by the virtual table
Timing of BindingOccurs at compile timeOccurs 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

Course Schedule

Name Date Details
Python Course 14 Dec 2024(Sat-Sun) Weekend Batch View Details
21 Dec 2024(Sat-Sun) Weekend Batch
28 Dec 2024(Sat-Sun) Weekend Batch

About the Author

Senior Consultant Analytics & Data Science

Sahil Mattoo, a Senior Software Engineer at Eli Lilly and Company, is an accomplished professional with 14 years of experience in languages such as Java, Python, and JavaScript. Sahil has a strong foundation in system architecture, database management, and API integration.