详解多态的实现机制

多态的三种表现形式

多态: 就是多种形态,在完成某个行为时,不同对象完成会产生不同的结果。在函数内部的表现形式便是同一个函数的接口,但是出现的不同的结果。
多态一共有三种形式:
1.静多态,就是在编译阶段确定函数的调用;
2.动多态,在运行阶段才会确定函数的调用。
3.宏多态,在预编译阶段就确定了函数的调用。
而在默认状态下,一般默认都是动多态。而实现动多态,在需要虚函数来提供支持;

多态的实现机制

因为静多态只有在运行阶段才会确定函数的调用,所以我们需要在运行阶段获取到函数的入口地址才能实现多态。但是在程序的编译阶段就确定了函数符号和入口地址。所以普通函数无法实现动多态。因此我们引入了virtual关键字,这个关键字的作用就是让函数的入口地址在.rodata数据段中的虚函数表(vftable)中也存放一份。然后再程序的执行阶段加载指令数据的时候可以把函数的入口地址也能加载到内存中。而虚函数表(vftable)中存放的是有三个信息:

1.RTTI(Run-Time Type Identification)信息,简单来说就是,RTTI信息就是用来确定运行时的类型信息。
2.偏移:指的是虚函数表指针相当于整体作用域的偏移,通常由于虚函数指针的优先级比较高,vfptr在一个对象的内存布局中占据低位值,通常都是0,而偏移就是0-vfptr的位置,所以大多数情况下偏移都是0;
3.函数的入口地址:这个地方存的就是虚函数的入口地址,也是发生多态时的灵魂所在。在后期解析函数的时候,可以找到函数的入口地址,从而实现同一个函数,而执行的结果不同。

由于对象和虚函数表是一种共享的关系,所以生成的对象内部都会自己产生一个vfptr来指向一个虚函数表(vftable)。所以在发生多态的时候,对象的sizeof()都会比原来大四个字节。

实现多态的内存空间布局

如上所述,根据描述,在发生多态之后,对象内部会多一个vfptr指针来指向vftable,而vfptr的优先级又会比较高,所以通过例子可以得到一个对象的内存布局如下:
代码:

class Base
{
public:
	void Base(int ma):_ma(ma)
	{}
	virtual void show()
	{
	std::cout<<"base::show ma="<<ma<<std::endl;
	}
private:
	int _ma;
}
class derive:public Base
{
public:
	void derive(int mb):_mb(mb),Base(mb)
	{}
	virtual void show()    
	{
		std::cout<<"derive show mb="<<mb<<std::endl;
	}
private:
	int mb; 
}
int main()
{
	Base a;
	derive b;
	return 0;
}

根据上面的代码,我们发现基类发生了多态,而派生类也发生了多态。所以我们利用工具可以查到Base的空间布局为:在这里插入图片描述
这进一步验证了上述观点:**发生多态时,对象内部会多一个vfptr;**同样,我们可以利用工具查到虚函数表的结构是:
在这里插入图片描述
这三行分别代表了RTTI信息,偏移量以及函数的入口地址。
注意:在发生继承之后,派生类中的虚函数表会发生变化:
基类的虚函数表结构:
在这里插入图片描述
未发生继承时的派生类虚函数表:在这里插入图片描述
当发生继承关系时, 派生类中的同名同参 虚函数 会 覆盖 基类中同名同参的虚函数在这里插入图片描述

基类和派生类的相互指向和引用

对于基类来说,所定义的一个基类指针是可以指向或者引用派生类对象的。而对于一个派生类指针,却不能指向一个基类对象。这是因为指针访问的时候,基类指针可以访问的到所有基类指针想要得到的信息,而派生类对象指针不能保证基类对象中包含自己所需要的接口或者变量。在这里插入图片描述
所以派生类的虚函数表最终由两部分实现:1.基类虚函数表 2.派生类自己的部分虚函数表。
此外,当发生继承的时候,内存布局也会改变:在这里插入图片描述
注意:虚函数指针会向内合并,也就是从派生类往基类里边合并;

总结

动多态的发生过程如下图:
在这里插入图片描述
先生成虚函数表,然后再画出内存布局中的vfptr来指向虚函数表,然后由基类对象指针pb来指向派生类的对象。从而实现基类指针对派生类函数Derive::show的访问。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值