一、多态的概念
多态用一句话说就是执行一个事物会发生不同的情况。
在代码中就是不同继承关系的类对象对一个函数进行操作,会有不同的结果产生。
举个生活中的例子:比如相亲这件事,
① 长得好看并且有钱的人相亲可能百战百胜;
② 长得不好看并且又没钱的人可能就会遭到别人的冷眼且相亲失败(职业法师刘海柱那种人hhh)
这就是不同的人做不同的事得到的结果是不一样的,这就是生活中的多态。
二、多态的定义和实现
想要实现多态,必须有三个前提条件:
1)首先多态的实现是建立在继承关系的基础上。
2)被调用的函数必须是虚函数并且完成了虚函数的重写。(重写的概念可以参见重写概念)
3)调用函数的对象的类型必须是指针或者引用。
class Beautiful_Person
{
public:
virtual void date()
{
cout << "成功牵手" << endl;
}
};
class Ugly_Person : public Beautiful_Person
{
public:
virtual void date()
{
cout << "牵手失败" << endl;
}
};
void func(Beautiful_Person& p)
{
p.date();
}
int main()
{
Beautiful_Person bp;
Ugly_Person up;
func(bp); //传入不同的对象,得到的结果不同
func(up);
return 0;
}
三、多态的原理
那么为什么传入不同的对象会产生不同的结果呢?
我们看一个例子:
有一道笔试题:sizeof(base)是多少?
class Base
{
public:
virtual void func()
{
cout << "func" << endl;
}
private:
int _a;
};
我们都知道,类成员的大小只与成员变量的大小有关。所以按道理应该是 4 个字节的大小。
但是运行出的结果却是 8 个字节,这是为什么呢?
我们可以看下 Base 的对象模型中还有什么我们不知道的东西!
通过调试我们可以看到 Base 的对象模型中多了一个 __vfptr 指针,普通的对象模型中只有成员变量。可以看到这个__vfptr 指针指向的是 Base 类中的那个虚函数。
这个指针其实就是虚函数表指针(v 是 virtual 的意思,f 是function的意思)。这个虚函数指针指向的其实是一个虚函数表,在类中的虚函数指针都要放在这个虚函数表中。虚函数表也叫虚表。
梳理一下虚函数表指针、虚函数表、虚函数指针的关系如下图:
那我们捋一捋多态的原理:
再顺一顺刚才相亲的那段代码,过程如下图:
可以看出不同对象是调用不同的函数的。bp是通过自己对象模型中的虚函数表指针找到对应的虚函数,然后调用它。up 也是一样,只不过他们对象模型中的虚表指针指向的虚表中的内容是不一样的。所以调用的函数也不一样。
所以总而言之:多态的原理就是在执行某个函数时,传进的对象不同,会根据对象模型中的虚函数指针找到对应的虚函数表,在虚函数表中找到对应要执行的虚函数指针,进而找到该虚函数并执行。
四、静态多态和动态多态
1、静态多态
静态多态也叫静态绑定,就是在函数编译时期就确定程序的行为,这种就是静态多态,比如函数重载就是一个静态多态的例子。
2、动态多态
那么动态多态就是在程序运行的过程才会确定程序的具体行为,调用不同的函数。
我们可以通过汇编代码再看看动态多态为什么是运行过程才会确定程序的具体行为:
可以看出是在运行过程中才确定执行的是什么函数。