optional · CSS study notes
12. Polymorphism
"Many forms" — the ability of different objects/classes to respond to the same interface/message in type-specific ways.
| Type | Mechanism | Resolved | Example |
|---|---|---|---|
| Compile-time (static) | Function overloading, Operator overloading | At compile time — compiler picks the matching version by signature | add(int,int) vs add(float,float) |
| Runtime (dynamic) | Virtual functions + inheritance | At runtime — via the vtable, based on the object's actual type | Shape* s = new Circle(); s->area(); calls Circle::area() |
This distinction matters for exams: many students only associate polymorphism with virtual functions, but function/operator overloading is polymorphism too, just resolved earlier (at compile time instead of runtime).
The umbrella term — and its two mechanisms in full
Polymorphism means the same interface (function name/operator) behaves differently depending on context. C++ achieves this two distinct ways:
1. Compile-time (static) polymorphism — Function Overloading
class MathUtil
{
public:
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; } // same name, different signature
};
// The COMPILER decides which add() to call based on argument types, before the program even runs
2. Runtime (dynamic) polymorphism — Virtual Functions
class Shape { public: virtual void draw() { cout << "shape\n"; } };
class Circle : public Shape { public: void draw() override { cout << "circle\n"; } };
Shape* s = new Circle();
s->draw(); // prints "circle" -- decided at RUNTIME, based on the actual object type
Why this distinction matters on the exam
A question like "what is polymorphism, give an example" is often answered with ONLY the virtual-function case — missing half the marks available for also covering function/operator overloading. Both are legitimate forms of polymorphism; they just resolve at different times.
Quick self-check: which is it?
| Scenario | Type |
|---|---|
| Two functions, same name, different parameter lists | Compile-time |
A base class pointer calling an overridden virtual method | Runtime |
a + b calling a custom operator+ | Compile-time |
An array of Shape* calling draw() and each shape drawing differently | Runtime |
Important MCQs
- Function overloading is an example of:
- a) Dynamic/runtime polymorphism b) Static/compile-time polymorphism c) Inheritance d) Not polymorphism at all
- Virtual functions achieve which type of polymorphism?
- a) Compile-time b) Runtime/dynamic c) Static d) None
Shape* s = new Circle(); s->draw();callingCircle::draw()is resolved:- a) At compile time based on the pointer type b) At runtime, based on the actual object type c) Randomly d) Never, this is a compile error
Self-Test Questions
- Give one example each of compile-time and runtime polymorphism.
- Why is function overloading considered "polymorphism" even though it has nothing to do with
virtual? - What determines, in the virtual function case, which version actually runs?
📌 Sample & Repeated FPSC Questions (2016–2026)
2025, Paper I, Q3(c) (8 marks) — Explain the role of pure virtual functions in implementing abstract classes in C++. How do they differ from virtual functions? Give an example of C++ classes where both types are used.
A pure virtual function (virtual void draw() = 0;) has no implementation to fall back on and makes its class abstract — uninstantiable until a derived class overrides every such function. An ordinary virtual function has a body in the base class; a derived class may override it, but the class remains instantiable either way.
class Shape
{
public:
virtual double area() const = 0; // pure virtual -- forces implementation
virtual void display() const { cout << "Area = " << area(); } // ordinary virtual -- has a default
};
class Circle : public Shape
{
double radius;
public:
double area() const override { return 3.1416 * radius * radius; }
// display() not overridden -- reuses Shape's version, which still calls Circle::area() polymorphically
};
Key reasoning points: Shape s; is a compile error (pure virtual area()); Circle c; compiles because it supplies area(). display() shows an ordinary virtual function reused as-is via runtime polymorphism.
2023, Paper I, Q4(a) (8 marks) — What is Polymorphism? Explain Ad-Hoc Polymorphism, Parametric Polymorphism & Subtyping with suitable coding examples.
Three distinct kinds, beyond just the compile-time/runtime split already on this page:
- Ad-hoc polymorphism — same name behaves differently for specific, enumerated types, resolved at compile time (function/operator overloading):
int add(int,int)vsdouble add(double,double). - Parametric polymorphism — one piece of code works uniformly for any type via a type parameter (generics/templates):
template<typename T> T maxVal(T a, T b) { return a > b ? a : b; }. - Subtyping (inclusion) polymorphism — a base-typed pointer/reference can operate on any derived-type object, behavior chosen at runtime via
virtual:Shape* s = new Circle(); s->draw();.
Exam angle: the two-row "compile-time vs runtime" table above this section is the simplified view; this three-way Ad-hoc/Parametric/Subtyping breakdown is the more advanced classification examiners sometimes expect for a full 8-mark answer — mention both framings if the question just says "explain polymorphism."
2023, Paper I, Q4(b) (6 marks) — Discuss Late Binding with a short program.
Late (dynamic) binding means the function actually executed is decided at run time based on the object's real type, not at compile time based on the pointer's declared type — achieved via virtual.
class Animal { public: virtual void sound() { cout << "Some sound"; } };
class Dog : public Animal { public: void sound() override { cout << "Bark"; } };
Animal* a = new Dog();
a->sound(); // late-bound via the vtable -> prints "Bark", even though a is declared Animal*
Contrast with early (static) binding, used for non-virtual functions, which is resolved purely at compile time from the pointer's declared type.
2022, Paper I, Q5(b) (10 marks) — What is a virtual function in C++? Using an example, explain how the virtual function mechanism works.
A virtual function is declared virtual in the base class so a derived class can override it, with the version executed chosen at runtime by the object's real type. Mechanically: the compiler gives any class with virtual functions a hidden vtable (array of function pointers, one per virtual function); each object carries a hidden vptr to its class's vtable. A call through a base pointer is translated into an indirect call via vptr → vtable, so it always lands on the actual class's function.
class Animal { public: virtual void speak() { cout << "generic sound"; } };
class Dog : public Animal { public: void speak() override { cout << "Bark!"; } };
class Cat : public Animal { public: void speak() override { cout << "Meow!"; } };
Animal* a1 = new Dog(); Animal* a2 = new Cat();
a1->speak(); // "Bark!"
a2->speak(); // "Meow!" -- both via Animal* pointers, resolved by vtable at runtime
Key reasoning points: the vtable/vptr mechanism is the concrete, examiner-favorite detail behind "how virtual functions work" — don't just say "runtime polymorphism," explain the vptr indirection.
📜 Past-Paper Questions & MCQs (CSS 2016–2026)
2023 · Paper-I · Q4(a) · 8 marks
Q. What is polymorphism? Explain ad-hoc polymorphism, parametric polymorphism and subtyping with coding examples.
Ans.
- Polymorphism means "many forms": one interface, different behaviour depending on the type.
- Ad-hoc polymorphism uses function or operator overloading (and type coercion). One name, different implementations for different argument types.
int add(int a, int b); double add(double a, double b); // overloaded
- Parametric polymorphism uses templates (generics). One piece of code works for any type.
template <class T> T maxOf(T a, T b) { return a > b ? a : b; }
maxOf(3, 5); maxOf(2.5, 1.7);
- Subtyping (inclusion) polymorphism uses inheritance and virtual functions. A base-class pointer calls the derived-class version at run time.
class Shape { public: virtual void draw() { } };
class Circle : public Shape { public: void draw() override { cout << "Circle"; } };
Shape *s = new Circle(); s->draw(); // prints Circle
MCQs
2026 P-I · Q4. A class needs to allow different implementations of a method. Which concept should be used?
- (A) Inheritance
- (B) Polymorphism ✅
- (C) Encapsulation
- (D) None of these
- Answer: (B) Polymorphism.
- Why: polymorphism is exactly the ability of one method name or interface to have different implementations (overloading, overriding). Inheritance is about reusing a base class, and encapsulation is about hiding data.
2026 P-I · Q8. A developer wants to define a class with methods that can be used with different data types. What should be used?
- (A) Templates ✅
- (B) Inheritance
- (C) Polymorphism
- (D) None of these
- Answer: (A) Templates.
- Why: a template (generic) is written once with a type parameter
Tand works for int, double, string and so on. Inheritance and polymorphism work through class hierarchies, not through type parameters.
2024 P-I · Q8. Which programming language allows for polymorphism but not classes?
- (A) C++ programming language
- (B) Java programming language
- (C) Ada programming language ✅
- (D) None of these
- Answer: (C) Ada (the intended key).
- Why: C++ and Java are class-based. Ada (the original Ada 83) was object-based: it had packages and generic (parametric) polymorphism and overloading but no classes.
- Note: this item is old and arguable, so learn the key.