多态详细解读
- 多态的概念
- 多态的构成条件
- 接口继承和实现继承:
- 多态的原理:
- 动态绑定和静态绑定
- 多继承中的虚函数表
多态的概念
-通俗的来说:当不同的对象去完成某同一行为时,会产生不同的状态。
多态的构成条件
- 必须通过基类的指针或者引用调用
虚函数
1 - 虚函数的重写2 – 三同(函数名,参数,返回值)
EG:
函数重写的两个例外:
- 协变(基类与派生类虚函数返回值类型不同):
但是仅仅支持返回各自的对象的指针或者引用。- 析构函数的重写(基类与派生类析构函数的名字不同):
虽然析构函数的函数名不同,但是编译器会做特殊的处理,统一处理成destructor(前提是基类的析构函数加了virtual)
接口继承和实现继承:
- 普通函数的继承叫做实现继承,虽然连函数名和参数等等也全部继承,但是主要是继承实现,且派生类可以直接使用。
- 虚函数的继承是接口继承,为了实现重写(重写实现操作)
到这里可能有些小伙伴会不太理解
接口
是什么,这里我们用一个例子来解释:
问:*p->test( )此时打印出来的结果是什么呢?
是不是第一眼就觉得是B->0呢?我们来看看解析:
多态的原理:
- 首先我们先要理解
函数虚表
这个概念,函数虚表里面存放着虚函数的地址。(为了描述方便,下面都称之为虚表) - 当满足多态的两个条件之后:如果传给基类的指针的是子类对象的地址,那就会去调用子类的虚表里面重写之后的对应函数,如果传的是基类对象的地址,那么就会去基类自身的虚表里面找对应的函数。
图例:
这时候有同学就要问了:这个基类的虚表和子类的虚表是同一张吗?虚表到底有几张?
-
只要有虚函数,那么对于各种类,他们都有各自的虚表。并且如果派生类(既然叫做派生类,说明是继承了某个类作为基类)中有实现虚函数
fun1
的重写,那么就会在自身的虚表中覆盖掉从原来基类中的继承下来的fun1
的实现(虚函数是接口继承,原函数的实现被覆盖),正因如此才能实现多态。 -
所以基类和子类的虚表不是同一张,每个类都有自己的虚表
动态绑定和静态绑定
- 静态绑定就是在程序编译期间,就明确了程序的行为。比如函数的重载
- 动态绑定也叫做后期绑定,就是在程序运行期间,根据传过来的类型去
*确定具体的行为*
,比如上述的不同的身份去购买车票会。 细节点
:虚表是在编译的时候就生成了,但是对象中只存储指向虚表的指针(由于虚表实际叫做函数指针数组,那么虚表的指针就叫做函数指针数组的指针,这个指针
是在对象执行构造函数
的时候生成的。)
多继承中的虚函数表
这里主要清楚:对于多继承的派生类,其本身未重写的虚函数放在其第一个继承的父类部分的虚表中。(但是这里要意识到大的来看此处仍然是在派生类的虚表,是派生类虚表中所继承的父类的虚表部分。)
虚函数:被virtual修饰的成员函数就称为虚函数 ↩︎
虚函数的重写:虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。 ↩︎