虚基类和虚继承
虚基类和虚继承
定义
虚基类:被虚继承的类,就称为虚基类。
virtual作用:
virtual修饰了成员方法是虚函数。
可以修饰继承方式,是虚继承。被虚继承的类就称为虚基类。
语法
class Baseclass;
class Subclass
: public/private/protected virtual Baseclass
{
public:
//...
private:
//...
protected:
//...
};
//其中Baseclass称之为Subclass的虚基类, 而不是说Baseclass就是虚基类
vfptr:一个类有虚函数,这个类生成的对象就有vfptr,指向vftable。 vbptr:在派生类中。 vftable:第一行为向上偏移量,第二行为离的偏移量。 vbtable:存放的RTTI指针,指向运行时RTTI信息与虚函数地址。
示例
class A
{
public:
private:
int ma;
};
class B : public A
{
public:
private:
int mb;
};
//A a; 4个字节
//B b; 8个字节
这里我们的对象a占4个字节,对象b占8个字节。但如果我们给B的继承方式访问限定符前面加了一个virtual
关键字。
内存分布
查看方法
使用命令:cl –d1reportSingleClassLayout[classname] xxx.cpp
查看此时的内存布局。
或者使用vs先配置项目属性,然后生成解决方案:
/d1 reportSingleClassLayout类名
/d1 reportAllClassLayout


简单虚继承内存分布
类A被虚继承了,但内存布局没有变化。

我们再看一下类B,不是之前的8个字节,变为12个字节,多了一个vbptr指针。原来最上面应该为ma与mb,但是现在多了一个vbptr(虚基类指针),ma跑到派生类最后面去了。vbptr指向的是vbtable,vbtable第一行为0,第二行为虚基类指针到虚基类数据的偏移量。

当我们遇到虚继承时候。要考虑派生类B的内存布局时,首先我们先不考虑虚继承。类B继承了基类的ma,还有自己的mb;当我们基类被虚继承后,基类变为虚基类,虚基类的数据一定要在派生类数据最后面,再在最上面添加一个vbptr。派生类的内存就由这几部分来构成。

虚基类指针(vbptr)指向虚基类表(vbtable),vbtable第一行为向上的偏移量,因为vbptr在该派生类内存起始部分,因此向上的偏移量为0;第二行为向下的偏移量(vbptr离虚基类数据的偏移量),原来基类的数据放到最后,找ma的时候还是在最开始找,但ma被移动,根据偏移的字节就可以找到。

含虚函数的内存分布

- B类有两个虚函数表,一个是继承基类的,另一个是自己有虚函数创建的
菱形继承内存分布
// 基类A
class A
{
public:
int dataA;
};
class B : virtual public A
{
public:
int dataB;
};
class C : virtual public A
{
public:
int dataC;
};
class D : public B, public C
{
public:
int dataD;
};

小结
结论一:单个虚继承,不带虚函数 虚继承与继承的区别
- 多了一个虚基指针
- 虚基类位于派生类存储空间的最末尾
结论二:单个虚继承,带虚函数
- 如果派生类没有自己的虚函数,此时派生类对象不会产生虚函数指针
- 如果派生类拥有自己的虚函数,此时派生类对象就会产生自己本身的虚函数指针,并且该虚函数指针位于派生类对象存储空间的****