以下内容源于慕课网的学习整理,如有侵权,请告知删除。
1、多态的定义
- 简单理解,就是对于同一条命令,不同对象会做出不同的操作。
- 相同对象收到不同消息,或者不同对象收到相同消息时,产生不同的动作。
2、多态的分类
(1)静态多态(早绑定)
- 如函数重载,在编译阶段,(根据传参情况)就已经知道要执行哪个函数。
(2)动态多态(晚绑定)
a、虚函数
多态具体到语法上是
- 父类和多个子类有同名函数;
- 定义了父类指针并指向了子类对象,然后通过父类指针,调用子类中的同名函数,而非父类中的同名函数。
- 动态多态,即父类调用子类的实现方式。
实现多态,需要将父类中的同名函数定义为虚函数
- 虚函数,即在成员函数前(如析构函数前、普通成员函数前)添加关键字virtual。
下面不是动态多态,因为main中调用的都是父类中的函数
为了实现调用子类中的同名函数,需要在父类的同名函数前添加关键字virtual,(建议在子类的同名函数前也添加virtual,但不是必须的)
b、虚析构函数
多态中存在的问题是内存泄漏问题,解决方法是虚析构函数。
- 使用父类指针,去销毁子类对象时,会出现内存泄漏的问题。
- 因为delet p(p是父类指针)时,执行的是父类的析构函数;
- 而我们希望执行的是子类的析构函数。
- 解决这个问题的方法是,在父类的析构函数前添加virtual,即虚析构函数。
- 实际上,执行完子类的虚析构函数就会执行父类的析构函数。
c、virtual在函数中的使用限制
- 普通函数不能是虚函数,必须是类的成员函数;
- 静态成员函数不能是虚函数,如 virtual stact int getCount() 是错误的;
- 内联函数不能是虚函数;
- 构造函数不能是虚函数。
3、虚函数和虚析构函数的实现原理
(1)首先知道函数指针这个概念。
(2)虚函数表
- 当父类有虚函数时,并且实例化一个父类对象时,会有一个虚函数表指针,指向了一个虚函数表。
- 通过虚函数表得到虚函数指针,从而得到虚函数的入口地址。
- 子类也有一个虚函数表,子类的虚函数表的首地址,一般和父类虚函数表的首地址不一样;
- 假如子类中没有定义和父类同名的虚函数(如下),那么子类从父类中继承了虚函数,因此子类的虚函数表中的虚函数指针指向同一个虚函数入口。
- 假如子类中定义了和父类同名的虚函数(如下),那么子类的虚函数将覆盖父类的虚函数。
(3)虚析构函数的实现原理
- 前提(已知),执行完子类的虚析构函数就会自动执行父类的析构函数。
- 子类中有虚析构函数(如果父类的析构函数用virtual修饰的话,则子类的虚构函数无论是否用virtual修饰,都是虚析构函数);
- 父类指针通过子类的虚函数表指针,得到虚析构函数的入口地址,执行完子类的虚析构函数后,会自动执行父类的析构函数。
(4)证明虚函数表指针的存在?
(5)细节
- 在c++中,多态的实现是通过虚函数表实现的;
- 每个类只有一份虚函数表,所有该类的对象共用同一张虚函数表;
- 两张函数表中的函数指针可能指向同一个函数。