[C++]单/多继承体系中的虚函数表

单继承中的虚表

单继承体系下:

class Base
{
public:
	virtual void func1()
	{
		cout << "Base::func1()" << endl;
	}
	int a = 0;
};
class Derive : public Base1
{
public:
	virtual void func1()
	{
		cout << "Derive::func1()" << endl;
	}
    virtual void func2()
	{
		cout << "Derive::func2()" << endl;
	}
	int c = 2;
};

对于单继承体系中的基类派生类中的虚表关系如下:

[C++]单/多继承体系中的虚函数表_第1张图片

基类有虚函数,所以基类有一个自己的虚表指针,指向自己的虚表,虚表中存放的就是func1()的地址。

派生类继承了基类的虚函数,所以有一个自己的虚表指针,指向自己的虚表,因为派生类重写了基类的虚函数,所以虚表中func1()函数的地址与基类不同。同时派生类又增加了一个虚函数,所以他的虚表里面有两个函数地址。


多继承中的虚表

多继承体系下:

class Base1
{
public:
	virtual void func1()
	{
		cout << "Base1::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Base1::func2()" << endl;
	}
	int a = 0; 
};
class Base2
{
public:
	virtual void func1()
	{
		cout << "Base2::func1()" << endl;
	}
	virtual void func3()
	{
		cout << "Base2::func3()" << endl;
	}
	int b = 1;
};
class Derive : public Base1, public Base2
{
public:
	virtual void func1()
	{
		cout << "Derive::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Derive::func2()" << endl;
	}
	int c = 2;
};

对于多继承体系中的派生类和基类的关系如下:

[C++]单/多继承体系中的虚函数表_第2张图片

两个基类各自都有唯一一个虚表指针,指向他们各自的虚表。

Derive对象这里有两个虚表指针,这两个分别是Base1和Base2部分的。这就表明了对于多继承,派生类继承了几个含有虚函数的基类,就会有几个虚表指针。而这里多个虚表指针的顺序则与他们继承的顺序相关:先继承的在前面,后继承的在后面。

但是注意,其中Derive中指向Derive::func1()函数的两个地址不相同(上图最右侧)。按理来说,他们应该是相同的,因为他们都调用了同一个函数。

先说结论:他们确实都调用的是同一个地址处的同一个函数 ,为何不同?原因是:

[C++]单/多继承体系中的虚函数表_第3张图片

上图是用多态的方式分别对Derive对象d的func1()函数进行调用。

Base1: Base1是先继承的,所以Derive中他的指针就是Derive对象d的起始地址0x0113F9C0

对于Base1来说,他的调用很简单:直接call函数地址,然后jump就完成了函数调用

Base2: Base2是后继承的,所以Derive中他的指针不是Derive对象d的起始地址,而是0x0113F9C8

而对于Base2来说,他的调用则略显复杂:同Base1一样,先是call了一个地址然后jump到一串指令(2),而这串指令的目的是修改this指针(ecx中存放的是this指针),将该指针从0x0113F9C8更改到了0x0113F9C0,也就是Derive对象d的地址。修改完指针后就跳转到函数执行函数体了。(以上测试均是在VS2022环境下进行)


菱形继承的虚表

这种继承十分少用,所以认识现象就足够

菱形继承

一般的菱形继承下:

class Boss
{
public:
	void test()
	{
		cout << "Boss::test()" << endl;
	}
	int o = 16;
};
class Base1 : public Boss
{
public:
	virtual void func1()
	{
		cout << "Base1::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Base1::func2()" << endl;
	}
	int a = 0; 
};
class Base2 : public Boss
{
public:
	virtual void func1()
	{
		cout << "Base2::func1()" << endl;
	}
	virtual void func3()
	{
		cout << "Base2::func3()" << endl;
	}
	int b = 1;
};
class Derive : public Base1,public Base2
{
public:
	virtual void func1()
	{
		cout << "Derive::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Derive::func2()" << endl;
	}
	virtual void func3()
	{
		cout << "Derive::func3()" << endl;
	}
	int c = 2;
};

对于菱形继承体系中的Derive存储模型如下:

[C++]单/多继承体系中的虚函数表_第4张图片

其虚表指针与前两个继承体系基本一致,唯一不同的就是对于Boss类,两个Base类中各自存储了一个独立的Boss中的成员变量int o(这个独立的成员变量属于菱形继承的弊端)。

菱形虚拟继承

菱形虚拟继承下:

class Boss
{
public:
	void test()
	{
		cout << "Boss::test()" << endl;
	}
	int o = 16;
};
class Base1 : virtual public Boss
{
public:
	virtual void func1()
	{
		cout << "Base1::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Base1::func2()" << endl;
	}
	int a = 0; 
};
class Base2 : virtual public Boss
{
public:
	virtual void func1()
	{
		cout << "Base2::func1()" << endl;
	}
	virtual void func3()
	{
		cout << "Base2::func3()" << endl;
	}
	int b = 1;
};
class Derive : public Base1,public Base2
{
public:
	virtual void func1()
	{
		cout << "Derive::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Derive::func2()" << endl;
	}
	virtual void func3()
	{
		cout << "Derive::func3()" << endl;
	}
	int c = 2;
};

对于菱形虚拟继承体系中的Derive存储模型如下:

[C++]单/多继承体系中的虚函数表_第5张图片

其中,绿色部分是虚表指针,他们同遵循之前的规则,但是绿色部分的后面位置还有一个指针。

这个指针不是虚表指针 ,而是虚基表指针 。这个虚基表指针是用来记录偏移量的。

他记录的该位置和实际存放Boss成员变量位置0x00CFF820的偏移量,这样菱形虚拟继承的做法在一定程度上解决了菱形继承的缺陷。

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