http://www.dreamincode.net/forums/topic/45816-detail-about-how-vptr-and-virtual-table-works/
这是一个相当牛的帖子。
1. vptr每个对象都会有一个,而vtable是每个类有一个
2. vptr指向了vtable
3. 在C++中,如果一个类有虚函数,那么这个对象的memory layout中就有特个vptr,且在最前面
4. 一个类中就算有多个虚函数,也只有一个vptr
5. 做多重继承,就是继承了多个父类时,就会有多个vptr
6. 看最后一个例子,当子类继承了多个父类,且自己又新加了虚函数,这个vptr是借用了第一个父类的?
Assumption: machine is 32-bit .
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
12 | cout << "obj's Size = " << sizeof(obj) << endl; |
13 | cout << "obj 's Address = " << &obj << endl; |
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
08 | class dTest : public Test |
17 | cout << "obj1's Size = " << sizeof(obj1) << endl; |
18 | cout << "obj1's Address = " << &obj1 << endl; |
20 | cout << "obj2's Size = " << sizeof(obj2) << endl; |
21 | cout << "obj2's Address = " << &obj2 << endl; |
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.
07 | cout << "Test::fun1" << endl; |
14 | cout << "obj's Size = " << sizeof(obj) << endl; |
15 | cout << "obj's Address = " << &obj << endl; |
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
05 | virtual void fun1() { cout << "Test::fun1" << endl; } |
06 | virtual void fun2() { cout << "Test::fun2" << endl; } |
07 | virtual void fun3() { cout << "Test::fun3" << endl; } |
08 | virtual void fun4() { cout << "Test::fun4" << endl; } |
14 | cout << "obj's Size = " << sizeof(obj) << endl; |
15 | cout << "obj's Address = " << &obj << endl; |
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:
06 | Test(int temp1 = 0, int temp2 = 0) |
27 | int* pInt = (int*)&obj; |
31 | cout << "a = " << obj.getA() << endl; |
32 | cout << "b = " << obj.getB() << endl; |
OUTPUT:
a = 200
b = 10
If we Change the code as then
// 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:
05 | cout << "Test::fun1" << endl; |
12 | cout << "VPTR's Address " << (int*)(&obj+0) << endl; |
13 | cout << "VPTR's Value " << (int*)*(int*)(&obj+0) << endl; |
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:
08 | cout << "Test::fun1" << endl; |
11 | typedef void (*Fun)(void); |
16 | cout << "VPTR's Address " << (int*)(&obj+0) << endl; |
17 | cout << " VIRTUAL TABLE 's Address " << (int*)*(int*)(&obj+0) << endl; // Value of VPTR |
18 | cout << "Value at first entry of VIRTUAL TABLE " << (int*)*(int*)*(int*)(&obj+0) << endl; |
20 | Fun pFun = (Fun)*(int*)*(int*)(&obj+0); // calling Virtual function |
OUTPUT:
VPTR's Address 0012FF7C
VIRTUAL TABLE 's Address 0046C0EC
Value at first entry of VIRTUAL TABLE 0040100A
Test: fun1
Example 8:
03 | virtual void fun1() { cout << "Test::fun1" << endl; } |
04 | virtual void func1() { cout << "Test::func1" << endl; } |
11 | cout << "VPTR's Address " << (int*)(&obj+0) << endl; |
12 | cout << "VIRTUAL TABLE 's Address" << (int*)*(int*)(&obj+0) << endl; |
14 | // Calling Virtual table functions |
15 | cout << "Value at 1st entry of VTable " << (int*)*((int*)*(int*)(&obj+0)+0) << endl; |
16 | cout << "Value at 2nd entry of VTable " << (int*)*((int*)*(int*)(&obj+0)+1) << endl; |
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
03 | virtual void fun1() { cout << "Test::fun1" << endl; } |
04 | virtual void func1() { cout << "Test::func1" << endl; } |
07 | typedef void(*Fun)(void); |
14 | // calling 1st virtual function |
15 | pFun = (Fun)*((int*)*(int*)(&obj+0)+0); |
18 | // calling 2nd virtual function |
19 | pFun = (Fun)*((int*)*(int*)(&obj+0)+1); |
OUTPUT:
Test::fun1
Test::func1
Example 10: multiple Inheritance
19 | class Derive : public Base1, public Base2, public Base3 |
26 | cout << "Derive's Size = " << sizeof(obj) << endl; |
OUTPUT:
Derive's Size = 12
Example 11: Calling Virtual Functions in case of Multiple Inheritance
03 | virtual void fun1() { cout << "Base1::fun1()" << endl; } |
04 | virtual void func1() { cout << "Base1::func1()" << endl; } |
08 | virtual void fun1() { cout << "Base2::fun1()" << endl; } |
09 | virtual void func1() { cout << "Base2::func1()" << endl; } |
13 | virtual void fun1() { cout << "Base3::fun1()" << endl; } |
14 | virtual void func1() { cout << "Base3::func1()" << endl; } |
17 | class Derive : public Base1, public Base2, public Base3 |
22 | cout << "Derive::Fn" << endl; |
26 | cout << "Derive::Fnc" << endl; |
30 | typedef void(*Fun)(void); |
37 | // calling 1st virtual function of Base1 |
38 | pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+0); |
41 | // calling 2nd virtual function of Base1 |
42 | pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+1); |
45 | // calling 1st virtual function of Base2 |
46 | pFun = (Fun)*((int*)*(int*)((int*)&obj+1)+0); |
49 | // calling 2nd virtual function of Base2 |
50 | pFun = (Fun)*((int*)*(int*)((int*)&obj+1)+1); |
53 | // calling 1st virtual function of Base3 |
54 | pFun = (Fun)*((int*)*(int*)((int*)&obj+2)+0); |
57 | // calling 2nd virtual function of Base3 |
58 | pFun = (Fun)*((int*)*(int*)((int*)&obj+2)+1); |
61 | // calling 1st virtual function of Drive |
62 | pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+2); |
65 | // calling 2nd virtual function of Drive |
66 | pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+3); |
OUTPUT:
Base1::fun
Base1::func
Base2::fun
Base2::func
Base3::fun
Base3::func
Drive::Fn
Drive::Fnc
By
Asadullah Ansari