1.多态的概念
多态通俗来说就是指多种形态,具体来说就是不同对象完成同一个行为产生的状态不同。比如不同动物完成“发出叫声”这一行为,狗会发出“汪”,猫会发出“喵”,不同的动物会发出不同的叫声,此即多态。
2.多态的定义及实现
多态的实现需要满足两个条件:
1.子类完成父类虚函数的重写。
2.由父类指针或引用调用虚函数。
2.1 虚函数
被virtual关键字修饰的成员函数称为虚函数。
2.2 虚函数的重写
子类重新写一个与父类虚函数函数名,返回值,参数完成相同的函数即完成了父类虚函数的重写。
2.3 多态的实现
这里首先子类完成了对父类虚函数的重写,第一次我们用父类指针指向子类对象,调用的虚函数为子类重新实现的虚函数,第二次我们用父类指针指向父类对象,调用的虚函数为父类的虚函数,这样我们就实现了“不同对象完成同一个行为产生的状态不同”。
注意:子类的virtual关键字可以省略。
2.4 虚函数重写的两个特例
2.4.1 协变(返回值不同)
如果父类虚函数返回值为父类指针或引用,子类虚函数返回值为子类指针或引用,这种情况称为协变。其他情况不允许返回值不同。
2.4.2 析构函数的重写
看下面的例子:
再上述例子中,会产生内存泄漏,因为用delete处理父类指时只会调用父类的析构函数,尽管它指向子类对象,但因为析构函数不是虚函数,没有实现多态。
因此如果要解决这个问题,我们建议将析构函数变为虚函数,加上virtual关键字,这里C++底层编译时统一将析构函数的名字变为destruct,以满足实现虚函数的条件。
2.5 override与final
2.5.1 final关键字
如果想要该虚函数不能被重写,只需在函数结尾加上final关键字即可。
这里我们的父类虚函数使用了final,所以该虚函数不能被重写,子类重写就会报错。
2.5.2 override关键字
如果想要检查子类的虚函数是否完成了某个父类虚函数的重写,在使用子类虚函数后override即可。
2.6 重载、隐藏,重写的对比
3.抽象类
存在纯虚函数的类称为抽象类。
3.1 纯虚函数
在虚函数后面加“=0”即构成纯虚函数。
3.2 抽象类的特点
抽象类不能实例化出对象。
如果抽象类的子类仍没有实现纯虚函数,那么该子类也是抽象类。
4.多态的原理
4.1 虚函数表
一个类的虚函数的地址会存进虚函数表(虚表)里面,如果一个类存在虚函数,那么该类会添加一个虚函数表指针,指向虚函数表。虚函数表本质是一个指针数组。
正常来说,类A的大小应该为8,但是这里却是16,正是因为A存在虚函数,使用A中会添加一个虚函数表指针。
这里的_vfptr就是虚函数表指针。
如果该类存在子类,那么子类也会有一个虚函数表,对父类的形成覆盖。
4.2 多态实现的原理
本质上就是通过父类指针指向的对象的虚函数表来调用对应的虚函数。这里的函数调用不是在编译时确定的,而是运行后在对象中找取。