首先给出以下继承关系,以便描述虚继承原理:
class AAA
{
public:
int age;
};
class BBB:virtual public AAA//变为虚继承,A变为虚基类
{
};
class CCC:virtual public AAA
{
};
class DDD:public BBB, public CCC
{
};
命令格式如下:
cl –d1reportSingleClassLayout[classname] xxx.cpp
- classname 为类名,-d1reportSingleClassLayout[classname] 之间没有空格。
- xxx.cpp为源代码文件名
通过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
- 目的:的到虚基类表中所记录的vbptr指针相对于父类成员(age)的偏移量。
- 先取得最低派生类的地址,强转为int**类型,即只取四个字节的地址,得到的便是vbptr指针的地址。
- 再对这个指针寻址*,得到的便是 在Sheep虚基类表中指向表头的一级指针,此时将这个指针强转为 int*类型再加+1,使其指向虚基类表中记录的偏移量,再对这个指针进行寻址,得到的就是偏移量的值了。
- 得到偏移量后,使用BBB的vbptr指针的起始位置加上这个偏移量,然后强转为AAA*类型,然后再次进行指向操作,便可以得到age的值了。
- 当然还可以对找到的成员变量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;
- 目的:通过Tuo的虚基类指针得到此指针对于age的偏移量。
- 还是&d,然后再转为int**类型,此时要先+1得到Tuo的vbptr指针,之后再转为int**类型进行寻址,得到Tuo虚基类表中指向表头的一级指针,之后再转为int类型,再次寻址,得到偏移量4。
- 强转为AAA*类型指向age。
- 将地址转为int*类型解引用。