C++虚继承实现原理——解决菱形继承问题

首先给出以下继承关系,以便描述虚继承原理:

class AAA
{
public:
	int age;
};

class BBB:virtual public AAA//变为虚继承,A变为虚基类
{
};

class CCC:virtual public AAA
{
};

class DDD:public BBB, public CCC
{
};

通过vs中的开发人员命令提示窗口得到类的内存布局:class DDD中左边的数字代表偏移量,vpter(virtual base table pointer)是虚基类表指针,指向的是虚基类表的表头;vbtable(virtual base table)是虚基类表,类中保存的父类指针指向了虚基类表中的表头(指针),而对这个表头+1进行寻址操作即可得到虚基类表中所记录的偏移量。

一.以下代码为通过BBB的虚基类指针得到此指针位置相对于成员变量age的偏移量,并根据偏移量计算出对应成员变量age的值。

DDD d;
d.age = 10;
cout << *((int *)*(int **)&d + 1) << endl;//8
cout << ((AAA *)((char *)&d + *((int *)*(int **)&d + 1)))->age << endl;//10
cout << *(int *)((char *)&d + *((int *)*(int **)&d + 1)) << endl;//10
  1. 目的:的到虚基类表中所记录的vbptr指针相对于父类成员(age)的偏移量。
  2. 先取得最低派生类的地址,强转为int**类型,即只取四个字节的地址,得到的便是vbptr指针的地址。
  3. 再对这个指针寻址*,得到的便是 在Sheep虚基类表中指向表头的一级指针,此时将这个指针强转为 int*类型再加+1,使其指向虚基类表中记录的偏移量,再对这个指针进行寻址,得到的就是偏移量的值了。
  4. 得到偏移量后,使用BBB的vbptr指针的起始位置加上这个偏移量,然后强转为AAA*类型,然后再次进行指向操作,便可以得到age的值了。
  5. 当然还可以对找到的成员变量age的地址直接解引用得到age的值。

需要注意的是:这里只能将地址转为AAA*类型做指向操作,BBB、CCC、DDD均不可以,因为内存结构都不相同。只有AAA是在首地址位置就就代表了成员变量age,而BBB、CCC则是保存的vbptr,DDD保存的是两个vbptr指针与一个age变量(当然这个变量也是它们的顶级父类AAA的)。

二.通过CCC的虚基类指针得到此指针位置相对于成员变量age的偏移量,并根据偏移量计算出对应成员变量age的值。

cout << *((int *)*(int **)((int **)&d + 1) + 1) << endl;//4
cout << (  (AAA *) ((char *)((int *)&d + 1) + *((int *)*(int **)((int **)&d + 1) + 1))  )->age << endl;//10
cout << *(int *)( ((char *)((int *)&d + 1) + *((int *)*(int **)((int **)&d + 1) + 1)) )<< endl;
  1. 目的:通过Tuo的虚基类指针得到此指针对于age的偏移量。
  2. 还是&d,然后再转为int**类型,此时要先+1得到Tuo的vbptr指针,之后再转为int**类型进行寻址,得到Tuo虚基类表中指向表头的一级指针,之后再转为int类型,再次寻址,得到偏移量4。
  3. 强转为AAA*类型指向age。
  4. 将地址转为int*类型解引用。

 

 

 

 

 

 

 

展开阅读全文

没有更多推荐了,返回首页