#Previous question #Index of C++ Questions #Next question

C++ compiler creates a lot of abstraction while constructing this vfptr, vtable and also while calling a virtual function of a class. To understand how this virtual function works, we have illustrated each section below with examples and with C language.

In the below example we have an example of a base class called CShape. This is an abstract base class. An abstract base class or ABC is a class which contains at least one pure virtual function.

class CShape
{
public:
  CShape();
  virtual void Draw(void) = 0;
  
}; 

Now we have derived two new shapes called CCircle and CRectangle from CShape.

class CCircle : public CShape
{
protected:
  int x, y, r;
public:
  CCircle(int x, int y, int r);
  virtual void Draw(void);
  
};
class CRectangle : public CShape
{
protected:
  int x, y, w, h;
public:
  CRectangle(int x, int y, int w, int h);
  virtual void Draw(void);
  
};

In object oriented environment we always use an abstract base class pointer to point the derived object and call base function and the corresponding derived class function get called. This is why we use virtual function, with base pointer we call the virtual function and the derived class function gets called.

/* ------ CShape ----- */
CShape::Construct()
{
  printf("Constructing an abstract shape\n");
}
/*-------------------*/
/* CCircle */
CCircle::CCircle(int x, int y, int r)
{
  this->x = x;
  this->y = y;
  this->r = r;
  printf("Constructing CCircle x = %d, y = %d, r = %d\n",
         this->x,
         this->y,
         this->r);
}
void CCircle::Draw(void)
{
  printf("Drawing CCircle x = %d, y = %d, r = %d\n",
         this->x,
         this->y,
         this->r);
}
/*-------------------*/
/* ------ CRectangle ----*/
CRectangle::CRectangle(int x, int y, int w, int h)
{
  this->x = x;
  this->y = y;
  this->w = w;
  this->h = h;
  printf("Constructing Rectangle x = %d, y = %d, w = %d, h = %d\n",
         this->x,
		 this->y,
		 this->w,
		 this->h);
}
void CRectangle::Draw(void)
{
  printf("Drawing Rectangle x = %d, y = %d, w = %d, h = %d\n",
         this->x,
		 this->y,
		 this->w,
		 this->h);
}
/*-------------------*/
  CShape *shapes[2];
  shapes[0] = new CCircle(x1, y1, r);
  shapes[1] = new CRectangle(x2, y2, w, h);
  //Now we call Draw() for both the shapes 
  for(i = 0; i < 2; i++)
  {
     shapes[i]->Draw();
  }
Result:
Constructing an abstract shape
Constructing CCircle x = 10, y = 20, r = 100
Constructing an abstract shape
Constructing Rectangle x = 100, y = 100, w = 50, h = 40
Drawing CCircle x = 10, y = 20, r = 100
Drawing Rectangle x = 100, y = 100, w = 50, h = 40

The result is that, with base class pointer, without knowing the type of shapes, we called the Draw()[inside for loop] and with a magic it called the corresponding Draw() for each derived classes, like for Circle it called Draw() of CCircle and for rectangle it called Draw() of CRectangle, although for each time pointer is of CShape(base).

Now the point is, how it knows which function to call? Also how base pointer is related to the derived objectís function address?

To elaborate this I want to use C language and some code using structure. Suppose we have a structure CShape. How I can relate the Draw() function to this structure?

The answer is simple, taking a function pointer inside the structure.

struct CShape
{
  void (*Draw)(void);
};

The problem with this approach is, each time I will add a new virtual function the size of the structure is going to increase by sizeof pointer. Like below if I add three virtual functions I am going to increase the size of structure by sizeof pointer x 3 thus each object of this type will consume this much of memory.

struct CShape
{
  void (*Draw)(void);
  void (*Draw1)(void);
  void (*Draw2)(void);
  ...
};

The solution is we can take one and only one pointer inside the structure to point to an array/table of function pointers. If we add more and more virtual functions, the size of the structure or object is not going to increase as it only holds the pointer to the table. But the size of the function pointer table will grow. This is acceptable as we increase the number of virtual function the table which records the location of the functions will surely going to increase. Now we have our picture ready as follows.

struct CShape
{
  struct CShape_vtable *vfptr;
};

This vfptr is called pointer to vtable. This is the first hidden member of any C++ class which has one or more virtual function.

struct CShape_vtable
{
  void (*Draw)(void);
  void (*Draw1)(void);
  void (*Draw2)(void);
  ...
};

vfptr points to a list/table or structure of function pointers also called vtable. This list/table is a built-in feature of C++ compiler. It holds the address of virtual functions for a particular class. Each class has its own vtable and vtable assignment is done by compiler after constructor is called and vtable gets de-assigned before destructor is called.

Now we will have our CShape, CCircle, CRectangle C++ code rewritten in C. This code is very elaborate and there has been given all the steps which compiler does for object construction. This will give a clear understanding.

