正 文

C++对象布局及多态探索之菱形结构虚继承


www.7dspace.com  更新日期:2005-12-10 3:01:57  七度空间


  我们再看看对成员的访问情况。运行以下代码并查看相应的汇编代码。

C110 c110;
c110.c_ = 0x51;
c110.C100::c_ = 0x52;
c110.C101::c_ = 0x52;
c110.C041::c_ = 0x53;
c110.foo();

  对应的汇编代码为:

01 00423993 push 1
02 00423995 lea ecx,[ebp+FFFFF7F0h]
03 0042399B call 0041DE60
04 004239A0 mov byte ptr [ebp+FFFFF7FAh],51h
05 004239A7 mov byte ptr [ebp+FFFFF7F4h],52h
06 004239AE mov byte ptr [ebp+FFFFF7F9h],52h
07 004239B5 mov eax,dword ptr [ebp+FFFFF7F0h]
08 004239BB mov ecx,dword ptr [eax+4]
09 004239BE mov byte ptr [ebp+ecx+FFFFF7F4h],53h
10 004239C6 mov eax,dword ptr [ebp+FFFFF7F0h]
11 004239CC mov ecx,dword ptr [eax+4]
12 004239CF lea ecx,[ebp+ecx+FFFFF7F0h]
13 004239D6 call 0041DF32

  前3行是对象的初始化,调用了对象的构造函数。4、5、6行是对子类、左右父类的成员变量的赋值。我们可以看到是直接写的,因为这一层的继承是普通继承。第7、8、9行是对祖父类成员变量的赋值,和上篇讨论过的一样,是通过偏移值指针指向的偏移值来间接访问的。最后的4行指令是对成员函数的调用。我们可以看到调用的函数地址是直接给出的(最后一行),因为我们是通过对象来调用,即使是虚函数调用也不会有多态的行为。但是得到this指针的方式却是颇为间接,即第10、11、12行。因为这个函数在祖父类中定义,那么它操作的数据成员应该是祖父类的。因此编译器要调整this指针的位置。而祖父类又是被虚继承,因此要通过偏移值指针指向的偏移值来进行调整。

  再观察一下第9行和第12行,可以看到计算出来的地址值是不一样的。这是因为第9行为给祖父类的成员变量赋值,而祖父类中有虚表指针存在,所以在得到对象的起始地址后,编译器给它加了4字节的偏移量以跳过虚指针。实际的得到地址的运算为: [ebp+ecx+FFFFF7F0h+4h],编译器在生成代码时会直接把最后一步运算做掉。

  我们再看一个例子,这个例子的继承结构和上一篇中是一样的,也是菱形结构。不同的是,每一个类都重写了顶层类声明的虚函数。代码如下:

struct C041
{
 C041() : c_(0x01) {}
 virtual void foo() { c_ = 0x02; }
 char c_;
};
struct C140 : public virtual C041
{
 C140() : c_(0x02) {}
 virtual void foo() { c_ = 0x11; }
 char c_;
};
struct C141 : public virtual C041
{
 C141() : c_(0x03) {}
 virtual void foo() { c_ = 0x12; }
 char c_;
};
struct C150 : public C140, public C141
{
 C150() : c_(0x04) {}
 virtual void foo() { c_ = 0x21; }
 char c_;
};

5页,页码:[1] [2] [3] [4] [5] 

上一篇:J2ME三种低级用户界面事件处理技术比较
下一篇:如何在活动目录中设置可登录的计算机
作者:潘凯  来源:VCHELP ( 责任编辑:7dspace )
收藏此页】【打印】【关闭
站 内 搜 索
 

热 点 导 读
特 别 推 荐