Sunday, December 9, 2012

vtable and vptr in C++...


C++ compiler creates a hidden class member called virtual-pointer or in short vptr when there are one or more virtual functions. This vptr is a pointer that points to a table of function pointers. This table is also created by compiler and called virtual function table or vtable. Each row of the vtable is a function pointer pointing to a corresponding virtual function.
To accomplish late binding, the compiler creates this vtable table for each class that contains virtual functions and for the class derived from it. The compiler places the addresses of the virtual functions for that particular class in ‘vtable’.
When virtual function call is made through a base-class pointer, the compiler quietly inserts code to fetch the VPTR and look up the function address in the VTABLE, thus calling the right function and causing late/dynamic binding to take place.
class base 
{
  virtual void funct1(void);
  virtual void funct2(void);
};
base b;
b.vptr = address of b.vtable
b.vtable[0]= &base::funct1()
b.vtable[1]= &base::funct2()
VPTRVTABLEFUNCTION
b.vptr ->Vtable[0] ->base::funct1()
Vtable[1] ->base::funct2()

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
#include<iostream.h>

class Base 
 { 
 public
    virtual void function1() {cout<<"Base :: function1()\n";}
    virtual void function2() {cout<<"Base :: function2()\n";}
    virtual ~Base(){};
}
   
class D1: public Base 
{ 
public
   ~D1(){};
   virtual void function1() { cout<<"D1 :: function1()\n";};
}
 
class D2: public Base 
{ 
public
   ~D2(){};
   virtual void function2() { cout<< "D2 :: function2\n";}
}

int main()
{
  D1 *d = new D1;;
  Base *b = d;

  b->function1();
  b->function2();

  delete (b);
 
  return (0);
}

output:
D1 :: function1()
Base :: function2()


:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Detail about How VPTR and Virtual table works

Assumption: 32-bit Machine.
Here I am going to explain How Virtual table, Virtual pointer for Virtual functions are internally working.
First we have understand memory layout.
Example 1: How the class's memory layout
Code: cpp
  1. class Test
  2. {
  3. public:
  4. int data1;
  5. int data2;
  6. int fun1();
  7. };
  8. int main()
  9. {
  10. Test obj;
  11. cout << "obj's Size = " << sizeof(obj) << endl;
  12. cout << "obj 's Address = " << &obj << endl;
  13. return 0;
  14. }

OUTPUT:
Sobj's Size = 8
obj 's Address = 0012FF7C
Note: Any Plane member function does not take any memory.
Example 2: Memory Layout of Derived class
Code: cpp
  1. class Test
  2. {
  3. public:
  4. int a;
  5. int b;
  6. };
  7. class dTest : public Test
  8. {
  9. public:
  10. int c;
  11. };
  12. int main()
  13. {
  14. Test obj1;
  15. cout << "obj1's Size = " << sizeof(obj1) << endl;
  16. cout << "obj1's Address = " << &obj1 << endl;
  17. dTest obj2;
  18. cout << "obj2's Size = "<< sizeof(obj2) << endl;
  19. cout << "obj2's Address = "<< &obj2 << endl;
  20. return 0;
  21. }
OUTPUT:
obj1's Size = 8
obj1's Address = 0012FF78
obj2's Size = 12
obj2's Address = 0012FF6C
Example 3: Memory layout If we have one virtual function.
Code: cpp
  1. class Test
  2. {
  3. public:
  4. int data;
  5. virtual void fun1()
  6. {
  7. cout << "Test::fun1" << endl;
  8. }
  9. };
  10. int main()
  11. {
  12. Test obj;
  13. cout << "obj's Size = " << sizeof(obj) << endl;
  14. cout << "obj's Address = " << &obj << endl;
  15. return 0;
  16. }
