Monday, 2 March 2015

The VTABLE in C++ with examples

The VTABLE in C++ with examples:

The VTABLE in C++ is having different names. We can call it as virtual method table, virtual function table, virtual call table, Dispatch Table or vtable. The VTABLE is used to support the runtime method binding or dynamic dispatch.

The virtual table is a lookup table of functions used to resolve function calls in a dynamic/late binding manner.

When the compiler founds that, there is the virtual function in the class it implements the hidden pointer that points to the VTABLE. So the VTABLE contains the array of pointers to the virtual functions, the actual function will call at the run time through these pointers.

The virtual table is created at the compile time and the table is created for every class that contains virtual function or the class that is derived from the class that uses the virtual functions. A virtual table contains one entry for each virtual function that can be called by objects of the class. Each entry in this table is simply a function pointer that points to the most-derived function accessible by that class.

Virtual Pointer:
The VPTR is the pointer that is created by the compiler to access the vtable. Initially it is created for the MyBase calss. And then at run time when the instance is created that points to the vtable of the required calss.


The VPTR is a real pointer, which is inherited by the derived class.

Let us look a sample code to understand better than reading theory.

#include "stdafx.h"
#include <iostream>
#include <stdio.h>
using namespace std;

class MyBase
{
public:
    virtual void Add ()
      { cout << "I am in the Base Add Function" << endl; };
    virtual void Sub ()
      { cout << "I am in the Base Sub Function" << endl; };
};
class Derived1: public MyBase
{
public:
    virtual void Add ()
      { cout << "I am in the Derived 1 Add Function" << endl; };
};
class Derived2: public MyBase
{
public:
    virtual void Sub ()
      { cout << "I am in the Derived 2 Sub Function" << endl; };
};
int main ()
{
  MyBase *pBase = NULL;
  pBase = new Derived1 ();
  MyBase *pBase1 = NULL;
  pBase1 = new Derived2 ();
  pBase->Add ();
  pBase->Sub ();
  pBase1->Add ();
  pBase1->Sub ();
   getchar ();
   return 0;
}


Because there are 3 classes here, the compiler will set up 3 virtual tables: one for MyBase, one for Derived1, and one for Derived2.

The compiler also adds a hidden pointer to the most MyBase class that uses virtual functions.
When a class object is created, *__VPTR is set to point to the virtual table for that class. For example, when a object of type MyBase is created, *__VPTR is set to point to the virtual table for Base. When objects of type Derived1or Derived2 are constructed, *__VPTR is set to point to the virtual table for Derived1 or Derived2respectively.

Here there are two virtual functions in the base class. The Derived1 and the Derived 2 classes are inherited from the MyBase class. The vtables are created for each class and each virtual table contains Add () and Sub () function entries.

Structure of the Virtual tables from base to Derived classes:
The MyBase VTable:

The vtable for the MyBase class is very simple.The MyBase class has two virtual functions Add & Sub, So the entry for the Add is points to MyBase::Add () and similarly fior Sub MyBase::Sub ();

Derived1’s virtual table is slightly more complex. An object of type Derived1 can access members of both Derived1 and MyBase. However, Derived1 has overridden Add(), making Derived1::Add() more derived than MyBase::Add(). Consequently, the entry for Add points to Derived1::Add(). Derived1 hasn’t overridden Sub(), so the entry for Sub will point to MyBase::Sub().

Derived2’s virtual table is similar to Derived1, except the entry for Add points to MyBase::Add(), and the entry for Sub points to Derived2::Sub().
MyBase VTable
 Although this diagram is kind of crazy looking, it’s really quite simple: the *__VPTR in each class points to the virtual table for that class. The entries in the virtual table point to the most-derived version of the function objects of that class are allowed to call.

Let us see each one seperatly, create the instance of the Derived 1 object

int main ()
{
  Derived1 *pDerived = new Derived1 ();
   getchar ();
   return 0;
} 

Because pDerived is a Derived1 object, pDerived has it’s *__VPTR set to the Derived1 virtual table.
Now, set a MyBase pointer to Derived1:
int main ()
{
    Derived1 pDerived;
    MyBase *pBase = &pDerived;
   getchar ();
   return 0;
}

Note that because pBase is a MyBase pointer, it only points to the MyBase portion of cClass. However, also note that *__VPTR is in the MyBase portion of the class, so pBase has access to this pointer. Finally, note that pBase->__VPTR points to the Derived1 virtual table! Consequently, even though pBase is of type MyBase, it still has access to Derived1’s virtual table.

So what happens when we try to call pBase->Add()?
#include "stdafx.h"
#include <iostream>
#include <stdio.h>
using namespace std;
class MyBase
{
public:
    virtual void Add ()
      { cout << "I am in the Base Add Function" << endl; };
    virtual void Sub ()
      { cout << "I am in the Base Sub Function" << endl; };
};
class Derived1: public MyBase
{
public:
    virtual void Add ()
      { cout << "I am in the Derived 1 Add Function" << endl; };
};
class Derived2: public MyBase
{
public:
    virtual void Sub ()
      { cout << "I am in the Derived 2 Sub Function" << endl; };
};
int main ()
{
    Derived1 pDerived;
    MyBase *pBase = &pDerived;
    pBase->Add ();
   getchar ();
   return 0;
}

First, the program recognizes that Add() is a virtual function. Second, uses pBase->__VPTR to get to Derived1’s virtual table. Third, it looks up which version of Add() to call in Derived1’s virtual table. This has been set to Derived1::Add(). Therefore, pBase->Add() resolves to Derived1::Add()!

Now, you might be saying, “But what if MyBase really pointed to a MyBase object instead of a Derived1 object. Would it still call Derived1::Add()?”. The answer is no.
int main ()
{
  MyBase *pBase = new MyBase;
    pBase->Add ();
   getchar ();
   return 0;
}

In this case, when cClass is created, __VPTR points to MyBase’s virtual table, not Derived1’s virtual table. Consequently, pBase->__VPTR will also be pointing to MyBase’s virtual table. MyBase’s virtual table entry for Add() points to MyBase::Add(). Thus, pBase->Add() resolves to MyBase::Add(), which is the most-derived version of Add() that a MyBase object should be able to call.

By using these tables, the compiler and program are able to ensure function calls resolve to the appropriate virtual function, even if you’re only using a pointer or reference to a MyBase class

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.

Understanding of virtual function by reading is bit confusing, try it yourself, you will become master in that

See the more about virtual destructors .

No comments: