optional · CSS study notes
13. Virtual Function
A base-class function declared virtual, allowing a derived class to override it such that the correct derived version is called through a base-class pointer/reference at runtime (dynamic binding).
class Shape
{
public:
virtual double area() { return 0; } // virtual -- can be overridden
virtual ~Shape() {} // virtual destructor -- required for polymorphic base classes
};
class Circle : public Shape
{
double radius;
public:
Circle(double r) : radius(r) {}
double area() override { return 3.1416 * radius * radius; }
};
Shape* s = new Circle(5);
cout << s->area(); // calls Circle::area(), NOT Shape::area() -- dynamic dispatch
delete s;
Without virtual, s->area() would call Shape::area() regardless of the actual object type ("static binding") — the virtual keyword is what enables runtime polymorphism.
The problem virtual functions solve
class Animal { public: void speak() { cout << "Some sound\n"; } }; // NOT virtual
class Dog : public Animal { public: void speak() { cout << "Bark!\n"; } };
Animal* a = new Dog();
a->speak(); // prints "Some sound" -- WRONG result if you wanted Dog's version
Without virtual, the compiler binds the call to Animal::speak() at compile time, based purely on the pointer's declared type — this is called static binding.
The fix
class Animal { public: virtual void speak() { cout << "Some sound\n"; } }; // now virtual
class Dog : public Animal { public: void speak() override { cout << "Bark!\n"; } };
Animal* a = new Dog();
a->speak(); // prints "Bark!" -- CORRECT -- resolved at runtime based on the actual object
This is dynamic binding: the compiler inserts a lookup through a hidden table (the vtable) at runtime to find the correct function for the object's actual type, not its declared pointer type.
How it works internally (brief, exam-relevant)
Every class with at least one virtual function gets a hidden vtable (virtual table) — an array of function pointers, one per virtual function. Each object of that class carries a hidden pointer (vptr) to its class's vtable. When you call a->speak(), the program follows a's vptr to the vtable and calls whichever speak() is listed there — which is Dog::speak() if a actually points to a Dog.
The override keyword (good practice, not mandatory)
class Dog : public Animal
{
public:
void speak() override { cout << "Bark!\n"; } // 'override' -- compiler checks this actually overrides something
// void spek() override { ... } -- COMPILE ERROR: no matching virtual function to override (typo caught!)
};
override doesn't change behavior — it makes the compiler verify you're actually overriding a real virtual function, catching typos like spek() instead of speak() at compile time instead of silently creating an unrelated new function.
Important MCQs
- A hidden table of function pointers used to implement runtime polymorphism is called the:
- a) Stack b) vtable c) Heap d) Symbol table
- Every object of a class with at least one virtual function carries a hidden pointer called the:
- a)
thispointer only b) vptr, pointing to the class's vtable c) Static pointer d) Friend pointer
- a)
- The
overridekeyword:- a) Changes runtime behavior b) Makes the compiler verify the function actually overrides a base virtual function (catches typos) c) Is required for all functions d) Only works with pure virtual functions
- Without
virtual, calling a derived method through a base pointer uses:- a) Dynamic binding b) Static binding, based on the pointer's declared type c) The vtable d) Multiple dispatch
Self-Test Questions
- What is "static binding," and why does it cause the wrong function to run in the first example?
- What are the vtable and vptr, and what role does each play?
- Does adding
overridechange the program's behavior? What does it actually do?
📌 Sample & Repeated FPSC Questions (2016–2026)
2022, Paper I, Q5(b) (10 marks) — What is a virtual function in C++? Using an example, explain how does a virtual function mechanism work.
A virtual function is a base-class member function declared with virtual so a derived class can override it, and the version actually executed through a base-class pointer/reference is chosen at runtime based on the object's real type, not the pointer's declared type (dynamic/late binding).
Mechanism: the compiler gives every class with ≥1 virtual function a hidden vtable (array of function pointers, one per virtual function); every object of such a class carries a hidden vptr pointing to its class's vtable. A call like a->speak() compiles to an indirect call through a's vptr into the vtable, so it always lands on the function belonging to the object's actual class.
class Animal {
public:
virtual void speak() { cout << "Some generic animal sound" << endl; }
};
class Dog : public Animal {
public:
void speak() override { cout << "Bark!" << endl; }
};
class Cat : public Animal {
public:
void speak() override { cout << "Meow!" << endl; }
};
int main() {
Animal* a1 = new Dog();
Animal* a2 = new Cat();
a1->speak(); // Bark!
a2->speak(); // Meow!
delete a1; delete a2;
}
Both pointers are declared Animal*, yet each call resolves to its own object's override — naming the vtable/vptr indirection explicitly is what earns full marks here.
2023, Paper I, Q4(b) (6 marks) — Discuss Late Binding with a short program.
Late (dynamic) binding means the function actually executed for a call is decided at runtime from the object's actual type, not at compile time from the pointer/reference's declared type — achieved in C++ via virtual functions.
class Animal {
public:
virtual void sound() { cout << "Some generic sound" << endl; }
};
class Dog : public Animal {
public:
void sound() override { cout << "Bark" << endl; }
};
int main() {
Animal* a = new Dog();
a->sound(); // late-bound -> "Bark", even though a is statically an Animal*
delete a;
}
Contrast with early (static) binding — used for non-virtual functions, resolved purely at compile time from the pointer's declared type (see this page's "problem virtual functions solve" section above for exactly this failure mode).
MCQ Trap: late binding needs three things together — a base-class pointer/reference, a virtual function, and a call made through that pointer/reference. Calling a virtual function on an object passed by value never uses late binding (object slicing defeats polymorphism).
2023, Paper I, Q4(a) (8 marks) — What is Polymorphism? Explain Ad-Hoc Polymorphism, Parametric Polymorphism & Subtyping with suitable coding examples.
Polymorphism means one interface behaving differently depending on the type involved. Three kinds matter for this exam:
- Ad-hoc (compile-time) — function/operator overloading: same name, different enumerated types, resolved at compile time.
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
- Parametric (generic) — one piece of code works for any type via a type parameter (templates).
template <typename T>
T maxVal(T a, T b) { return (a > b) ? a : b; }
- Subtyping (inclusion, run-time) — a base pointer/reference operates on any derived object; the override that runs is chosen at run time via virtual functions — exactly the mechanism this page covers.
class Shape { public: virtual void draw() { cout << "Shape"; } };
class Circle : public Shape { public: void draw() override { cout << "Circle"; } };
Shape* s = new Circle();
s->draw(); // "Circle" -- resolved at run time
Exam angle: when asked "what kind of polymorphism does C++ support," naming all three types — not just virtual-function/subtyping polymorphism — is what separates a full-marks answer from a partial one.
📜 Past-Paper Questions & MCQs (CSS 2016–2026)
2022 · Paper-I · Q5(b) · 10 marks
Q. What is a virtual function in C++? Using an example, explain how the virtual function mechanism works.
Ans.
- A virtual function is a member function declared with
virtualin a base class that a derived class can override. A call through a base-class pointer or reference is resolved at run time to the version for the actual object type. - Mechanism:
- every class with virtual functions gets a vtable, a table of pointers to its virtual functions
- every object holds a hidden vptr that points to its class's vtable
- on a call such as
p->speak(), the program follows the vptr, looks up the function in the vtable and calls it (dynamic or late binding)
class Animal { public: virtual void speak() { cout << "Animal sound\n"; } virtual ~Animal() {} };
class Dog : public Animal { public: void speak() override { cout << "Bark\n"; } };
class Cat : public Animal { public: void speak() override { cout << "Meow\n"; } };
int main() {
Animal *p;
Dog d; Cat c;
p = &d; p->speak(); // Bark
p = &c; p->speak(); // Meow
}
- Without
virtual, both calls would print "Animal sound", because the compiler would bind to the pointer's type. - Tip: make the destructor virtual in a base class that has virtual functions.
2023 · Paper-I · Q4(b) · 6 marks
Q. Discuss late binding with a short program.
Ans.
- Late (dynamic) binding means the function to be called is decided at run time from the actual object type, not at compile time. It is achieved with virtual functions and base-class pointers.
- Early (static) binding is decided at compile time (normal and overloaded functions).
class Shape { public: virtual double area() { return 0; } };
class Square : public Shape {
double s;
public:
Square(double x) : s(x) {}
double area() override { return s * s; }
};
int main() {
Shape *p = new Square(4);
cout << p->area(); // 16, chosen at run time (late binding)
}
MCQs
2026 P-I · Q5. What is the purpose of using virtual functions in inheritance?
- (A) To prevent overriding
- (B) To hide members
- (C) To enable polymorphism ✅
- (D) None of these
- Answer: (C) To enable polymorphism.
- Why: a
virtualfunction makes the call through a base-class pointer resolve to the derived-class version at run time. To prevent overriding you would usefinal, and hiding members is the job ofprivate.