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
01 |
class Test |
02 |
{ |
03 |
public: |
04 |
int data1; |
05 |
int data2; |
06 |
int fun1(); |
07 |
}; |
08 |
09 |
int main() |
10 |
{ |
11 |
Test obj; |
12 |
cout << "obj's Size = " << sizeof(obj) << endl; |
13 |
cout << "obj 's Address = " << &obj << endl; |
14 |
return 0; |
15 |
} |
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
01 |
class Test |
02 |
{ |
03 |
public: |
04 |
int a; |
05 |
int b; |
06 |
}; |
07 |
08 |
class dTest : public Test |
09 |
{ |
10 |
public: |
11 |
int c; |
12 |
}; |
13 |
14 |
int main() |
15 |
{ |
16 |
Test obj1; |
17 |
cout << "obj1's Size = " << sizeof(obj1) << endl; |
18 |
cout << "obj1's Address = " << &obj1 << endl; |
19 |
dTest obj2; |
20 |
cout << "obj2's Size = " << sizeof(obj2) << endl; |
21 |
cout << "obj2's Address = " << &obj2 << endl; |
22 |
return 0; |
23 |
} |
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.
01 |
class Test |
02 |
{ |
03 |
public: |
04 |
int data; |
05 |
virtual void fun1() |
06 |
{ |
07 |
cout << "Test::fun1" << endl; |
08 |
} |
09 |
}; |
10 |
11 |
int main() |
12 |
{ |
13 |
Test obj; |
14 |
cout << "obj's Size = " << sizeof(obj) << endl; |
15 |
cout << "obj's Address = " << &obj << endl; |
16 |
return 0; |
17 |
} |
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
01 |
class Test |
02 |
{ |
03 |
public: |
04 |
int data; |
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; } |
09 |
}; |
10 |
11 |
int main() |
12 |
{ |
13 |
Test obj; |
14 |
cout << "obj's Size = " << sizeof(obj) << endl; |
15 |
cout << "obj's Address = " << &obj << endl; |
16 |
return 0; |
17 |
} |
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:
01 |
class Test |
02 |
{ |
03 |
public: |
04 |
int a; |
05 |
int b; |
06 |
Test(int temp1 = 0, int temp2 = 0) |
07 |
{ |
08 |
a=temp1; |
09 |
b=temp2; |
10 |
} |
11 |
int getA() |
12 |
{ |
13 |
return a; |
14 |
} |
15 |
int getB() |
16 |
{ |
17 |
return b; |
18 |
} |
19 |
virtual ~Test(); |
20 |
}; |
21 |
22 |
int main() |
23 |
{ |
24 |
Test obj(5, 10); |
25 |
26 |
// Changing a and b |
27 |
int* pInt = (int*)&obj; |
28 |
*(pInt+0) = 100; |
29 |
*(pInt+1) = 200; |
30 |
31 |
cout << "a = " << obj.getA() << endl; |
32 |
cout << "b = " << obj.getB() << endl; |
33 |
return 0; |
34 |
} |
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:
01 |
class Test |
02 |
{ |
03 |
virtual void fun1() |
04 |
{ |
05 |
cout << "Test::fun1" << endl; |
06 |
} |
07 |
}; |
08 |
09 |
int main() |
10 |
{ |
11 |
Test obj; |
12 |
cout << "VPTR's Address " << (int*)(&obj+0) << endl; |
13 |
cout << "VPTR's Value " << (int*)*(int*)(&obj+0) << endl; |
14 |
return 0; |
15 |
} |
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:
01 |
#include <iostream> |
02 |
using namespace std; |
03 |
04 |
class Test |
05 |
{ |
06 |
virtual void fun1() |
07 |
{ |
08 |
cout << "Test::fun1" << endl; |
09 |
} |
10 |
}; |
11 |
typedef void (*Fun)(void); |
12 |
13 |
int main() |
14 |
{ |
15 |
Test obj; |
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; |
19 |
|
20 |
Fun pFun = (Fun)*(int*)*(int*)(&obj+0); // calling Virtual function |
21 |
pFun(); |
22 |
return 0; |
23 |
} |
OUTPUT:
VPTR's Address 0012FF7C
VIRTUAL TABLE 's Address 0046C0EC
Value at first entry of VIRTUAL TABLE 0040100A
Test: fun1
Example 8:
01 |
class Test |
02 |
{ |
03 |
virtual void fun1() { cout << "Test::fun1" << endl; } |
04 |
virtual void func1() { cout << "Test::func1" << endl; } |
05 |
}; |
06 |
07 |
int main() |
08 |
{ |
09 |
Test obj; |
10 |
11 |
cout << "VPTR's Address " << (int*)(&obj+0) << endl; |
12 |
cout << "VIRTUAL TABLE 's Address" << (int*)*(int*)(&obj+0) << endl; |
13 |
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; |
17 |
18 |
return 0; |
19 |
} |
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
01 |
class Test |
02 |
{ |
03 |
virtual void fun1() { cout << "Test::fun1" << endl; } |
04 |
virtual void func1() { cout << "Test::func1" << endl; } |
05 |
}; |
06 |
07 |
typedef void(*Fun)(void); |
08 |
09 |
int main() |
10 |
{ |
11 |
Test obj; |
12 |
Fun pFun = NULL; |
13 |
|
14 |
// calling 1st virtual function |
15 |
pFun = (Fun)*((int*)*(int*)(&obj+0)+0); |
16 |
pFun(); |
17 |
18 |
// calling 2nd virtual function |
19 |
pFun = (Fun)*((int*)*(int*)(&obj+0)+1); |
20 |
pFun(); |
21 |
22 |
return 0; |
23 |
} |
OUTPUT:
Test::fun1
Test::func1
Example 10: multiple Inheritance
01 |
class Base1 |
02 |
{ |
03 |
public: |
04 |
virtual void fun(); |
05 |
}; |
06 |
07 |
class Base2 |
08 |
{ |
09 |
public: |
10 |
virtual void fun(); |
11 |
}; |
12 |
13 |
class Base3 |
14 |
{ |
15 |
public: |
16 |
virtual void fun(); |
17 |
}; |
18 |
19 |
class Derive : public Base1, public Base2, public Base3 |
20 |
{ |
21 |
}; |
22 |
23 |
int main() |
24 |
{ |
25 |
Derive obj; |
26 |
cout << "Derive's Size = " << sizeof(obj) << endl; |
27 |
return 0; |
28 |
} |
OUTPUT:
Derive's Size = 12
Example 11: Calling Virtual Functions in case of Multiple Inheritance
01 |
class Base1 |
02 |
{ |
03 |
virtual void fun1() { cout << "Base1::fun1()" << endl; } |
04 |
virtual void func1() { cout << "Base1::func1()" << endl; } |
05 |
}; |
06 |
07 |
class Base2 { |
08 |
virtual void fun1() { cout << "Base2::fun1()" << endl; } |
09 |
virtual void func1() { cout << "Base2::func1()" << endl; } |
10 |
}; |
11 |
12 |
class Base3 { |
13 |
virtual void fun1() { cout << "Base3::fun1()" << endl; } |
14 |
virtual void func1() { cout << "Base3::func1()" << endl; } |
15 |
}; |
16 |
17 |
class Derive : public Base1, public Base2, public Base3 |
18 |
{ |
19 |
public: |
20 |
virtual void Fn() |
21 |
{ |
22 |
cout << "Derive::Fn" << endl; |
23 |
} |
24 |
virtual void Fnc() |
25 |
{ |
26 |
cout << "Derive::Fnc" << endl; |
27 |
} |
28 |
}; |
29 |
30 |
typedef void(*Fun)(void); |
31 |
32 |
int main() |
33 |
{ |
34 |
Derive obj; |
35 |
Fun pFun = NULL; |
36 |
|
37 |
// calling 1st virtual function of Base1 |
38 |
pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+0); |
39 |
pFun(); |
40 |
41 |
// calling 2nd virtual function of Base1 |
42 |
pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+1); |
43 |
pFun(); |
44 |
45 |
// calling 1st virtual function of Base2 |
46 |
pFun = (Fun)*((int*)*(int*)((int*)&obj+1)+0); |
47 |
pFun(); |
48 |
49 |
// calling 2nd virtual function of Base2 |
50 |
pFun = (Fun)*((int*)*(int*)((int*)&obj+1)+1); |
51 |
pFun(); |
52 |
53 |
// calling 1st virtual function of Base3 |
54 |
pFun = (Fun)*((int*)*(int*)((int*)&obj+2)+0); |
55 |
pFun(); |
56 |
57 |
// calling 2nd virtual function of Base3 |
58 |
pFun = (Fun)*((int*)*(int*)((int*)&obj+2)+1); |
59 |
pFun(); |
60 |
61 |
// calling 1st virtual function of Drive |
62 |
pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+2); |
63 |
pFun(); |
64 |
65 |
// calling 2nd virtual function of Drive |
66 |
pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+3); |
67 |
pFun(); |
68 |
69 |
return 0; |
70 |
} |
OUTPUT:
Base1::fun
Base1::func
Base2::fun
Base2::func
Base3::fun
Base3::func
Drive::Fn
Drive::Fnc
By
Asadullah Ansari