Learn polymorphism in object-oriented programming (OOP) with clear examples and easy explanations. Understand method overloading, method overriding, runtime and compile-time polymorphism, and how they improve code flexibility, reusability, and scalability.
Polymorphism is a fundamental concept in object-oriented programming (OOP) that allows a single function, method, or interface to perform different behaviors depending on the object or input.
In simple terms, polymorphism means “one name, many forms,” where the same operation behaves differently in different situations.
Polymorphism = “Many Forms”
In programming, it means:
One name (function/method) can do different things depending on the object or input.
Simple Real-Life Example
Think of the word “run”:
- You run in a race
- A machine runs
- A program runs
Same word → different meanings → polymorphism
Programming Example
- For a circle → it draws a circle
- For a square → it draws a square
Same function name → different behavior
Code Example
class Shape {
public:
void draw() {
cout << "Drawing shape";
}
};
class Circle : public Shape {
public:
void draw() {
cout << "Drawing circle";
}
};
class Square : public Shape {
public:
void draw() {
cout << "Drawing square";
}
};
What happens?
Shape* s;
Circle c;
Square sq;
s = &c;
s->draw(); // Drawing circle
s = &sq;
s->draw(); // Drawing square
Same draw() → different outputs
This is polymorphism
Why Do We Need Polymorphism?
- Code Reusability
Write one function and use it for multiple types of objects, reducing duplicate code. - Flexibility in Code
The same interface can handle different data types or objects, making code adaptable. - Cleaner & Maintainable Code
Reduces complexity and keeps code organized, especially in large projects. - Extensibility (Easy to Add Features)
You can add new classes or behaviors without changing existing code. - Supports Dynamic Behavior
Objects can behave differently at runtime, making programs more realistic. - Improves Readability
Using a common method name makes code easier to understand. - Reduces Conditional Statements
Avoids multipleif-elseorswitchcases by using a common interface. - Better Abstraction
Focus on what to do, not how it is done, improving design quality. - Real-World Modeling
Helps represent real-world scenarios where the same action behaves differently.
Types of Polymorphism
There are two main types:

