C++中多态实现的原理

多态是C++面向对象的三大特征之一。
首先说一下多态的分类。
在这里插入图片描述
多态分为静态多态和动态多态。函数重载就是属于静态多态。这个无需多言。

接下来说一下另一种静态多态。
先上一段代码:

#include <iostream>
using namespace std;

class Person
{
public:
	void Gender()	//显示性别
	{
		cout << "性别" << endl;
	}
};

class Man : public Person
{
public:
	void Gender()
	{
		cout << "这是个男人" << endl;
	}
};

void showData(Person & person)
{
	person.Gender();
}

int main()
{
	Man m;
	showData(m);
	system("pause");
	return 0;
}

首先我们看到,showData函数需要的参数是Person的对象,但是我们传入了一个Man的对象,程序仍然成功运行了。运行结果如下:
在这里插入图片描述
我们利用父类指针指向子类对象,这实际上就已经用到了静态多态。
将父类的指针指向子类引用,该种操作更像是一个C语言中的强制类型转换,舍弃了子类所特有的东西,将其强制转换成了一个父类对象。

Man man;
Person p = (Person)man;

所以其只能调用父类中的属性以及方法,无法调用子类中所特有的属性和方法。这种操作一般没有什么意义。

接下来说一下动态多态。
首先我对上面的代码进行了修改

#include <iostream>
using namespace std;

class Person
{
public:
	virtual void Gender()	//显示性别
	{
		cout << "性别" << endl;
	}
};

class Man : public Person
{
public:
	int a = 10;
	void Gender()
	{
		cout << "Man.a = " << a << endl;
		cout << "这是个男人" << endl;
	}
};

void showData(Person & person)
{
	person.Gender();
}

int main()
{
	Man m;
	showData(m);
	system("pause");
	return 0;
}

可以看到,在父类的Gender方法前面加了关键字 virtual ,然后在子类Man中又增加了一个新的属性,并在重写的Gerder方法中调用了这个Man类中特有的属性。运行结果如下:
在这里插入图片描述
主函数中同样是调用showData方法,然后传入的同样是一个子类对象,当时与上面的运行结果不同的是,其调用了子类重写后的Gender方法,并且可以访问子类特有的属性,比如属性a。

接下来就来解释一下这种神奇的现象。

首先我们查看一下Geader方法没有加virtual时类的内存分布是什么样的。
在这里插入图片描述
可以看到。该类中是空的,大小为1是为了占位。
接下来是Man方法;
在这里插入图片描述
可以看到Man类同样是空的

然后再看一看加了virtual之后的类的内存分布。
在这里插入图片描述
可以看到Person类同样多了一个虚函数指针,指向而在他所指向的虚函数表中指向的是Person类中的Gender方法的引用。

接下来是Man类:
在这里插入图片描述
可以看到Person类中多了一个vfptr的东西,名字中带有ptr、而且还是4个字节大小,所以显而易见,他是一个指针,和vbptr(虚基类指针)类似的,这是一个虚函数指针,指向了vftable(虚函数表)。虚函数表中存储了父类Person中Gender方法的引用。

但是如果我们不重写呢??
在这里插入图片描述
同样拥有函数虚函数指针,同样指向虚函数表,但是表内的函数发生了变化,由于子类没有重写父类的Gender方法,所以其指向的是父类的Gender方法

所以,得出如下结论:

  1. 当父类的方法被virtual修饰后,其方法称为虚函数,其内存布局中会多出一个名为vfptr的指针,指向虚函数表。
  2. 子类继承了含有虚函数的父类后,内存中同样会多出一个名为vfptr的指针,指向自己的虚函数表。
  3. 当子类没有重写父类的虚函数时,子类的虚函数表中存储的是父类的虚函数的引用。
  4. 当子类重写了父类的虚函数后,子类的虚函数表中云本存储的父类的虚函数会被替换为子类自己重写后的方法的引用。

这就是动态多态实现的原理:

没有虚函数时,由于子类指针被转换成父类指针,而父类指针又无法指向子类的方法,所以在编译时会将其直接连接到父类的方法。
由于虚函数的存在,编译器不会直接将其链接到父类的虚函数,而是通过vfptr指针的指向来调用相应的方法。
当子类没有重写父类的虚函数时,子类虚函数表中存储的是父类的虚函数,所以会调用父类的方法;
当子类重写了父类的虚函数时,子类虚函数表中的父类虚函数被替换成子类虚函数,所以其会调用子类的虚函数。

所以动态多态实现的连个条件:

  1. 父类中含有虚函数
  2. 子类重写父类中的虚函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值