31.C++多态4(静态多态,动态多态,虚函数表的存储位置)

⭐上篇文章:30.C++多态 3 (多态的原理,虚指针,虚函数表,抽象类)-CSDN博客

⭐本篇代码:c++学习/17.C++三大特性-多态 · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)

⭐标⭐是比较重要的部分

目录

一. 静态多态与动态多态

二. 派生类的虚表的内存分布

2.1 单继承派生类虚表的分布

2.2 多继承下派生类虚表的分布 


一. 静态多态与动态多态

        静态多态是在编译的时候就确定并绑定好了的,比如函数重载,模板的使用。

        动态多态是在运行期间再绑定的,当调用的时候才去确定函数的地址

 关于模板的使用可以看我的这些文章:

17. C++模板(template)1(泛型编程,函数模板,类模板)_c++ 17 template-CSDN博客

24. C++模板 2 (非类型模板参数,模板的特化与模板的分离编译)-CSDN博客

二. 派生类的虚表的内存分布

2.1 单继承派生类虚表的分布

        在上篇文章中,我们知道对于派生类来说。它会先将父类的虚表拷贝一份,如果自己有重写虚函数,就会将重写的虚函数覆盖到虚表中,如果有新的虚函数,就会添加到原本虚表的末尾。

我们通过代码来输出虚函数的地址来观察一下:

通过解引用虚函数指针来获取虚函数表中的内容

#define _CRT_SECURE_NO_WARNINGS 1
#include
using namespace std;

//单继承
class A
{
public:
	virtual void f1() { cout << "A::f1" << endl; }
	virtual void f2() { cout << "A::f2" << endl; }
private:
	int a = 1;
};

class B :public A
{
public:
	virtual void f1() { cout << "B::f1" << endl; }
	//f2没有重写被直接继承
	virtual void f3() { cout << "B::f3" << endl; }
	virtual void f4() { cout << "B::f4" << endl; }
private:
	int b = 1;
};



//void(*p)();//函数指针
typedef void(*VF_PTR)();	//重定义函数指针的名称
void PrintFTable(VF_PTR* table,int size) //函数指针数组(存函数指针的数组)
{
	for (int i = 0; i < size; ++i)
	{
		printf("vftable[%d]:%p\n", i, table[i]);
		table[i]();
	}
	cout << endl;
}

//单继承虚函数表
void test1()
{
	A a;
	B b;
	//取虚函数表的地址(前四个字节)
	cout << "a的虚表:" << endl;
	PrintFTable((VF_PTR*)(*(int*)&a), 2);
	cout << "a的虚表:" << endl;
	PrintFTable((VF_PTR*)(*(int*)&b), 4);
}

int main()
{
	test1();
	return 0;
}

输出结果如下:

31.C++多态4(静态多态,动态多态,虚函数表的存储位置)_第1张图片

可以看到,由于f2没有被重写,所以两个虚表中f2的地址是一样的!

2.2 多继承下派生类虚表的分布 

        如果一个派生类有两个父类,那么它会有两张虚表。如果派生类重写了父类的虚函数,同样会在相应的虚表中进行覆盖(比如 重写了A函数,且A函数是来自类Person中的,那么就会在Person这个虚表中进行覆盖),        

        如果是新增的虚函数,则只会写入到第一张表中。

        如果某一个函数在多个父类中都存在,那么重写这个虚函数的话就会覆盖所有表中的这个虚函数

        

测试代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include
using namespace std;
//请在32位下运行此程序!

//单继承
class A
{
public:
	virtual void f1() { cout << "A::f1" << endl; }
	virtual void f2() { cout << "A::f2" << endl; }
private:
	int a = 1;
};

//多继承
class C
{
public:
	virtual void f1() { cout << "C::f1" << endl; }
	virtual void f2() { cout << "C::f2" << endl; }
private:
	int c = 1;

};

class D :public A, public C
{
public:
	virtual void f1() { cout << "D::f1" << endl; }
	virtual void f3() { cout << "D::f3" << endl; }
private:
	int d = 1;
};


typedef void(*VF_PTR)();	
void PrintFTable(VF_PTR* table, int size)
{
	for (int i = 0; i < size; i++)
	{
		printf("vftable[%d]:%p\n", i, table[i]);
		table[i]();
	}
	cout << endl;
}



//多继承虚函数表
void test2()
{
	A a;
	C c;
	D d;
	cout << sizeof(d) << endl;//32位下为20,64位下位40
	PrintFTable((VF_PTR*)(*(int*)&a), 2);
	PrintFTable((VF_PTR*)(*(int*)&c), 2);
	PrintFTable((VF_PTR*)(*(int*)&d), 3);	//表1的位置
	PrintFTable((VF_PTR*)(*(int*)((char*)&d + sizeof(A))), 2);	//表2的位置
}
//此时子类有两个虚表,分别存在第一个基类,第二个父基类。
//且子类自己的虚函数位于第一个基类的虚表中

int main()
{
	test2();
	return 0;
}

 运行结果如下:

31.C++多态4(静态多态,动态多态,虚函数表的存储位置)_第2张图片

        可以看到,多继承的派生类有两张表,并且重写的虚函数f1会进行覆盖 ,而且新增的虚函数f3会添加到第一张表中。

你可能感兴趣的:(c++,开发语言)