接下来,我们再观察类C040和从它派生的类C050的对象,这两个类各有自己的虚表,但由于C050没有重写继承自C040的虚函数,所以它们的虚表中的条目的值,即指向的虚函数的地址应该是一样的。
运行如下代码:
C040 c040; C050 c050; PRINT_VTABLE_ITEM(c040, 0, 0) PRINT_VTABLE_ITEM(c050, 0, 0) |
结果为:
c040 : objadr:0012FD4C vpadr:0012FD4C vtadr:0045B448 vtival(0):0041D834 c050 : objadr:0012FD40 vpadr:0012FD40 vtadr:0045B44C vtival(0):0041D834 |
果然这次我们可以看到虽然前几列皆不相同,但最后一列的值相同。即它们共享同一个虚函数。
定义一个C043类,包含两个虚函数。再定义一个C071类,从C043派生,并重写继承的第一个虚函数。
struct C043 { virtual void foo1() {} virtual void foo2() {} }; struct C071 : C043 { virtual void foo1() {} }; |
我们可以预料到,C043和C071各有一个包含两个条目的虚表,由于C071派生自C043,并且重写了第一个虚函数。那么这两个类的虚表的第一个条目值是不同的,而第二项应该是相同的。运行如下代码。
C043 c043; C071 c071; PRINT_SIZE_DETAIL(C071) PRINT_VTABLE_ITEM(c043, 0, 0) PRINT_VTABLE_ITEM(c071, 0, 0) PRINT_VTABLE_ITEM(c043, 0, 1) PRINT_VTABLE_ITEM(c071, 0, 1) |
结果为:
The size of C071 is 4 The detail of C071 is 5c b4 45 00 c043 : objadr:0012FCD4 vpadr:0012FCD4 vtadr:0045B450 vtival(0):0041D4F1 c071 : objadr:0012FCC8 vpadr:0012FCC8 vtadr:0045B45C vtival(0):0041D811 c043 : objadr:0012FCD4 vpadr:0012FCD4 vtadr:0045B450 vtival(1):0041DFE1 c071 : objadr:0012FCC8 vpadr:0012FCC8 vtadr:0045B45C vtival(1):0041DFE1 |
观察第1、2行的最后一列,即两个类的虚表的第一个条目,由于C071重写了foo1函数,所以这个值不一样。而第3、4行的最后一列为两个类的虚表的第二个条目,由于C071并没有重写它,所以这两个值是相同的。和我们之间的猜测是一致的。
接下来我们看看多重继承。定义两个类,各含一个虚函数,及一个数据成员。再从这两个类派生一个空子类。
struct C041 { C041() : c_(0x01) {} virtual void foo() { c_ = 0x02; } char c_; }; struct C042 { C042() : c_(0x02) {} virtual void foo2() {} char c_; }; struct C051 : public C041, public C042 {}; |
运行如下代码:
PRINT_SIZE_DETAIL(C041) PRINT_SIZE_DETAIL(C042) PRINT_SIZE_DETAIL(C051) |
结果为:
The size of C041 is 5 The detail of C041 is 64 b3 45 00 01 The size of C042 is 5 The detail of C042 is 68 b3 45 00 02 The size of C051 is 10 The detail of C051 is 6c b4 45 00 01 68 b4 45 00 02 |
注意,首先我们观察C051的对象输出,发现它的大小为10字节,这说明它有两个虚表指针,从导出的内存数据我们可以推断,首先是一个虚表指针,然后是从C041继承的成员变量,值也是我们在C041的构造函数中赋的值0x01,然后又是一个虚表指针,再是从C042继承的成员变量,值为0x02。
为了验证,我们再运行如下代码:
C041 c041; C042 c042; C051 c051; PRINT_VTABLE_ITEM(c041, 0, 0) PRINT_VTABLE_ITEM(c042, 0, 0) PRINT_VTABLE_ITEM(c051, 0, 0) PRINT_VTABLE_ITEM(c051, 5, 0) |
注意最后一行的第二个参数,5。它是从对象起始地址开始到虚表指针的偏移值(按字节计算),从上面的对象内存输出我们看到C041的大小为5字节,因此C051中第二个虚表指针的起始位置距对象地址的偏移为5字节。输出的结果为:
(注:这个偏移值是通过观察而判断出来的,并不通用,而且它依赖于我们前面所说的编译器在生成代码时所用的结构成员对齐方式,我们将这个值设为1。如果设为其他值会影响对象的大小及这个偏移值。参见第一篇起始处的说明。下同。)
c041 : objadr:0012FB88 vpadr:0012FB88 vtadr:0045B364 vtival(0):0041DF1E c042 : objadr:0012FB78 vpadr:0012FB78 vtadr:0045B368 vtival(0):0041D43D c051 : objadr:0012FB64 vpadr:0012FB64 vtadr:0045B46C vtival(0):0041DF1E c051 : objadr:0012FB64 vpadr:0012FB69 vtadr:0045B468 vtival(0):0041D43D |
这下我们可以看到C051的两个虚表指针指向两个不现的虚表(第3、4行的vtadr列),而虚表中的条目的值分别等于C041和C042(即它的两个父类)的虚表条目的值(第1、3行和2、4行的vtival列的值相同)。 
2/2 首页 上一页 1 2 |