Introduction to Polymorphism

C++ Polymorphism is one of the four fundamental concepts of Object-Oriented Programming (OOP). It allows the same function, interface, or operation to behave differently depending on the object or situation. In C++, polymorphism is mainly classified into compile-time polymorphism and runtime polymorphism. Compile-time polymorphism includes function overloading and operator overloading, while runtime polymorphism is commonly implemented using virtual functions and function overriding.

In this complete C++ polymorphism tutorial, you will learn the definition, types, syntax, practical programs, virtual functions, function overriding, pure virtual functions, abstract classes, advantages, common mistakes, and frequently asked interview questions. The examples are designed for beginners, students, and anyone preparing for C++ programming exams or interviews.

Advantages of Polymorphism

  • Polymorphism allows the same method or function to be used for different objects, which reduces repeated code.
  • It makes programs easier to update and maintain because changes can be done in one place.
  • It increases flexibility because the same code can work in different situations.
  • It reduces code complexity by avoiding duplicate methods and keeping the program simple.
  • It improves readability because similar tasks use the same method name.
  • It supports extensibility, so new features or classes can be added easily without changing much existing code.
  • It saves development time because reusable code makes programming faster and easier.

Types of Polymorphism

Polymorphism is mainly divided into two types: Compile-time Polymorphism and Run-time Polymorphism.

1 Compile-time Polymorphism:
Compile-time Polymorphism is also called Static Polymorphism. In this type, the method to be called is decided at the time of compilation. It is achieved using method overloading. In method overloading, multiple methods have the same name but different parameters. The compiler decides which method to use based on the arguments. It is faster and easier to implement.

Types of Compile-time Polymorphism

Compile-time polymorphism is mainly achieved in two ways:

a. Method Overloading or Function Overloading
Method overloading is a type of compile-time polymorphism where multiple methods have the same name but different parameters. These parameters can differ in number, type, or order. The compiler decides which method to call based on the arguments passed during compilation. It helps in improving code readability and reusability. For example, an add() method can be used to add two numbers or three numbers by changing parameters.

Example of Function Overloading:

#include <iostream>
using namespace std;
class Calculator {
public:
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
};
int main() {
Calculator obj;
cout << obj.add(10, 20) << endl;
cout << obj.add(10, 20, 30) << endl;
}

b. Operator Overloading

Operator overloading is another type of compile-time polymorphism where operators are given different meanings based on the operands. It allows operators like +, -, *, etc., to work with user-defined data types such as objects. The compiler determines the correct operation during compilation. It makes code more intuitive and easier to understand. For example, the An + operator can be used to add two complex numbers or to concatenate strings.

Example of Operating Overloading: 

#include <iostream>
using namespace std;
class Complex {
public:
int real, imag;
Complex(int r, int i) {
real = r;
imag = i;
}
Complex operator + (Complex obj) {
Complex temp(0, 0);
temp.real = real + obj.real;
temp.imag = imag + obj.imag;
return temp;
}
};
int main() {
Complex c1(2, 3), c2(4, 5);
Complex c3 = c1 + c2;
cout << c3.real << " + " << c3.imag << "i" << endl;
}

Rules for Operator Overloading 

  • Only existing operators can be overloaded; we cannot create new operators.
  • The meaning of an operator can be changed only for user-defined data types (like classes).
  • At least one operand must be a user-defined type (object of a class).
  • We cannot change the basic meaning of operators for built-in data types (like int, float).
  • Some operators cannot be overloaded such as ::, . , .*, ?:, sizeof.
  • Operator overloading does not change the number of operands an operator takes.
  • The precedence and associativity of operators cannot be changed.
  • Overloaded operators must follow the syntax rules of the programming language (like C++).

2 Run-time Polymorphism
Run-time Polymorphism is also called Dynamic Polymorphism. In this type, the method to be called is decided at the time of program execution. It is achieved using method overriding. In method overriding, a child class provides a specific implementation of a method that already exists in the parent class. The decision is made during runtime based on the object. It is more flexible but slightly slower than compile-time polymorphism.

Run-time polymorphism mainly has one important type:

A. Method Overriding or Function Overriding.
Method overriding is a type of run-time polymorphism in which a child class provides its own implementation of a method that already exists in the parent class. The method in the child class has the same name, same parameters, and same return type as the parent class method. When the program runs, the decision of which method to execute is made at run time based on the object. If the object is of the child class, the child class method is called instead of the parent class method. This helps in achieving flexibility and reusability in programming. It is widely used in object-oriented programming languages like Java and C++.

