多态的概念、定义以及实现
多种形态,当不同的对象去执行同一种行为时,产生的不同表现形态
构成条件:在不同的继承关系的类对象,去调用同一函数,产生了不同的行为
- 继承关系
- 必须通过基类的指针或者引用调用的虚函数,一般都是用父类指针/引用指向父类以及子类实体,即都为切片行为
- 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
必须同时满足以上条件,缺一不可
虚函数的重写:重写函数逻辑
构成虚函数重写条件:子类含有和父类接口完全相同的函数(返回值,函数名,参数完全相同),例外:协变,析构函数重写,如果不满足所有条件,但是函数名相同,则构成函数隐藏
协变:返回值类型可以不同,但是返回值类型必须是父子关系
注意:如果父类函数加了virtual
声明,则子类接口完全一致的函数即使不加virtual
也具有虚函数的属性,但是反过来不成立,建议一般对于所有的虚函数都加上virtual
非多态:看类型
多态:看实际指向的实体
析构函数重写:
只要父类的析构函数是虚函数,则子类的析构函数和父类的析构函数构成重写
原因:编译器对继承关系下的所有类的析构函数的名字做了统一处理,保证了继承关系下所有的析构函数同名,一般把析构函数的名称统一处理成
destructor
final
和override
关键字:
final
:修饰虚函数,表示该虚函数不能再被继承,其定义的函数不能被重写,体现实现继承override
:检查派生类虚函数是否重写了某个虚函数,如果没有重写则编译报错,强制子类重写父类的某一个虚函数
抽象类
抽象类:包含纯函数的类,抽象类不能实例化对象
1 | class A |
抽象类存在意义,实现多态,其体现出接口继承
多态原理
虚函数表
1 | class D |
包含虚函数的类:类中包含一个隐含的成员变量,即虚表指针
虚表指针:指向虚表的首地址,类型为二级指针,函数指针的指针
虚表:存放虚函数指针的数组,类型为指针数组
- 只存放虚函数的指针
- 普通函数不会存入虚表
- 如果子类重写了父类的虚函数,则子类虚表中对应位置使用子类虚函数指针覆盖
- 如果子类没有重写父类的任何虚函数,则子类完全继承父类虚表,不做任何修改
- 虚函数指针在虚表中的存放顺序和其声明/定义的顺序一致
- 子类新定义的虚函数,其虚函数指针按照声明/定义的顺序依次加入虚表的末尾
- 虚表一般以
nullptr
结束
只要类中包含虚函数,就会有虚表指针和虚表
- 虚表指针存在对象当中
- 虚表存放在代码段
- 虚函数存放在代码段
多态:看实际指向的实体
多态原理:如果访问的为虚函数,则通过指针/引用找到实际指向的实体,获取实体中的虚表指针,通过虚表指针访问虚表,在虚表中找到需要执行的虚函数指针,通过虚函数指针执行具体函数行为
继承下的虚表
单继承虚表:
- 子类继承父类虚表
- 用重写的虚函数指针覆盖子类对应的虚函数指针
- 子类新定义的虚函数,其指针按照声明/定义顺序存入虚表的末尾
多继承虚表:
- 虚表个数:等同于直接父类的个数
- 子类继承父类所有的虚表
- 用重写的虚函数指针覆盖对应父类的虚函数指针
- 子类新定义的虚函数,其函数指针按照声明/定义的顺序存入第一个直接父类虚表的末尾