OUTPUT:
obj's Size = 8
obj's Address = 0012FF7C
Note: Adding one virtual function in a class takes 4 Byte extra.
Example 4: More than one Virtual function
Code: cpp
  1. class Test
  2. {
  3. public:
  4. int data;
  5. virtual void fun1() { cout << "Test::fun1" << endl; }
  6. virtual void fun2() { cout << "Test::fun2" << endl; }
  7. virtual void fun3() { cout << "Test::fun3" << endl; }
  8. virtual void fun4() { cout << "Test::fun4" << endl; }
  9. };
  10. int main()
  11. {
  12. Test obj;
  13. cout << "obj's Size = " << sizeof(obj) << endl;
  14. cout << "obj's Address = " << &obj << endl;
  15. return 0;
  16. }
OUTPUT:
obj's Size = 8
obj's Address = 0012FF7C
Note: Adding more virtual functions in a class, no extra size taking i.e. Only one machine size taking(i.e. 4 byte)
Example 5:
Code: cpp
  1. class Test
  2. {
  3. public:
  4. int a;
  5. int b;
  6. Test(int temp1 = 0, int temp2 = 0)
  7. {
  8. a=temp1 ;
  9. b=temp2 ;
  10. }
  11. int getA()
  12. {
  13. return a;
  14. }
  15. int getB()
  16. {
  17. return b;
  18. }
  19. virtual ~Test();
  20. };
  21. int main()
  22. {
  23. Test obj(5, 10);
  24. // Changing a and b
  25. int* pInt = (int*)&obj;
  26. *(pInt+0) = 100;
  27. *(pInt+1) = 200;
  28. cout << "a = " << obj.getA() << endl;
  29. cout << "b = " << obj.getB() << endl;
  30. return 0;
  31. }
OUTPUT:
a = 200
b = 10
If we Change the code as then
Code: Cpp

// Changing a and b
int* pInt = (int*)&obj;
*(pInt+1) = 100; // In place of 0
*(pInt+2) = 200; // In place of 1
OUTPUT:
a = 100
b = 200
Note: Who sits 1st place of Class : Answer is VPTR
VPTR - 1st placed in class and rest sits after it.
Example 6:
Code: cpp
  1. class Test
  2. {
  3. virtual void fun1()
  4. {
  5. cout << "Test::fun1" << endl;
  6. }
  7. };
  8. int main()
  9. {
  10. Test obj;
  11. cout << "VPTR's Address " << (int*)(&obj+0) << endl;
  12. cout << "VPTR's Value " << (int*)*(int*)(&obj+0) << endl;
  13. return 0;
  14. }
OUTPUT:
VPTR's Address 0012FF7C
VPTR's Value 0046C060
NOTE: This VPTR's value is a address of Virtual table. Lets see in next Example.
Example 7:
Code: cpp
  1. class Test
  2. {
  3. virtual void fun1()
  4. {
  5. cout << "Test::fun1" << endl;
  6. }
  7. };
  8. typedef void (*Fun)(void);
  9. int main()
  10. {
  11. Test obj;
  12. cout << "VPTR's Address " << (int*)(&obj+0) << endl;
  13. cout << " VIRTUAL TABLE 's Address " << (int*)*(int*)(&obj+0) << endl; // Value of VPTR
  14. cout << "Value at first entry of VIRTUAL TABLE " << (int*)*(int*)*(int*)(&obj+0) << endl;
  15. Fun pFun = (Fun)*(int*)*(int*)(&obj+0); // calling Virtual function
  16. pFun();
  17. return 0;
  18. }
OUTPUT:
VPTR's Address 0012FF7C
VIRTUAL TABLE 's Address 0046C0EC
Value at first entry of VIRTUAL TABLE 0040100A
Test: fun1

Example 8:
Code: cpp
  1. class Test
  2. {
  3. virtual void fun1() { cout << "Test::fun1" << endl; }
  4. virtual void func1() { cout << "Test::func1" << endl; }
  5. };
  6. int main()
  7. {
  8. Test obj;
  9. cout << "VPTR's Address " << (int*)(&obj+0) << endl;
  10. cout << "VIRTUAL TABLE 's Address"<< (int*)*(int*)(&obj+0) << endl;
  11. // Calling Virtual table functions
  12. cout << "Value at 1st entry of VTable " << (int*)*((int*)*(int*)(&obj+0)+0) << endl;
  13. cout << "Value at 2nd entry of VTable " << (int*)*((int*)*(int*)(&obj+0)+1) << endl;
  14. return 0;
  15. }

