Theory
Two decisions, two clocks
Yesterday you saw the till behave two ways: bill(50) vs bill(50, 0.1) picked by the compiler, and p.discount() picked only when a real card tapped.
Same pillar, but the moment of decision is completely different: one choice is sealed before the program ever starts, the other stays open until the exact instant of the call.
Examiners adore this contrast: "Differentiate compile-time and run-time polymorphism" is close to a guaranteed question. Let us make the difference impossible to forget.
Theory
Printed menu vs daily special
The canteen's printed menu was fixed at the printing press: whatever you order from it, the kitchen's response was decided long ago. That is compile-time: decided before serving begins.
The daily special board is decided each morning by whoever runs the kitchen that day. Same board, different dish depending on the actual day. That is run-time: the answer exists only in the moment.
Theory
Binding: the word behind both
Binding means connecting a function call to the function body that will run.
- Early (static) binding: the connection is fixed during compilation. The compiler reads
bill(50, 0.1), matches the argument list to one overload, and hard-wires the call. - Late (dynamic) binding: the connection is made while the program runs. The call
p.discount()is routed to whichever object p actually refers to, checked at that moment.
Compile-time polymorphism uses early binding; run-time polymorphism uses late binding.
At a glance
The exam table
| Aspect | Compile-time | Run-time |
|---|---|---|
| Also called | Static, early binding | Dynamic, late binding |
| Decided | During compilation | During execution |
| Achieved by | Function + operator overloading | Virtual functions + inheritance |
| Needs inheritance? | No | Yes, with base pointer/reference |
| Speed | Faster (call hard-wired) | Slightly slower (looked up) |
| Flexibility | Fixed at build | Adapts to the actual object |
Practical
Both clocks in one program
#include <iostream>
using namespace std;
class Person {
public:
virtual double discount() { return 0.0; } // virtual: late binding
};
class Student : public Person {
public:
double discount() { return 0.10; } // overrides Person's
};
// COMPILE-TIME: two overloads, picked by argument list
double bill(double amt) { return amt; }
double bill(double amt, double disc) { return amt * (1 - disc); }
int main() {
cout << bill(50.0) << endl; // compiler chose overload 1
cout << bill(50.0, 0.2) << endl; // compiler chose overload 2
Person *p = new Student(); // base pointer, derived object
cout << p->discount() << endl; // 0.10: decided AT RUN TIME
delete p;
return 0;
}
Think first
Remove one word, change the answer
In the code above, delete the word virtual from Person::discount(). Now what does p->discount() print, and why?
Show the answer
It prints 0, the Person version. Without virtual, the compiler binds the call early, by the pointer's type: p is a Person pointer, so Person::discount() is hard-wired, no matter what object actually lives there. One keyword is the entire difference between early and late binding, which is why exams love asking for output with and without virtual.
Quiz
Which statement about run-time polymorphism is TRUE?
- It requires inheritance and is achieved through virtual functions
- It is faster than compile-time polymorphism because decisions are fresher
- Function overloading is its most common example
- The compiler resolves it by checking argument types
Show the answer
It requires inheritance and is achieved through virtual functions
Run-time polymorphism exists only across an inheritance hierarchy, dispatched through virtual functions via base pointers/references. It is marginally slower (the program looks the target up at call time), not faster, so B is wrong. C and D describe compile-time polymorphism: overloading, resolved from the argument list during compilation. Swapping the two families is the classic lost mark here.
Watch out
Where answers lose marks
Three precision points examiners check:
- Say "early/static binding" for compile-time and "late/dynamic binding" for run-time; the synonyms ARE the marks.
- Overriding without
virtualis NOT run-time polymorphism: the base pointer still calls the base version. - Templates also count as compile-time polymorphism; mention them only if your paper covers them, else stick to overloading.
Theory
The rule of thumb that always works
Ask one question: could the compiler already know? If the argument list decides (overloading), it could, so compile-time. If the answer depends on which object a pointer happens to hold while running, it could not, so run-time. The next lessons zoom into each family: overloading vs overriding first, then virtual functions themselves.
Summary
Key takeaways
- Binding = linking a call to a function body; early binding at compilation, late binding at execution.
- Compile-time polymorphism: function/operator overloading, resolved from the argument list, faster.
- Run-time polymorphism: virtual functions + inheritance via base pointers/references, flexible.
- Without the virtual keyword, the pointer's type decides: base version runs (early binding).
- Run-time needs inheritance; compile-time does not.
- Memory hook: printed menu vs daily special board.