什么是多态?
多态就是多种状态,就是在完成某个行为时,当不同的对象去完成时会有不同的状态。
举个栗子:买票的时候,普通人买的是全票,学生买的是半票。
多态的实现
构成多态的条件
多态是在不同的继承关系的类对象,调用同一的函数,产生的不同行为。
- 被调用的函数必须是虚函数,并且完成了虚函数的重写;
- 调用函数的对象必须是指针或者引用。
为什么调用函数的对象必须是指针或者引用?
答:因为如果不是指针或者引用,它会创建一个新的对象,就不是在同一个对象中操作了。调用函数的对象是指针或者引用是在同一个对象中,根据指针或引用来确定具体调用哪个类的虚函数。
什么是虚函数?
虚函数就是在类的成员函数前面加virtual
关键字。
虚函数的意义就是实现多态。
class Person
{
public:
virtual void BuyTicket()
{
cout<<"买全票"<<endl;
}
};
纯虚函数(接口继承的体现)
在虚函数后面加上 = 0,这个函数就是纯虚函数了。 包含纯虚函数的类叫抽象类(也叫接口类),抽象类不能实例化对象,派生类继承后也不能实例化对象,只能重写了纯虚函数才能实例化出对象。
class Car
{
public:
virtual void Drive() = 0;
};
通过看一些书和一些博客,都建议在实际的应用中多使用纯虚函数 + override的方式来强制重写虚函数
class Car{
public:
virtual void Drive(){}
};
// override 修饰派生类虚函数强制完成重写,如果没有重写会编译报错
class Benz :public Car {
public:
virtual void Drive() override {cout << "Benz-舒适" << endl;}
};
什么是虚函数的重写(覆写)?
派生类中有一个和基类 完全相同 (返回值、参数、函数名都相同) 的虚函数,我们称子类的虚函数重写了基类的虚函数
class Person {
public:
virtual void BuyTicket()
{
cout << "买票-全价" << endl;
}
};
class Student : public Person {
public:
virtual void BuyTicket()
{
cout << "买票-半价" << endl;
}
};
重载、重写(覆写)、重定义的区别
多态的原理
在理解多态原理之前,我们先要知道什么是虚函数表,什么是虚表指针?
因为多态的底层是通过虚函数表实现的
虚函数表:(只存放虚函数指针)函数指针数组
虚表指针(_vfptr): 存放在对象模型中,在32位程序下,存放在对象模型的前4个字节
多态调用的步骤:
- 从指针或者引用的具体对象中获取虚表的指针
- 传递this指针
- 虚表中获取虚函数的地址
- 调用虚函数
这里从反汇编看看多态的调用过程,便于理解多态的原理
单继承和多继承关系的虚函数表、
单继承虚表:
函数指针按声明顺序存放,先是存放来自于基类的函数指针,然后子类自定义的函数指针依次存放。
多继承虚表:
虚表个数是直接父类的个数,子类自定义的函数指针存放在第一个直接父类的虚表中