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

【C++】多态(二)

多态的实现原理

  • 多态实现原理
    • 对象模型(带有虚函数的类对象的模型)
    • 多态的原理
    • 多态的分类
    • 多继承体系中虚函数存储

不同的编译器对于多态底层实现原理细节上可能会有差异(当前使用 vs2017 32位 编译)

多态实现原理

对象模型(带有虚函数的类对象的模型)

1、基类虚函数存储

首先来看看,在一个类中 虚函数是如何进行存储的:

(1)一个类有一个对象:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

(2)一个类有多个对象:

在这里插入图片描述

由此可总结:

当一个类中包含有虚函数的时候,该类在定义对象时候编译器会自动调用类的构造方法来实现虚函数表指针信息的初始化,虚表指针指向一个地址,该地址中存储的是类中所有的虚函数入口地址信息(按照虚函数的声明顺序存储在虚表指针所指向的地址中),并且对于同一个类所定义的对象,它们的虚表指针是相同的

2、子类虚函数存储

(1)当继承关系中没有虚函数:

class A {
public:
	void Func1()
	{
		cout << "A::Func1()" << endl;
	}
public:
	int _a;
};


class B :public A {
public:
	void Func()
	{
		cout << "B::Func()" << endl;
	}
public:
	int _b;
};

在这里插入图片描述

在这里插入图片描述

(2)当继承关系中存在虚函数

不存在虚函数的重写时:

class A {
public:
	virtual void Func1()
	{
		cout << "A::Func1()" << endl;
	}
public:
	int _a;
};


class B :public A {
public:
	void Func()
	{
		cout << "B::Func()" << endl;
	}
public:
	int _b;
};

在这里插入图片描述

在这里插入图片描述

当存在虚函数的重写时:

class A {
public:
	virtual void Func1()
	{
		cout << "A::Func1()" << endl;
	}
public:
	int _a;
};


class B :public A {
public:
	virtual void Func1()            //构成虚函数重写
	{
		cout << "B::Func1()" << endl;
	}
public:
	int _b;
};

在这里插入图片描述

class A {
public:
	virtual void Func1()
	{
		cout << "A::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "A::Func2()" << endl;
	}
	virtual void Func3()
	{
		cout << "A::Func3()" << endl;
	}
public:
	int _a;
};

class B :public A {
public:
	virtual void Func1()     //构成重写
	{
		cout << "B::Func1()" << endl;
	}
	virtual void Func3()   //构成重写
	{
		cout << "B::Func3()" << endl;
	}
public:
	int _b;
};

在这里插入图片描述

当子类中存在新添加的虚函数信息时,虚表指针指向的空间中又是如何存储的呢?

虚函数表指针所指向的空间中存放的是虚函数的入口地址信息,派生类的虚表生成规则:
(1)先将基类中虚表内容拷贝一份到派生类虚表中;
(2)若派生类重写了基类中某个虚函数,则用派生类自己的虚函数覆盖虚表中被重写的基类的虚函数;
(3)派生类自己新增加的虚函数按照其在派生类中声明次序增加在派生类虚表的最后。

class A {
public:
	virtual void Func1()
	{
		cout << "A::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "A::Func2()" << endl;
	}
	virtual void Func3()
	{
		cout << "A::Func3()" << endl;
	}
public:
	int _a;
};

class B :public A {
public:
	virtual void Func1()     //构成重写
	{
		cout << "B::Func1()" << endl;
	}
	virtual void Func3()   //构成重写
	{
		cout << "B::Func3()" << endl;
	}

	virtual void Func4()
	{
		cout << "B::Func4()" << endl;
	}
	virtual void Func5()
	{
		cout << "B::Func5()" << endl;
	}
public:
	int _b;
};

在这里插入图片描述
在这里插入图片描述

多态的原理

class A {
public:
	virtual void Func1()       //虚函数
	{
		cout << "A::Func1()" << endl;
	}
	virtual void Func2()          //虚函数
	{
		cout << "A::Func2()" << endl;
	}
	void Func3()           //普通函数
	{
		cout << "A::Func3()" << endl;
	}
public:
	int _a;
};

class B :public A {
public:
	virtual void Func1()     //重写
	{
		cout << "B::Func1()" << endl;
	}
public :
	int _b;
};



void Test(A pa)
{   //参数--->传值方式
	pa.Func1();
	pa.Func2();
	pa.Func3();
}

void Test(A* pa)        //形成函数重载
{    //基类指针方式接收参数
	pa->Func1();
	pa->Func2();
	pa->Func3();
}

在这里插入图片描述

分析:

在这里插入图片描述

构成多态的条件:
(1)基类中存在虚函数,子类中重写了基类虚函数;
(2)基类的指针 / 引用 ,可以 指向 / 引用子类的对象