OUTPUT:
VPTR's Address 0012FF7C
VIRTUAL TABLE 's Address 0046C0EC
Value at first entry of VIRTUAL TABLE 0040100A
Value at 2nd entry of VIRTUAL TABLE 004012

Example :9
Code: cpp
  1. class Test
  2. {
  3. virtual void fun1() { cout << "Test::fun1" << endl; }
  4. virtual void func1() { cout << "Test::func1" << endl; }
  5. };
  6. typedef void(*Fun)(void);
  7. int main()
  8. {
  9. Test obj;
  10. Fun pFun = NULL;
  11. // calling 1st virtual function
  12. pFun = (Fun)*((int*)*(int*)(&obj+0)+0);
  13. pFun();
  14. // calling 2nd virtual function
  15. pFun = (Fun)*((int*)*(int*)(&obj+0)+1);
  16. pFun();
  17. return 0;
  18. }
OUTPUT:
Test::fun1
Test::func1
Example 10: multiple Inheritance
Code: cpp
  1. class Base1
  2. {
  3. public:
  4. virtual void fun();
  5. };
  6. class Base2
  7. {
  8. public:
  9. virtual void fun();
  10. };
  11. class Base3
  12. {
  13. public:
  14. virtual void fun();
  15. };
  16. class Derive : public Base1, public Base2, public Base3
  17. {
  18. };
  19. int main()
  20. {
  21. Derive obj;
  22. cout << "Derive's Size = " << sizeof(obj) << endl;
  23. return 0;
  24. }
OUTPUT:
Derive's Size = 12

Example 11: Calling Virtual Functions in case of Multiple Inheritance
Code: cpp
  1. class Base1
  2. {
  3. virtual void fun1() { cout << "Base1::fun1()" << endl; }
  4. virtual void func1() { cout << "Base1::func1()" << endl; }
  5. };
  6. class Base2 {
  7. virtual void fun1() { cout << "Base2::fun1()" << endl; }
  8. virtual void func1() { cout << "Base2::func1()" << endl; }
  9. };
  10. class Base3 {
  11. virtual void fun1() { cout << "Base3::fun1()" << endl; }
  12. virtual void func1() { cout << "Base3::func1()" << endl; }
  13. };
  14. class Derive : public Base1, public Base2, public Base3
  15. {
  16. public:
  17. virtual void Fn()
  18. {
  19. cout << "Derive::Fn" << endl;
  20. }
  21. virtual void Fnc()
  22. {
  23. cout << "Derive::Fnc" << endl;
  24. }
  25. };
  26. typedef void(*Fun)(void);
  27. int main()
  28. {
  29. Derive obj;
  30. Fun pFun = NULL;
  31. // calling 1st virtual function of Base1
  32. pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+0);
  33. pFun();
  34. // calling 2nd virtual function of Base1
  35. pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+1);
  36. pFun();
  37. // calling 1st virtual function of Base2
  38. pFun = (Fun)*((int*)*(int*)((int*)&obj+1)+0);
  39. pFun();
  40. // calling 2nd virtual function of Base2
  41. pFun = (Fun)*((int*)*(int*)((int*)&obj+1)+1);
  42. pFun();
  43. // calling 1st virtual function of Base3
  44. pFun = (Fun)*((int*)*(int*)((int*)&obj+2)+0);
  45. pFun();
  46. // calling 2nd virtual function of Base3
  47. pFun = (Fun)*((int*)*(int*)((int*)&obj+2)+1);
  48. pFun();
  49. // calling 1st virtual function of Drive
  50. pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+2);
  51. pFun();
  52. // calling 2nd virtual function of Drive
  53. pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+3);
  54. pFun();
  55. return 0;
  56. }

OUTPUT:
Base1::fun
Base1::func
Base2::fun
Base2::func
Base3::fun
Base3::func
Drive::Fn
Drive::Fnc

No comments:

Post a Comment

Creating mirror of BST