1.Compile-Time Polymorphism (Static Polymorphism)
Decided at compile time (before program runs)
How it works?
The compiler decides which function to call based on:
- Number of parameters
- Type of parameters
A ) Method Overloading
Same function name, but:
- Different number of arguments OR
- Different data types
Example (C++)
class Math {
public:
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
float add(float a, float b) {
return a + b;
}
};
What’s happening?
Math m;
m.add(2, 3); // calls int version
m.add(2, 3, 4); // calls 3-arg version
m.add(2.5, 3.5); // calls float version
Compiler chooses function before execution
B) Operator Overloading
Operators behave differently based on operands.
int a = 5 + 3; // addition
string s = "Hi " + "Bro"; // concatenation
Same + → different behavior
Key Features
- Fast (no runtime decision)
- No inheritance required
- Less flexible than runtime polymorphism
2.Run-Time Polymorphism (Dynamic Polymorphism)
Decided at runtime (during execution)
How it works?
- Uses inheritance
- Uses function overriding
- Uses pointers/references
- Uses virtual functions (C++)
Method Overriding
Child class provides its own version of parent function.
Example (C++)
class Animal {
public:
virtual void sound() {
cout << "Animal sound";
}
};
class Dog : public Animal {
public:
void sound() {
cout << "Dog barks";
}
};
class Cat : public Animal {
public:
void sound() {
cout << "Cat meows";
}
};
Runtime Behavior
Animal* a;
Dog d;
Cat c;
a = &d;
a->sound(); // Dog barks
a = &c;
a->sound(); // Cat meows
Decision happens at runtime, not compile time
What is virtual in C++?
The virtual keyword is used in a base class to ensure that a function can be overridden in a derived (child) class and that the correct function is called at runtime, not compile time.
Definition
virtualenables runtime polymorphism by allowing function calls to be resolved dynamically (during execution).
Why Do We Need virtual?
Without virtual, C++ uses early binding (compile-time)
With virtual, C++ uses late binding (runtime)
This ensures the program calls the correct function based on the actual object, not the pointer type.
Without virtual (Problem ❌)
class Animal {
public:
void sound() {
cout << "Animal sound";
}
};
class Dog : public Animal {
public:
void sound() {
cout << "Dog barks";
}
};
Animal* a;
Dog d;
a = &d;
a->sound(); // ❌ Output: Animal sound
Even though object is Dog, it calls Animal function
Because binding happens at compile time
With virtual (Solution ✔️)
class Animal {
public:
virtual void sound() {
cout << "Animal sound";
}
};
class Dog : public Animal {
public:
void sound() {
cout << "Dog barks";
}
};
Animal* a;
Dog d;
a = &d;
a->sound(); // ✔️ Output: Dog barks
Now correct function is called at runtime
How virtual Works Internally
When you use virtual:
Compiler creates:
- vtable (Virtual Table) → stores function pointers
- vptr (Virtual Pointer) → inside each object
At runtime:
- Object’s
vptrpoints to correct function in vtable - Function call is resolved dynamically
Key Concepts Related to virtual
1.Function Overriding
Child class replaces parent function
2.Base Class Pointer
Animal* a;
Needed for runtime polymorphism
3.Dynamic Binding
Function call decided at runtime
Virtual Destructor
class Base {
public:
virtual ~Base() {
cout << "Base destructor";
}
};
Ensures:
- Proper cleanup of derived class objects
- Prevents memory leaks
Pure Virtual Function (Advanced)
class Shape {
public:
virtual void draw() = 0; // pure virtual
};
Makes class abstract
Child class must implement this function
When to Use virtual?
- When using inheritance
- When behavior should change in child classes
- When using base class pointers/references
- When implementing polymorphism
Why virtual is important?
Without virtual:
Base class function will always be called ❌
With virtual:
Correct child function is called ✔️
Behind the scenes
- Uses vtable (virtual table)
- Each object keeps pointer to correct function
- Enables dynamic dispatch
Key Features
- Flexible
- Supports real-world modeling
- Slightly slower (runtime decision)
- Requires inheritance
Difference Between Both
| Feature | Compile-Time | Run-Time |
|---|---|---|
| Decision Time | Before execution | During execution |
| Speed | Faster | Slightly slower |
| Concept | Overloading | Overriding |
| Inheritance | Not required | Required |
| Flexibility | Less | More |
Simple Analogy
- Compile-time → Teacher assigns roles before play
- Run-time → Actor changes role on stage dynamically
Compile-Time Polymorphism
Other Names:
- Static Polymorphism
- Early Binding
- Static Binding
All mean the same thing:
Decision is made before program runs
Run-Time Polymorphism
Other Names:
- Dynamic Polymorphism
- Late Binding
- Dynamic Binding
All mean:
Decision is made during program execution
Example WITHOUT virtual ❌ (Compile-Time Binding)
Code
#include <iostream>
using namespace std;
class Animal {
public:
void speak() {
cout << "Animal speaks" << endl;
}
};
class Dog : public Animal {
public:
void speak() {
cout << "Dog barks" << endl;
}
};
int main() {
Animal* a;
Dog d;
a = &d;
a->speak(); // What will this print?
return 0;
}
Output
Animal speaks
Why this happens (Important)
- Pointer type =
Animal* - Object type =
Dog
But C++ ignores object type here
It only looks at pointer type at compile time
So:
- Compiler decides → call
Animal::speak() - This is called Static Binding / Early Binding
Key Point
Without
virtual, function call depends on pointer type, not actual object.
Example WITH virtual ✔️ (Run-Time Binding)
Code
#include <iostream>
using namespace std;
class Animal {
public:
virtual void speak() {
cout << "Animal speaks" << endl;
}
};
class Dog : public Animal {
public:
void speak() {
cout << "Dog barks" << endl;
}
};
int main() {
Animal* a;
Dog d;
a = &d;
a->speak(); // Now what?
return 0;
}
Output
Dog barks
Why this happens
Because of virtual:
Compiler delays decision until runtime
It checks actual object type (Dog)
Calls Dog::speak()
This is called:
What Changes Internally?
Without virtual:
- Direct function call
- No extra mechanism
- Faster
- No polymorphism
With virtual:
C++ creates:
vtable (Virtual Table)
- A table storing addresses of virtual functions
vptr (Virtual Pointer)
- Hidden pointer inside each object
Flow at Runtime:
a = &d;- Object
dhas its own vptr vptrpoints to Dog’s vtablea->speak():- Looks into vtable
- Finds
Dog::speak() - Calls it ✔️
Side-by-Side Comparison
| Feature | Without virtual | With virtual |
|---|---|---|
| Binding | Compile-time | Run-time |
| Function Called | Based on pointer | Based on object |
| Polymorphism | ❌ No | ✔️ Yes |
| Speed | Faster | Slightly slower |
| Flexibility | Low | High |
Real-Life Analogy
Without virtual:
You call a person by their job title only
(“Hey Employee”) → always same response
With virtual:
You call based on actual person
(“Hey Developer / Manager”) → different response
Basic Interview Questions
- What is polymorphism?
- What are the types of polymorphism?
- Difference between compile-time and runtime polymorphism?
- What is method overloading vs method overriding?
- What is the
virtualkeyword in C++? - Why do we use
virtualfunctions?
Core Concept Questions
- What happens if we don’t use
virtualin function overriding? - Why do we need a base class pointer for runtime polymorphism?
- Explain early binding vs late binding.
- How does C++ decide which function to call?
- Can we achieve polymorphism without inheritance? (Yes → overloading)
Code-Based Questions
Interviewer may give code like this:
class A {
public:
void show() { cout << "A"; }
};
class B : public A {
public:
void show() { cout << "B"; }
};
int main() {
A* obj = new B();
obj->show();
}
Question:
What will be the output and why?
Expected Answer:
- Output =
A(because novirtual) - Uses compile-time binding
Same with virtual
virtual void show()
Output = B
Because of runtime polymorphism
Advanced Questions
- What is a virtual table (vtable)?
- What is a virtual pointer (vptr)?
- How does dynamic dispatch work internally?
- What is the cost of using virtual functions?
- Can constructors be virtual? (No ❌)
- Can destructors be virtual? (Yes ✔️, very important)
Tricky Questions
- What happens if base class destructor is not virtual?
👉 Can cause memory issues / incomplete cleanup - Can we override a non-virtual function?
👉 Yes, but no runtime polymorphism - Difference between
virtualandpure virtual?
👉 Pure virtual makes class abstract - What is an abstract class?
👉 Class with at least one pure virtual function
Scenario-Based Questions
- When should you use polymorphism in real projects?
- Give a real-world example of polymorphism.
- Where have you used virtual functions?
- How would you design a system using polymorphism?
Top 5 Must-Prepare Questions
If you prepare only these, you’re safe 👇
- What is polymorphism with example?
- Compile-time vs runtime polymorphism
- What is
virtualand why needed? - Output-based question (with/without virtual)
- What is vtable and how it works?
For detailed understanding of Platform Devices and Drivers on Linux, refer to the Linux documentation on Platform Devices and Drivers .
Mr. Raj Kumar is a highly experienced Technical Content Engineer with 7 years of dedicated expertise in the intricate field of embedded systems. At Embedded Prep, Raj is at the forefront of creating and curating high-quality technical content designed to educate and empower aspiring and seasoned professionals in the embedded domain.
Throughout his career, Raj has honed a unique skill set that bridges the gap between deep technical understanding and effective communication. His work encompasses a wide range of educational materials, including in-depth tutorials, practical guides, course modules, and insightful articles focused on embedded hardware and software solutions. He possesses a strong grasp of embedded architectures, microcontrollers, real-time operating systems (RTOS), firmware development, and various communication protocols relevant to the embedded industry.
Raj is adept at collaborating closely with subject matter experts, engineers, and instructional designers to ensure the accuracy, completeness, and pedagogical effectiveness of the content. His meticulous attention to detail and commitment to clarity are instrumental in transforming complex embedded concepts into easily digestible and engaging learning experiences. At Embedded Prep, he plays a crucial role in building a robust knowledge base that helps learners master the complexities of embedded technologies.