在这里插入图片描述

多态的分类

(1)静态多态-------函数重载
(2)动态多态-------运行时多态,程序运行期间根据具体拿到的类型确定程序的具体行为,调用具体的函数

多继承体系中虚函数存储

多继承中,子类新增加的虚函数地址存储在第一个继承体系的虚表最后

//定义两个不同的类

class B1 {
public:
	int _b1;
	virtual void Func1()
	{
		cout << "B1::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "B1::Func2()" << endl;
	}
};

class B2 {
public:
	int _b2;
	virtual void Func3()
	{
		cout << "B2::Func3()" << endl;
	}
	virtual void Func4()
	{
		cout << "B2::Func4()" << endl;
	}
};
//实现多继承

class D :public B1, public B2
{
public:
	int _d;
	virtual void Func1()      //与 B1 类中虚函数实现重写
	{
		cout << "D::Func1()" << endl;
	}
	virtual void Func4()      //与 B2 类中虚函数实现重写
	{
		cout << "D::Func4()" << endl;
	}
	virtual void Func5()     //子类自己的虚函数
	{
		cout << "D::Func5()" << endl;
	}
};

前边我们介绍到, 子类的虚表建立过程为: (1)将基类中虚表信息拷贝过来;
(2)检查子类中是否存在基类虚函数的重写,若有则需要修改子类虚表中重写的虚函数入口地址; (3)子类自己的虚函数放在虚表最后

因此,我们猜想在多继承体系中也是分为三个步骤:直接将基类虚表拷贝过来,然后检测虚函数重写,添加子类自己的虚函数,接下来我们来验证一下~~

定义两个函数,分别用来获取子类 D 中由继承 B1 , B2 类而来的两个虚表中的信息:

typedef void(*VFT)();    //由于类中虚函数的类型都为 void------->函数指针

//用来获取  D 类中由基类 B1 继承而来的虚表信息
void GetB1Vft(B1& pb)
{
	cout << "由 B1 继承给 D 的虚表信息" << endl;

	VFT* vft = (VFT*)*(int*)(&pb);   //获取虚表指针指向的虚函数入口地址
	while (*vft) {
		(*vft)();  //调用该虚函数
		vft++;
	}
	cout << "====================" << endl;
}

//用来获取  D 类中由基类 B2 继承而来的虚表信息
void GetB2Vft(B2& pb)
{
	cout << "由 B1 继承给 D 的虚表信息" << endl;

	VFT* vft = (VFT*)*(int*)(&pb);   //获取虚表指针指向的虚函数入口地址
	while (*vft) {
		(*vft)();  //调用该虚函数
		vft++;
	}
	cout << "====================" << endl;
}

在这里插入图片描述

由此可见,多继承体系中子类中存在多个虚表信息(由多继承个数决定),并且子类自己新增加的虚函数入口地址存放在第一个继承体系的虚表最后。

小点点:

虚表是在编译时生成的,而并非是运行时生成,但是在运行时调用虚表来实现具体虚函数调用的

虚表是在继承体系中用来存储虚函数入口地址的指针表;
虚基表是在虚拟继承中用来存储偏移量信息的;

ps:

欢迎各位读者评论留言鸭~~


http://www.kler.cn/a/6526.html

相关文章:

  • Idean 处理一个项目引用另外一个项目jar 但jar版本低的问题
  • <项目代码>YOLO Visdrone航拍目标识别<目标检测>
  • Nginx - 负载均衡及其配置(Balance)
  • 探索 Python编程 调试案例:计算小程序中修复偶数的bug
  • Java期末复习暨学校第十三次上机课作业
  • Vue+element 回车查询页面刷新
  • 从零开始学习InfluxDB:安装和使用入门教程
  • C++ using:软件设计中的面向对象编程技巧
  • 分库分表笔记
  • MVVM理解、object.defineProperty、数据代理
  • Android动态换肤框架实现小结
  • 射频接收机概述
  • PyTorch模型保存方法对比及其实现原理详解
  • 大数据领域的发展及其对现实世界的价值
  • BloombergGPT(LLM大模型用于金融科技)
  • Qt源码阅读(三) 对象树管理
  • java程序中常见的漏洞类型
  • 五 MySQL 存储过程
  • [jetson]pillow 报ImportError: The _imagingft C module is not installed
  • Unity游戏逆向及破解方法介绍
  • BUUCTF-逆向[GXYCTF2019]luck_guy1题解
  • day14—编程题
  • JVM垃圾回收——ZGC垃圾收集器
  • 【算法】树状数组维护总结
  • 计算机底层:循环冗余校验码CRC
  • 实验十八、测量运放的开环差模放大倍数