跳至主要內容

虚基类和虚继承

张威大约 4 分钟c/c++继承

虚基类和虚继承

定义

虚基类:被虚继承的类,就称为虚基类。

virtual作用:

  1. virtual修饰了成员方法是虚函数。

  2. 可以修饰继承方式,是虚继承。被虚继承的类就称为虚基类。

语法

class Baseclass;
class Subclass 
: public/private/protected virtual Baseclass 
{
public:    
    //...
private:   
    //...
protected: 
    //...
};
//其中Baseclass称之为Subclass的虚基类, 而不是说Baseclass就是虚基类

vfptr:一个类有虚函数,这个类生成的对象就有vfptr指向vftablevbptr在派生类中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被移动,根据偏移的字节就可以找到

含虚函数的内存分布

image-20240415111605527
image-20240415111605527
  • 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;
};

小结

结论一:单个虚继承,不带虚函数 虚继承与继承的区别

  1. 多了一个虚基指针
  2. 虚基类位于派生类存储空间的最末尾

结论二:单个虚继承,带虚函数

  1. 如果派生类没有自己的虚函数,此时派生类对象不会产生虚函数指针
  2. 如果派生类拥有自己的虚函数,此时派生类对象就会产生自己本身的虚函数指针,并且该虚函数指针位于派生类对象存储空间的****