当前位置: 首页 > news >正文

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<iostream>
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;
}

输出结果如下:

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

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

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

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

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

        

测试代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
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;
}

 运行结果如下:

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

相关文章:

  • 使用 kubeadm 创建高可用 Kubernetes 及外部 etcd 集群
  • 基于Spring Boot的产业园区智慧公寓管理系统设计与实现(LW+源码+讲解)
  • C++ 红黑树万字详解(含模拟实现(两种版本))
  • qt QProgressBar总结
  • `pip install .` 的作用详解:从当前目录安装Python包
  • 短连接服务器压测-wrk
  • 【缓存】缓存雪崩与缓存穿透:高并发系统的隐形杀手
  • IDEA 2024.1 最新永久可用(亲测有效)
  • redis序列化设置
  • 使用vscode导出Markdown的PDF无法显示数学公式的问题
  • 【R包】pathlinkR转录组数据分析和可视化利器
  • PyInstaller 打包python 程序 成 可执行文件
  • 算法-数据结构(图)-迪杰斯特拉最短逻辑算法( Dijkstra)
  • 使用扩散模型DDPM生成Sine正弦曲线的案例(使用Classifier-free guidance)
  • 力扣——最长递增子序列
  • (二)未来十至二十年的信息技术核心领域(AI、数据库、编程语言)完全零基础者的学习路径与技能提升策略
  • StableDiffusion打包 项目迁移 项目分发 0
  • DeepSeek如何辅助学术写作的性质研究?
  • 什么是回调函数
  • Linux版本控制器Git【Ubuntu系统】
  • 深圳拟出让3宗居住用地,共计用地面积6.77公顷
  • 上海国际电影节纪录片单元,还世界真实色彩
  • A股午后拉升,沪指收复3400点:大金融发力,两市成交超1.3万亿元
  • 沙青青评《通勤梦魇》︱“人机组合”的通勤之路
  • 王毅谈中拉命运共同体建设“五大工程”及落实举措
  • 秦洪看盘|交易新逻辑,银行股成A股稳定器