Polymorphism is a Greek word that translates to "many forms." In C++, it is the ability of a message (a function call or operator) to be processed in more than one way. It allows a single interface to represent a general class of actions, with the specific action being determined by the exact nature of the situation.
Theoretically, polymorphism is divided into two main categories based on when the decision of which function to call is made.
The compiler determines which function to call at the time of compilation. This is fast and efficient.
Function Overloading: Multiple functions with the same name but different parameters.
Operator Overloading: Giving special meaning to operators (like + or <<) for user-defined classes.
The decision of which function to call is delayed until the program is actually running. This is achieved through Inheritance and Virtual Functions.
Method Overriding: A derived class provides a specific implementation of a function already defined in its base class.
You can have multiple functions with the same name as long as their "signatures" (number or types of arguments) are different.
Theory: The compiler performs "Name Mangling," creating unique internal names for each version of the function to keep them distinct.
This allows you to redefine how operators work with objects. For example, you can use the + operator to add two ComplexNumber objects together.
This is the most "powerful" form of polymorphism. It allows a Base Class Pointer to point to a Derived Class Object and call the correct version of a function.
To enable runtime polymorphism, you must declare the base class function as virtual.
Theory: Without virtual, C++ performs "Early Binding," calling the function associated with the pointer type.
With virtual, C++ performs "Late Binding," calling the function associated with the actual object the pointer is holding.
How does the computer know which function to call at runtime? C++ uses a hidden mechanism called the Virtual Table (VTable).
The VTable: For every class that contains at least one virtual function, the compiler creates a static table. This table contains the memory addresses of all the virtual functions for that specific class.
The VPtr (Virtual Pointer): Every object created from such a class contains a hidden pointer (vptr). This pointer points to the VTable of its class.
The Call: When you call a virtual function through a pointer, the program follows these steps:
Follow the object's vptr to the VTable.
Look up the address of the function in the VTable.
Jump to that address and execute the code.
Theoretical Cost: Runtime polymorphism has a tiny overhead because of this extra "look-up" step, but it provides immense flexibility.
#include <iostream> using namespace std; // Base Class class Shape { public: // Virtual function: enables runtime polymorphism virtual void draw() { cout << "Drawing a generic shape..." << endl; } // Virtual destructor is vital when using polymorphism! virtual ~Shape() {} }; class Circle : public Shape { public: void draw() override { // 'override' is optional but best practice cout << "Drawing a Circle!" << endl; } }; class Square : public Shape { public: void draw() override { cout << "Drawing a Square!" << endl; } }; int main() { // A base class pointer can point to any derived object Shape* s; Circle c; Square sq; s = &c; s->draw(); // Calls Circle's draw() s = &sq; s->draw(); // Calls Square's draw() return 0; }
Sometimes, a base class is so general that it shouldn't have a definition for a function. For example, what does it mean to "draw" a generic Shape?
Pure Virtual Function: A function declared with = 0.
virtual void draw() = 0;
Abstract Class: Any class containing at least one pure virtual function. You cannot create an object of an abstract class. It exists only to act as a blueprint for other classes.
Theory: This forces all derived classes to implement the function, ensuring a consistent interface across your entire program.
Copyright ©2025. All Rights Reserved Emblab THE RAVE INNOVATION