/* ------ CShape ----- */
struct CShape_vtable;
struct CShape
{
  struct CShape_vtable *vfptr;
};
struct CShape_vtable
{
  void (*Draw)(void);
};
void CShape_Construct(struct CShape *_this)
{
  printf("Constructing an abstract shape\n");
}
/*-------------------*/
/* CCircle */
struct CCircle_vtable;
struct CCircle
{
  struct CCircle_vtable *vfptr;
  int x, y, r;
};
struct CCircle_vtable
{
  void (*Draw)(void);
};
void CCircle_Construct(struct CCircle *_this, int x, int y, int r)
{
  _this->x = x;
  _this->y = y;
  _this->r = r;
  printf("Constructing CCircle x = %d, y = %d, r = %d\n",
         _this->x,
		 _this->y,
		 _this->r);
}
void CCircle_Draw(struct CCircle *_this)
{
  printf("Drawing CCircle x = %d, y = %d, r = %d\n",
         _this->x,
		 _this->y,
		 _this->r);
}
/*-------------------*/
/* ------ CRectangle ----*/
struct CRectangle_vtable;
struct CRectangle
{
  struct CRectangle_vtable *vfptr;
  int x, y, w, h;
};
struct CRectangle_vtable
{
  void (*Draw)(void);
};
void CRectangle_Construct(struct CRectangle *_this,
                          int x, int y, int w, int h)
{
  _this->x = x;
  _this->y = y;
  _this->w = w;
  _this->h = h;
  printf("Constructing Rectangle x = %d, y = %d, w = %d, h = %d\n",
         _this->x,
		 _this->y,
		 _this->w,
		 _this->h);
}
void CRectangle_Draw(struct CRectangle *_this)
{
  printf("Drawing Rectangle x = %d, y = %d, w = %d, h = %d\n",
         _this->x,
		 _this->y,
		 _this->w,
		 _this->h);
}
/*-------------------*/
  ///Main code, C++ code has been commented. 
  struct CShape *shapes[2];
  //v-table construction, Built-in compiler hidden code
  struct CShape_vtable csvtable;
  struct CShape_vtable ccvtable;
  struct CRectangle_vtable crvtable;
  csvtable.Draw = 0;//Pure virtual function
  ccvtable.Draw = &CCircle_Draw;
  crvtable.Draw = &CRectangle_Draw;
  //C++ Code //shapes[0] = new CCircle(x1, y1, r);
  //Memory allocation
  shapes[0] = (struct CShape *)malloc(sizeof(struct CCircle)); 
  CShape_Construct(shapes[0]); // CShape Base Constructor call
  shapes[0]->vfptr = &csvtable;  //CShape v-table assignment
  CCircle_Construct(shapes[0],
                     x1, y1, r); //CCircle Derived Constructor call
  shapes[0]->vfptr = &ccvtable;  // CCircle v-table assignment
  //C++ Code ////shapes[1] = new CRectangle(x2, y2, w, h);
  //Memory allocation
  shapes[1] = (struct CShape *)malloc(sizeof(struct CRectangle)); 
  CShape_Construct(shapes[1]); //CShape Base Constructor call
  shapes[1]->vfptr = &csvtable;  // CShape v-table assignment
  CRectangle_Construct(shapes[1], 
                       x2, y2, w, h); // CRectangle Derived Constructor call
  shapes[1]->vfptr = &crvtable;  // CRectangle v-table assignment
  //Now we call Draw() for both the shapes 
  for(i = 0; i < 2; i++)
  {
     shapes[i]->vfptr->Draw(shapes[i]);
  }

See here the call of the virtual function Draw(). Compiler calls the function pointer from the vtable through vfptr which is inside the object. Compiler calls a virtual function in three steps and they are -

  1. Get the vfptr,
  2. Goto the vtable and take the proper function pointer from the offset,/LI>
  3. Call the function using the pointer./LI>
Note: These are compiled and tested in VC++ 6.0.

You have viewed 1 page out of 62. Your C++ learning is 0.00% complete. Login to check your learning progress.

 Vote 0

Similar topics related to this section

object-oriented, C vs C++, C++ vs Java, encapsulation, constructor, overloaded constructor, destructor, destructor overloaded, copy constructor, copy constructor, deep copy, deep copy vs shallow copy, singleton, default access modifier, default access modifier, default access modifier, abstraction, THIS pointer, static function, THIS pointer, a static function, scope resolution operator, inline function, private vs protected, const function, polymorphism, operator overloading, function overloading, overriding, prefix vs postfix, friend class, friend function, sizeof class with virtual, vfptr and vftable, vfptr and vftable using C, early binding and late binding, inheritance, virtual function call from a constructor/destructor, virtual destructor, virtual base class, virtual base class, exception, stack unwinding, exception handling, try-catch block, namespace, mutable variable and const function, mutable keyword, explicit, access a member function, object slicing, ctor sequence of constructor, dtor sequence of destructor, virtual destructor, print the type, dynamic casting, static casting, dynamic and static casting, const casting, reinterpret casting, static and reinterpret casting, template class, template vs macro,

#Object-Oriented Programming in C++ (4th Edition)
#Programming: Principles and Practice Using C++ (2nd Edition)
#The C++ Programming Language, 4th Edition
#Professional C++ Paperback by Nicholas A.Solter, Scott J.Kleper
#Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14