Curriculum
Course: Learn C++
Login

Curriculum

Learn C++

Text lesson

Introduction to Polymorphism in C++

In this lesson, you will learn

  • Polymorphism and its Type
  • Compile Time Polymorphism
  • Function Overloading
  • Operator Overloading
  • Function Overriding

 

Polymorphism

Polymorphism in C++ means the same entity (function or object) behaves differently in different scenarios.

For example: A person can be a father to someone, a husband, a boss, an employee, a son, a brother, or can have many other relationships with various people.

polymrphism1

Here, a person represents the object, and his relationships display the object’s ability to be represented in many forms with totally different characteristics.

 

Need of Polymorphism

Polymorphism helps to write code consistently and efficiently.

For example, suppose you want to find the area of a circle and a rectangle.

So, you create a base class that will deal with the type of shape. This base class contains two derived classes, one for the circle and another for the rectangle.

Now, instead of declaring two separate functions with separate names in both the derived classes to calculate the area of each shape, you can declare one function in the base class and override it in the child classes to calculate the area.

In this way, you increase the code consistency using polymorphism.

 

Types of Polymorphism

Polymorphism in C++ is broadly classified into two categories based on their characteristic features as shown below

Polymorphism in C++

 

1. Compile Time Polymorphism

In compile-time polymorphism, an object is bound to its function call at compile-time. It means, there is no ambiguity at compile time about which function is linked to a particular call. This is also called early binding, static binding, or static linking.

The compile-time polymorphism can be achieved in two ways

  1. Function Overloading
  2. Operator Overloading

 

Function Overloading

Function overloading allows multiple functions with the same name to exist in the same scope, with each function having a different parameter list (either in the number or type of parameters). This helps in defining functions with similar purposes but different implementations.

Example-1: Compile Time Polymorphism (Method Overloading)

#include<iostream>
using namespace std;

class Calculator {
public:
    // 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 add two double values
    double add(double a, double b) {
        return a + b;
    }
};

int main() {
    Calculator calc;

    cout << "Sum of 10 and 20: " << calc.add(10, 20) << endl; // Calls add(int, int)
    cout << "Sum of 10, 20, and 30: " << calc.add(10, 20, 30) << endl; // Calls add(int, int, int)
    cout << "Sum of 5.5 and 6.5: " << calc.add(5.5, 6.5) << endl; // Calls add(double, double)

    return 0;
}

 

Output:

Sum of 10 and 20: 30
Sum of 10, 20, and 30: 60
Sum of 5.5 and 6.5: 12

Operator Overloading

Operator overloading allows defining a new behavior for existing operators (like +, -, *, etc.) when they are used with user-defined types. This enables you to use operators with custom objects in a natural way.

Example-2: Compile Time Polymorphism (Operator Overloading)

#include<iostream>
using namespace std;

class Complex {
private:
    float real, imag;

public:
    Complex(float r = 0, float i = 0) : real(r), imag(i) {}

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

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

int main() {
    Complex c1(3.2, 4.5), c2(1.1, 2.3);
    Complex c3 = c1 + c2;
    c3.display();
    return 0;
}

 Here, the + operator is overloaded to add two Complex objects by adding their real and imaginary parts separately.

 

Example-3: Compile Time Polymorphism(Inheritance)

#include<iostream>
using namespace std;

class Shape {
public:
    // Function to calculate the area of a square
    double area(double side) {
        return side * side;
    }
};

class Rectangle : public Shape {
public:
    // Overloaded function to calculate the area of a rectangle
    double area(double length, double width) {
        return length * width;
    }
};

int main() {
    Rectangle rect;

    // Calling the overloaded area functions using the derived class object
    cout<<"Area of rectangle with length 5 and width 10: " 
    <<rect.area(5, 10) << endl; // Calls Rectangle::area(double, double)
    cout<<"Area of square with side 5: " 
    <<rect.Shape::area(5) << endl; // Calls Shape::area(double)

    return 0;
}


Output:

Area of rectangle with length 5 and width 10: 50
Area of square with side 5: 25

 

Important!

In C++, compile-time polymorphism typically involves function overloading (same function name with different parameters) or operator overloading, and it cannot involve the same function signature in both base and derived classes. If the same method with the same signature is defined in both base and derived classes, it is instead a case of function hiding rather than polymorphism.

Let me clarify this with an example. If you have a function with the same name, signature, and type in both base and derived classes (without using virtual), then the derived class function will hide the base class function when accessed through a derived class object.

However, this does not fall under compile-time polymorphism or true polymorphism at all.

 

Example – Function Hiding with the Same Method, Signature, and Type

Here’s an example where the derived class function hides the base class function with the same method signature:

#include<iostream>
using namespace std;

class Base {
public:
    // Function with the same name and signature in both base and derived classes
    void show() {
        cout << "Show from Base class" << endl;
    }
};

class Derived : public Base {
public:
    // Same function as in the base class (no virtual keyword)
    void show() {
        cout << "Show from Derived class" << endl;
    }
};

int main() {
    Base base;
    Derived derived;

    // Calling the function through a Base class object
    base.show(); // Calls Base::show()

    // Calling the function through a Derived class object
    derived.show(); // Calls Derived::show(), hides Base::show()

    // Calling the base class using scope resolution operator
    derived.Base::show(); //Calls the base class show()
    
    return 0;
}

Output:

Show from Base class
Show from Derived class
Show from Base class

In this example, derived.show() function call overrides the show() function defined in the base class. So, to call a specific function of a base class, you have to use the scope resolution (::) operator.

 

Function Overriding

Function overriding in C++ occurs when two or more functions with the same name, arguments, and return type exist in base and derived classes.

If you create an object of the derived class and write code to access that member function, the member function in the derived class is only invoked; i.e., the member function of the derived class overrides the member function of the base class.

 

Accessing Base Class Overridden Function using Derived Class Object

 

2. Run-Time Polymorphism

It refers to the linking of a function call to a particular class much later i.e. run time. Thus, it does not know which function will be invoked till an object makes the function call during a program execution.

This process is known as late or dynamic binding.

In C++, run-time polymorphism is achieved using a virtual function.

 

Need of Run-Time Polymorphism

The scope resolution operator is fine to resolve the ambiguity but if you want to build a large or complex application on a large scale(which can be expanded in the future to add more features), then it would be very difficult to modify the code that implements the static binding or compile time polymorphism. So, there is a need for Run-Time polymorphism.

 

 

 

 

 

 

Layer 1
Login Categories