Example of Run-Time Polymorphism (Method Overriding)

#include <iostream>
using namespace std;
class Animal {
public:
void sound() {
cout << "Animal makes sound" << endl;
}
};
class Dog : public Animal {
public:
void sound() {
cout << "Dog barks" << endl;
}
};
int main() {
Animal *a; // parent class pointer
Dog d; // child class object
a = &d; // pointing child object

a->sound(); // run-time decision
}

Comparison between Compile-Time and Run-Time Polymorphism.

Compile-Time PolymorphismRun-Time Polymorphism
Also called static polymorphismAlso called dynamic polymorphism
Method is decided at compile timeMethod is decided at run time
Uses method overloadingUses method overriding
Uses early bindingUses late binding (dynamic binding)
Faster in executionSlightly slower in execution
Less flexibleMore flexible
Easier to implementMore complex to implement
Inheritance is not requiredInheritance is required

Comparison between Function Overloading and Function Overriding

Function OverloadingFunction Overriding
It is a compile-time polymorphismIt is a run-time polymorphism
Occurs in the same classOccurs in parent and child classes
Methods have the same name but different parametersMethods have the same name and same parameters
Return type may or may not be sameReturn type is usually same or covariant
Decided by the compilerDecided at run time
Inheritance is not requiredInheritance is required
Also called static bindingAlso called dynamic binding
Improves code readabilityProvides specific implementation in child class

Virtual Functions

A virtual function is a member function in a base (parent) class that is declared using the keyword virtual and is meant to be overridden in a derived (child) class. It is mainly used to achieve run-time polymorphism (dynamic binding) in object-oriented programming.

When a virtual function is used, the program decides at run time which function to call based on the object type, not the pointer or reference type. This allows the same function call to behave differently for different objects.

Virtual functions help make programs more flexible and powerful because they allow the child class to provide its own specific implementation of a function defined in the parent class.

Simple Example (C++)

#include <iostream>
using namespace std;
class Animal {
public:
virtual void sound() {
cout << "Animal sound" << endl;
}
};
class Dog : public Animal {
public:
void sound() override {
cout << "Dog barks" << endl;
}
};
int main() {
Animal *a;
Dog d;
a = &d;
a->sound(); // calls Dog's function (run-time decision)
}

Rules of Virtual Functions

  • A virtual function must be declared in the base (parent) class using the virtual keyword.
  • The function in the derived (child) class must have the same name, same parameters, and same return type as the base class function.
  • Virtual functions are used only with class inheritance.
  • They must be accessed using base class pointer or reference to achieve run-time polymorphism.
  • If a function is not declared as virtual in the base class, run-time polymorphism will not occur properly.
  • A virtual function can be overridden in the child class to provide a new implementation.
  • Constructors cannot be declared as virtual, but destructors can be virtual.
  • Virtual functions support dynamic binding (run-time decision making).

Why is the virtual Keyword Used in C++?

The virtual keyword is used when we want a function call to be resolved dynamically at runtime. Consider:

Animal* animal = new Dog();
animal->sound();

If sound() is virtual, C++ calls the Dog version. Without virtual dispatch, a base-class pointer can resolve the call to the base-class implementation instead. Therefore, virtual functions are essential when implementing runtime polymorphism through base-class pointers or references.

Pure Virtual Function in C++

A pure virtual function is a virtual function that has no implementation in the base class and is declared using = 0. Syntax

virtual void display() = 0;

A class containing at least one pure virtual function is called an abstract class. Example

#include <iostream>
using namespace std;

class Shape {
public:
    virtual void area() = 0;
};

class Circle : public Shape {
public:
    void area() override {
        cout << "Area of circle = πr²" << endl;
    }
};

int main() {
    Circle c;

    c.area();

    return 0;
}

Output

Area of circle = πr²

The Shape class provides a common interface, while the derived class provides the actual implementation.


Abstract Class in C++

An abstract class is a class that cannot normally be instantiated directly because it contains at least one pure virtual function. For example:

class Vehicle {
public:
    virtual void start() = 0;
};

You cannot create an ordinary object such as:

Vehicle v;

Instead, derived classes provide implementations of the pure virtual functions.

 
error: Content is protected !!
Scroll to Top