【C++】多态的底层原理

前言

上篇博客,我们学习了实现多态的两个条件

  1. 父类的指针或引用接收子类对象
  2. 子类重写父类的虚函数

还有,final,override,纯虚函数,抽象类等相关知识。
本篇我们将学习多态实现的底层原理
话不多说,马上开始今天的学习

在这里插入图片描述

一. 虚表指针

我们先来做一个小题

下面程序的运行结果是什么?也就是这个Base类的大小是多少?

class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1" << endl;
	}


private:
	int _b = 1;
	char _ch;
};

int main()
{
	cout << sizeof(Base) << endl;
	Base b;

	return 0;
}

答案是:12
在这里插入图片描述
根据内存对齐的规则不是应该是8吗?怎么会是12呢?这是因为该类还有一个虚表指针
在这里插入图片描述

_vfptr就是虚表指针,virtual function table pointer,虚函数表指针

二. 多态原理

只有当一个类有虚函数的时候,才会有虚表指针。

class Person
{
public:
	void BuyTicket()
	{
		cout << "买票-全价" << endl;
	}
};

class Student:public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "买票-半价" << endl;
	}
};

void func(Person& people)
{
	people.BuyTicket();
}



int main()
{
	Person Tom;
	func(Tom);

	Student Jerry;
	func(Jerry);

	return 0;
}

在这里插入图片描述
Person::Tom内没有虚函数,内部没有虚表指针
Student::Jerry内有虚函数,内部有虚表指针


在这里插入图片描述
我们首先看到,父类和子类都有虚函数,子类重写了父类的虚函数,在监视窗口,二者都有各自的虚表指针,并且虚函数表内部内容不同
PS:虚函数表是在编译的时候就产生的
父类存储父类虚函数
子类存储子类虚函数


接下里,我们查看实现多态的父类调用BuyTicket函数的汇编指令和没有实现多态的汇编指令
在这里插入图片描述

我们发现,满足多态时,虚函数的调用汇编指令变多了很多
这是因为满足多态与否,直接导致函数的编译不同
当不满足多态时,编译器直接调用函数的地址
满足多态时,编译器需要通过虚表找到相应函数的地址

这就是为什么子类需要重写父类的虚函数,只有重写了,才能有不同的内容
没有重写,子类虚表中的虚函数地址就还是父类的虚函数
在这里插入图片描述


接下来,我们说明为什么需要父类的指针或者引用。

因为多态是让不同的对象使用多名接口,但却有不同的表现/内容
我们上述也提到,满足多态的父子类,实际调用函数,是通过虚表指针和虚表完成调用的。
使用父类指针或引用指向子类,会发生切片
在这里插入图片描述
父类指针或引用只是指向子类的父类部分,在多态里,那部分当然也包括虚表指针这样在通过父类指针或引用调用函数时,实际函数通过子类的虚表指针和虚表,所以调用的子类重写的虚函数
而使用父类对象,也会发生切片,但这个切片是将子类的父类部分单独拿出来赋回父类,但其中不包括子类的虚表指针和虚表,因为虚表指针和虚表是类共有的,不会因为赋值兼容而改变

三. 总结

  1. 总结一下子类的虚表生成:a. 先将父类中的虚表内容拷贝一份到子类的虚表中。b. 如果子类重写了父类的中的某个虚函数,就用子类自己的函数覆盖虚表中父类的虚函数 c. 子类自己新增加的虚函数按其子类中的声明次序增加到子类虚表的最后
  2. 虚函数存在哪?虚表存在哪?

在进程中,有代码段,数据段(静态区),栈,堆等。
调用的函数,main函数等会在栈区进行压栈,动态申请的空间是在堆中申请。
代码段中存储的其实是代码转汇编再转二进制的那些二进制
虚表全称虚函数表,虚表指针全称虚函数表指针
虚表本质是一个虚函数指针数组,内部存储虚函数指针,不是虚函数,虚函数和普通函数一样,都存储在代码段,只是其指针在虚表中。
另外对象存储的也不是虚表,存的是虚表指针虚表实际也存储在代码段

  1. 同类的对象都共用一张虚表

总结语

本节博客仅学习多态的底层原理,和一些总结性的知识(三. 总结)
一些证明在下一篇C++的博客,敬请期待。

如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值