Calling a virtual function is slower than calling a non-virtual function for a couple of reasons: First, we have to use the *__vptr to get to the appropriate virtual table. Second, we have to index the virtual table to find the correct function to call. Only then can we call the function. As a result, we have to do 3 operations to find the function to call, as opposed to 2 operations for a normal indirect function call, or one operation for a direct function call. However, with modern computers, this added time is usually fairly insignificant.
Also as a reminder, any class that uses virtual functions has a __vptr, and thus each object of that class will be bigger by one pointer. Virtual functions are powerful, but they do have a performance cost.
https://www.learncpp.com/cpp-tutorial/125-the-virtual-table/
Because for virtual functions linking was not done at compile time. So, what happens when a call to virtual function is executed ,i.e.
Steps are as follows,
- vpointer hidden in first 4 bytes of the object will be fetched
- vTable of this class is accessed through the fetched vPointer
- Now from the vTable corresponding function’s address will be fetched
- Function will be executed from that function pointer
https://thispointer.com/how-virtual-functions-works-internally-using-vtable-and-vpointer/
A late-binding process involves the following activities:
1)Compiler adds a hidden vPtr member to the class, and generates one unique vtable for the class.
At compilation time, when compiler sees the definition of a class with virtual methods, it will build a virtual table (vtable) for the class, which is an array of function pointers to the implementations of all the virtual methods, and add a hidden data member vPtr to the class definition as the FIRST data member.
Now suppose the methods of classes in Fig. 1 (Hi, Hi1, Hi2, Hi3) are all virtual functions. The memory footprint of an object of class Derived becomes:
pDerived
pBase1 pBase2 pBase3
+----+-------------+-------+---------+-------+----------+----------+
|vptr| a1 | vptr2 | a2 | vptr3 | a3 | a |
+----+-------------+-------+---------+-------+----------+----------+
0 4 104 108 208 212 312 412
Fig. 2. Memory footprint of a polymorphic type object
As you can see in the memory footprint, if you use a Base2 pointer to receive a Derived object, for example, this pointer will point to memory offset 104 as pBase2 does.
Note that each Derive object will have its own memory footprint, with the same structure but in different memory locations. However, the vPtrs will all be pointing to the same method implementations, in other words, the vPtr2 of two instances will contain the same address.
The derived-class and the first base class shares the same vPtr, which points to their shared merged vtable (see following section “Inheritance of Base-class vPtrs” for details). The rest of the base classes have their own vPtrs.
Note that no matter how complicated the inheritance hierarchy is, a function pointer in the vtable always points to the latest/lowest implementation of the virtual function in the inheritance hierarchy.
2)Compiler generates code to do dynamic binding using the vtable.
At compilation time, when compiler sees a call to a virtual method thourgh a pointer (pBase2->Hi2( )), it knows that the address of the function is only known at run time, so it will not try to find the implementation of the function. Instead, it knows that the pointer (pBase2) will be pointing to a vPtr at run time. So it generates code to go through the vPtr to find the vtable (whose composition is already know from the type of the pointer), and go to a certain entry of that vtable, fatch that function pointer, and make the call.
3)At run time, when an object is created out of this class definition, its vPtr member will be assigned the address of the class’s vtable.
http://www.referencecode.org/2013/02/c-advanced-tutorial-vptr-and-vtable.html