图文详解虚继承的实现原理(内存布局分析)

1.为什么需要虚继承?

虚继承是为了解决多重继承中存在的一些问题而出现的。在多重继承中,可能会存在多个派生类继承同一个基类的情况(菱形继承),这个时候,不仅会浪费存储空间,而且还会导致二义性。二义性解释如下:
图文详解虚继承的实现原理(内存布局分析)_第1张图片

//diamond_Inherit.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include 
using namespace std;

class Animal
{
public:
	int age;

};
//普通继承
class Sheep: public Animal
{
public:
	
};

class Tuo: public Animal
{
public:

};

class SheepTuo :public Sheep, public Tuo
{
public:

};
//diamond_Inherit.cpp
#include"diamond_Inherit.h"
#include 
#include 
using namespace std;


void test01()
{
    SheepTuo yt;
    yt.Sheep::age = 10;
    yt.Tuo::age = 20;
    cout << yt.Sheep::age << endl;
    cout << yt.Tuo::age << endl;

}

int main()
{
    test01();
    system("pause");
    return EXIT_SUCCESS;
}

这个时候Sheep类继承了一个age,Tuo类也继承了一个age。他们都各自保有了一份age拷贝,就会导致age不同。
在这里插入图片描述

2.虚继承如何实现?

先更改代码,在继承的类前加上virtual:

//diamond_Inherit.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include 
using namespace std;

class Animal
{
public:
	int age;

};
//这里添加的virtual对于Sheep和Tuo本身没影响,只对他们的子类有影响。
class Sheep: virtual public Animal
{
public:
	
};

class Tuo: virtual public Animal
{
public:

};

class SheepTuo :public Sheep, public Tuo
{
public:

};

要探究虚继承如何实现,需要借用VS的开发人员命令提示工具,在VS2019的工具->命令行->开发者命令提示中。
cd到当前项目的目录,输入cl /d1reportSingleClassLayout"要查看的类名" “文件名”,在这里就是cl /d1reportSingleClassLayoutSheepTuo diamond_Inherit.cpp。可以看到当前类内存的结构。(编译后才能查看到内存分布)。
图文详解虚继承的实现原理(内存布局分析)_第2张图片
这个图就是内存结构,可以看到,SheepTuo类中分别继承了来自Sheep类的vbptr(虚基类指针)和Tuo类的vbptr(虚基类指针)。这个虚基类指针指向的是一个虚基类表,可以在图中看到虚基类表中第一项存储的是vbptr与本类的偏移地址,也就是继承过来的Sheep类中初始位置就是存放Sheep类的的vbptr,在这里为0;第二项是本类的vbptr与虚基类的公有成员之间的偏移量,也就是Sheep的vbptr和Animal类的age之间偏移为8,Tuo的vbptr和age之间偏移量为4。对于虚基类的派生类,虚基类的偏移量由实际类型决定,因此在运行时才可以确定虚基类的地址。

指的注意的是,Sheep类中也是存放了一份age,在这里还可以看到,Sheep和Tuo的Size都是8,因为除了继承的age以外,还有Size为4的虚函数指针。
图文详解虚继承的实现原理(内存布局分析)_第3张图片图文详解虚继承的实现原理(内存布局分析)_第4张图片
因为class SheepTuo :public Sheep, public Tuo继承的时候,把Sheep和Tuo的vbptr都继承了,然后通过他们类距离虚基类中的公共成员age的偏移量发现他们指向的是同一个age,所以就不会拷贝两份,SheepTuo只保留一份age。至于虚继承底层实现原理则与编译器相关。

3.虚继承和虚函数的关系?

答:没啥关系。
至于共同点就是都利用了虚指针和虚表。
虚基类依旧存在于他的派生类中,要占用存储空间;虚函数不占用存储空间。
虚基类表存储的是虚基类相对于其派生类的偏移;而虚函数表存储的是该类中所有的虚函数对应的指针

你可能感兴趣的:(C/C++)