Understanding Virtual Functions in C++
A caller making a virtual call needs only two facts: the location of the pointer to the vtbl, and the index of each virtual function.
Why a Caller Cannot Simply Look Inside
Imagine a piece of code that receives an object and needs to ask it to do something, such as draw itself or report its area. The caller would like to write one request and have the right behavior happen, whatever the object turns out to be. The difficulty is that the caller may not know how big the object is or how its data members are arranged in memory. If the caller had to know those details for every possible kind of object, the code would be fragile and tied to each concrete class.
The virtual function table, usually called the vtbl, is the mechanism that removes this need. Its job is to let the object be used correctly even when the caller has no knowledge of the object's size or internal data layout. This article follows that mechanism step by step, so you can see what the caller actually does and what the object provides.
The Two Facts Behind Every Virtual Call
Picture the moment a virtual call happens. The caller is holding an object, but it does not look inside that object to find the code it wants. Instead, it relies on two pieces of information. First, it knows where the pointer to the vtbl is located, relative to the object's container. Second, it knows the index that corresponds to the particular virtual function it wants to run. Using the pointer, the caller reaches the table. Using the index, it selects one entry in that table. The entry leads to the function that the object's class provides.
A caller making a virtual call needs only two facts: the location of the pointer to the vtbl, and the index of each virtual function.
Notice what the caller never needs. It does not need the object's data members, their order, or the total size of the object. Those details belong to the class that defines the object. The caller only needs a fixed access pattern: follow the pointer, then pick the slot by index. The two facts are kept separate because that makes the mechanism easier to reason about. The next two parts examine each one in turn.
Tracing One Virtual Call Step by Step
Now follow a single call through the mechanism. The caller holds an object and asks for a virtual function. It follows the vtbl pointer stored in that object to reach the table. It then uses the function's index to select the matching entry. The entry leads to the function the object's class supplies, and that function runs. At no point did the caller open up the object to read its data members or measure its size.
Why the Extra Step Costs Only About 25 Percent
A natural worry is that following a pointer and then choosing an index must make every virtual call much slower than an ordinary call. The reference material addresses this directly. It says the virtual call mechanism can be made almost as efficient as the normal function call mechanism, within 25 percent. In other words, the extra work of going through the table is small enough that the difference is modest in practice.
This closeness is why virtual functions are a reasonable tool to use when the design calls for them. The extra cost is the pointer step plus the index step, and both are small compared with the benefit of letting one request produce the right behavior for many kinds of object.
Counting the Memory Cost
Using the vtbl is not free in terms of memory. The source gives a precise accounting. Each object of a class with virtual functions carries one pointer, and each such class has one vtbl. The table belongs to the class and is not repeated for every object. The pointer, however, is stored in each object. This split explains why the cost scales in a useful way.
Counting Pointers and Tables for a Small Program
A program creates 4 objects. They belong to 3 different classes, and every class has virtual functions. How many extra pointers and how many extra vtbls does the program carry?
- 1Count the pointers
Each object carries one vtbl pointer. Four objects give 4 pointers.
- 2Count the tables
Each class with virtual functions has one vtbl. Three classes give 3 vtbls.
- 3Notice what does not multiply
The tables do not depend on how many objects exist. Adding more objects of these classes adds pointers but no new tables.
4 extra pointers (one per object) and 3 extra vtbls (one per class).
What do you think happens?
If a program creates 10 objects that all belong to one class with virtual functions, how many vtbls exist for that class?
Reveal answer
1 vtbl, shared by the class. The program also carries 10 pointers, one stored in each object.
The table belongs to the class and is not repeated for every object. Only the pointer is stored in each object, so the table count depends on classes, not on objects.
Mistakes When Reasoning About Virtual Calls
Common mistake Thinking the vtbl is copied into every object
What it looks likeAssuming that three objects of one class each carry their own full function table.
Why it failsThe table belongs to the class and is not repeated for every object. Only the pointer is stored in each object.
Do this insteadCount one pointer per object and one vtbl per class with virtual functions.
Common mistake Assuming the caller must know the object's data members
What it looks likeBelieving that to call an area function, the caller must know where the object's width and height fields sit.
Why it failsThe caller needs only the location of the vtbl pointer and the index of the function. The data members belong to the class that defines the object.
Do this insteadFollow the pointer, then pick the slot by index.
Common mistake Assuming a virtual call is many times slower than a normal call
What it looks likeExpecting each virtual call to cost several times as much as a direct call, and avoiding virtual functions for that reason alone.
Why it failsThe 25 percent figure says the mechanism can be made almost as efficient as a normal call, within that margin.
Do this insteadTreat the extra cost as a small step through the table, and weigh it against the design benefit.
Common mistake Confusing the index of a function with its address
What it looks likeTreating the number 1 in a slot as if it were the function's memory location.
Why it failsThe index is a fixed slot number that selects an entry in the table. It is not the address the entry leads to.
Do this insteadThink of the index as a slot number, and the table entry as the thing that leads to the function.
Trace a Virtual Call Yourself
A program creates 4 objects. They belong to 3 different classes, and every class has virtual functions. Using one pointer per object and one vtbl per class, how many extra pointers and how many extra vtbls does the program carry? Then explain, in your own words, what the caller needs to know to call a virtual function on one of these objects.
Hints
- Count the objects for the pointers, and the classes for the tables.
- The caller needs exactly two facts: where the vtbl pointer is, and which index the function has.
Key Takeaways
- A vtbl lets a caller use an object correctly without knowing its size or data layout.
- A virtual call needs only two facts: the location of the vtbl pointer inside the object, and the index of the function in the table.
- The source puts the extra cost of a virtual call within about 25 percent of a normal call.
- Space overhead is one pointer in each object and one vtbl for each class with virtual functions, so the table does not grow with the object count.