Early vs Late Binding using Virtual Table and VPtr

Early BindingLate Binding
Happens at compile time.Happens at run time.
Compiler has all information to invoke correct function version at compile time.Compiler doesn’t have information to identify correct function version.
Normal function calls are example of Early Binding.Virtual functions are example of Late Binding.
Functions invoked by object is example of early bindingFunctions invoked by pointers can be example of late binding.
Early binding is efficient as no cost paid at run-time for function resolution.Late Binding is slightly costly as function resolution happens at run time.
Early binding doesn’t provide flexibility of one method multiple interfaces.Late Binding provide flexibility.
Doesn’t have any virtual table or virtual pointer.It uses Virtual Table and Virtual Pointer.

Virtual Table and Virtual Pointer

Whenever a class has virtual function, a virtual table is created for that class by the compiler. This virtual table contains all the virtual function addresses of that class. A virtual pointer is also defined by the compiler to access this virtual table at run time to identify correct version of function to be called.

Let’s have a look at the below diagram to understand how virtual table and virtual pointer works.

Important Points

  • Whenever a virtual function is invoked by pointer or reference, virtual table is used to resolve the function address.
  • There is only one Virtual Table for a class and all the objects share the same virtual table.
  • Compiler adds a virtual pointer for all objects, which points to corresponding virtual table.
  • Object of a class having virtual functions will have more size than normal version of class object. This is because each object has one extra virtual pointer to point virtual table.
  • To find correct function address, virtual pointer uses virtual table as it contains function addresses of all virtual function defined by class

Let’s have a look at the sample program to understand it better.

#include <iostream>

using namespace std;
class BaseWithoutVirtual
{
	public:
		~BaseWithoutVirtual ()
		{
			cout << "Inside BaseWithoutVirtual Destructor." << endl;
		}

		void print ()
		{
			cout << "BaseWithoutVirtual version called." << endl;
		}

		void pure_virtual_fun ()
		{}
};

class Base
{
	public:
		virtual ~Base ()
		{
			cout << "Inside Base Destructor." << endl;
		}

		virtual void print ()
		{
			cout << "Base version called." << endl;
		}

		virtual void pure_virtual_fun () = 0;
};

class Derived:public Base
{
	public:
		virtual ~Derived ()
		{
			cout << "Inside Derived Destructor." << endl;
		}

		void print () /* virtual keyword not needed */
		{
			cout << "Derived version called." << endl;
		}

		void pure_virtual_fun ()
		{
			cout << "Derived class version for pure_virtual_fun" << endl;
		}
};

int main ()
{
	Base *p = new Derived ();
	p -> print ();
	p -> pure_virtual_fun ();
	delete p;
	/* Not allowed to create Base class object as it has pure virtual function. */
	/*
	p = new Base ();
   */

	cout << "Derived class object size: " << sizeof (Derived) << endl;
	cout << "Base Without Virtual class object size: " << sizeof (BaseWithoutVirtual) << endl;
}

Let’s analyze the output of above program.

Derived version called.
Derived class version for pure_virtual_fun
Inside Derived Destructor.
Inside Base Destructor.
Derived class object size: 8
Base Without Virtual class object size: 1

Leave a Reply

Your email address will not be published. Required fields are marked *