C++类与对象笔记十四:多态二:多态的底层原理(C++程序员Must掌握)

上一个案例中,我们先把父类中的virtual去掉。来看一下一般情况。

class Animal
{
public:
	void Speak()
	{
		cout << "动物会说话" << endl;
	}
};

此时Animal类只有一个非静态的成员函数不属于类的对象上面。分开存储的。

int main()
{
	cout << "size of the Animal : " << sizeof(Animal) << endl;
	system("pause");
	return 0;
}

此时Animal类类似于一个空类空类默认大小为1字节

验证方式:开发人员命令提示工具

验证方式: 开发人员命令提示工具

此时如果加上virtual:非静态成员函数变成虚函数。

#include<iostream>
using namespace std;


class Animal
{
public:
	virtual void Speak()
	{
		cout << "动物会说话" << endl;
	}
};

int main()
{
	cout << "size of the class Animal : " << sizeof(Animal) << endl;
	system("pause");
	return 0;
}

变成了4个字节。原来Animal类是占一个字节,现在占4个字节。

验证方式:开发人员命令提示工具

验证方式: 开发人员命令提示工具

什么东西占四个字节:int类型、float类型、指针

不论什么类型的指针都占四个字节



底层原理:

父类中增加了virtual之后,类的大小变为了4字节。增加的这个量就是虚函数指针的大小。

vfptr:虚函数(表)指针(virtual function pointer):指针指向虚函数表vftable(Virtual function table)

表的内部,会记录一个虚函数的地址。virtual void Speak函数地址。为了记录函数的地址,还要加上一个作用域:Animal::Speak。

成员函数的函数地址要加上一个作用域。告诉编译器,这是Animal作用域下的一个成员函数。为了记录这个地址,还要在前面加一个取址符号&。

这样虚函数表记录虚函数的入口地址


然后,我们用一个Cat类继承Animal。

先不写重写,看看情况:假设没有重写,就是普通的继承

class Cat : public Animal
{

};

此时父类中的,所有内容全部复制一份过来。 父类中的指针也要拿过来,即:子类中也有一个vfptr指针,指向子类的虚函数表。子类的虚函数表内部记录着虚函数。

验证方式: 开发人员命令提示工具

 但是当发生子类重写之后呢?

class Cat : public Animal
{
public:
	void Speak()
	{
		cout << "小猫喵喵叫" << endl;
	}
};

子类会把子类中虚函数表内容做一个 覆盖 操作。

即把虚函数表内部保存的虚函数入口地址,修改为子类自己重写的函数地址

验证方式:开发人员命令提示工具。
验证方式: 开发人员命令提示工具

父类中的虚函数地址并未改变,修改的只是子类自己重写的虚函数地址。

当父类的指针或者引用指向子类对象时,发生动态多态

Animal& animal = cat;

当赋值发生时,animal指向子类对象(cat或者dog)时,它会去子类中寻找这个重写的虚函数。如果没有重写,那么就执行子类继承的父类的虚函数。

动态多态

当父类指针或者引用指向不同的子类对象时,比如指向Cat或者指向Dog的对象。那么就会动态(碰到哪个子类,就去找哪个子类)去寻找这两个子类的虚函数,如果没有,那就直接执行继承的虚函数。如果有,就多态(每个子类都有不同的继承和重写)的执行各自类别的重写的虚函数。

实现方式:虚函数表指针

这个指针,指向虚函数表:虚函数表中存储的是虚函数的函数入口地址子类会继承这个虚函数表指针,如果子类重写了这个虚函数,那么子类的虚函数表指针就会指向新的虚函数入口地址。(虚函数表指针指向的变更。)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值