快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
Xp sp3
Vs2008
欢迎转载,但请保留作者信息
这次我们为父类加上虚函数:
class CParent
{
public:
int parent_a;
int parent_b;
public:
virtual void parent_f1()
{
parent_a = 0x10;
}
virtual void parent_f2()
{
parent_b = 0x20;
}
};
class CChild : public CParent
{
public:
int child_a;
int child_b;
public:
virtual void parent_f1()
{
child_a = 0x30;
}
virtual void parent_f2()
{
child_b = 0x40;
}
};
CChild child, *pchild;
CParent parent, *pparent;
同样,我们先对其进行简单赋值再观察内存变化。
先对父类进行赋值:
parent.parent_a = 1;
004139FE C7 05 B8 81 41 00 01 00 00 00 mov dword ptr [parent+4 (4181B8h)],1
parent.parent_b = 2;
00413A08 C7 05 BC 81 41 00 02 00 00 00 mov dword ptr [parent+8 (4181BCh)],2
看内存:
0x004181B4 5c 68 41 00 01 00 00 00 02 00 00 00 00 00 00 00 /hA.............
很显然,在数据之前插入了四个字节,猜想这个应该就是所谓的vtbl的指针,如果这样,那么所有同类对象都应该有相同的指针,定义两个变量:
CParent v1, v2;
先看v1的内存:
0x0012FF58 5c 68 41 00 cc cc cc cc cc cc cc cc cc cc cc cc PhA.............
再看v2的内存:
0x0012FF44 5c 68 41 00 cc cc cc cc cc cc cc cc cc cc cc cc PhA.............
果然是一样的,嘿。
再看子类的赋值:
child.child_a = 3;
00413A12 C7 05 AC 81 41 00 03 00 00 00 mov dword ptr [child+0Ch (4181ACh)],3
child.child_b = 4;
00413A1C C7 05 B0 81 41 00 04 00 00 00 mov dword ptr [child+10h (4181B0h)],4
看&child的内存:
0x004181A0 50 68 41 00 00 00 00 00 00 00 00 00 03 00 00 00 PhA.............
0x004181B0 04 00 00 00 5c 68 41 00 01 00 00 00 02 00 00 00 ..../hA.........
很明显,父类和子类指向的vtbl是不一样的。
先看父类虚函数的调用:
pparent = &parent;
00413A26 C7 05 98 81 41 00 B4 81 41 00 mov dword ptr [pparent (418198h)],offset parent (4181B4h)
pparent->parent_f1();
00413A30 A1 98 81 41 00 mov eax,dword ptr [pparent (418198h)]
00413A35 8B 10 mov edx,dword ptr [eax]
00413A37 8B F4 mov esi,esp
00413A39 8B 0D 98 81 41 00 mov ecx,dword ptr [pparent (418198h)]
00413A3F 8B 02 mov eax,dword ptr [edx]
00413A41 FF D0 call eax
00413A43 3B F4 cmp esi,esp
00413A45 E8 1E D7 FF FF call @ILT+355(__RTC_CheckEsp) (411168h)
pparent->parent_f2();
00413A4A A1 98 81 41 00 mov eax,dword ptr [pparent (418198h)]
00413A4F 8B 10 mov edx,dword ptr [eax]
00413A51 8B F4 mov esi,esp
00413A53 8B 0D 98 81 41 00 mov ecx,dword ptr [pparent (418198h)]
00413A59 8B 42 04 mov eax,dword ptr [edx+4]
00413A5C FF D0 call eax
00413A5E 3B F4 cmp esi,esp
00413A60 E8 03 D7 FF FF call @ILT+355(__RTC_CheckEsp) (411168h)
这回的函数调用就不是简单地使用名称进行调用了,而是先取出vtbl的首地址(pparent指向的头四个字节),然后加上偏移量,再取出这个新地址中存储的指针,最后用call进行函数调用。这个偏移量是由编译器自动排列得到的。
我们看看vtbl的内容:
0x0041685C b4 10 41 00 08 12 41 00 44 76 41 00 dc 10 41 00 ..A...A.DvA...A.
这里有两个指针:0x004110b4和0x00411208,看看它们指向的位置:
004110B4 E9 57 0C 00 00 jmp CParent::parent_f1 (411D10h)
……..
00411208 E9 43 0B 00 00 jmp CParent::parent_f2 (411D50h)
果然指向的是父类的成员函数。
现在我们用子类的指针调用子类的虚函数:
pchild = &child;
00413A65 C7 05 9C 81 41 00 A0 81 41 00 mov dword ptr [pchild (41819Ch)],offset child (4181A0h)
pchild->parent_f1();
00413A6F A1 9C 81 41 00 mov eax,dword ptr [pchild (41819Ch)]
00413A74 8B 10 mov edx,dword ptr [eax]
00413A76 8B F4 mov esi,esp
00413A78 8B 0D 9C 81 41 00 mov ecx,dword ptr [pchild (41819Ch)]
00413A7E 8B 02 mov eax,dword ptr [edx]
00413A80 FF D0 call eax
00413A82 3B F4 cmp esi,esp
00413A84 E8 DF D6 FF FF call @ILT+355(__RTC_CheckEsp) (411168h)
pchild->parent_f2();
00413A89 A1 9C 81 41 00 mov eax,dword ptr [pchild (41819Ch)]
00413A8E 8B 10 mov edx,dword ptr [eax]
00413A90 8B F4 mov esi,esp
00413A92 8B 0D 9C 81 41 00 mov ecx,dword ptr [pchild (41819Ch)]
00413A98 8B 42 04 mov eax,dword ptr [edx+4]
00413A9B FF D0 call eax
00413A9D 3B F4 cmp esi,esp
00413A9F E8 C4 D6 FF FF call @ILT+355(__RTC_CheckEsp) (411168h)
它的调用方式和父类完全一样,都是先取vtbl的首地址,再加上偏移量后进行call。看一下子类的vtbl:
0x00416850 a9 11 41 00 0d 12 41 00 20 76 41 00 b4 10 41 00 ..A...A. vA...A.
这里有两个指针:0x004111a9和0x0041120d,看看它们指向的位置:
004111A9 E9 82 03 00 00 jmp CChild::parent_f1 (411530h)
……..
0041120D E9 6E 03 00 00 jmp CChild::parent_f2 (411580h)
果然指向的是子类的成员函数。
这回我们用一个父类指针来调用虚函数:
pparent = &child;
00413AA4 C7 05 98 81 41 00 A0 81 41 00 mov dword ptr [pparent (418198h)],offset child (4181A0h)
pparent->parent_f1();
00413AAE A1 98 81 41 00 mov eax,dword ptr [pparent (418198h)]
00413AB3 8B 10 mov edx,dword ptr [eax]
00413AB5 8B F4 mov esi,esp
00413AB7 8B 0D 98 81 41 00 mov ecx,dword ptr [pparent (418198h)]
00413ABD 8B 02 mov eax,dword ptr [edx]
00413ABF FF D0 call eax
00413AC1 3B F4 cmp esi,esp
00413AC3 E8 A0 D6 FF FF call @ILT+355(__RTC_CheckEsp) (411168h)
可以看到,这个时候使用的是子函数的VTBL,因此调用的自然是子类的虚函数。
vs2008下C++对象内存布局(2):简单继承(2009-9-9)
vs2008下C++对象内存布局(1)(2009-9-9)