概念
通俗来说就是多种形态;
也就是说 不同对象完成某个(同样)行为会产生不同的结果
例如: 比如买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;
代码实现
虚函数:被virtual修饰的类成员(不能是静态)函数
满足了两个条件才能实现多态
- 构造条件
继承体系下
父类中有虚函数
子类对父类中的虚函数进行了重写(写法:直接将方法原型拷到子类中,改写函数体)- 调用条件
虚函数只能通过父类的引用或者指针调用
多态的体现:程序运行时,根据基类的指针指向不同类的对象,选择对应的虚函数进行调用
如果不通过父类指针或者引用调用虚函数,虚函数就是直接调用的
- 编译过程:
编译阶段,p.bark(),该方法不知道要调用哪个类的方法
编译时,p到底引用的是猫还是狗,是确定不下来的,程序运行时需要传参,才能知道p指向哪个类的对象- 函数的调用过程:56行的注释
重写(重新写方法)
子类中有一个和父类完全相同的虚函数(函数名,返回值,参数列表),则子类虚函数重写了父类虚函数
构成重写的条件
- 父类函数一定要是虚函数(一定要加关键字),
子类可以不加关键字但是最好加上;- 父子类虚函数原型要一致:函数名,参数列表,返回值类型
返回值类型(但是有规则)和函数名不同(只有一种情况)也可能构成重写
例外:协变(返回值类型不同) 和 析构函数重写(函数名不同)- 重写和在父子的权限无关
析构函数重写
将父类的析构函数设置为虚函数
- 作用:防止因子类涉及资源管理造成内存泄漏
- 过程分析:
父类指针指向子类对象时会有资源泄露
new是调用子类构造函数构造对象
delete在析构时,调用的是父类析构函数,导致资源泄露- 为什么可以防止资源泄露
父类指针指向子类对象,调用父类的析构函数,编译器发现父类析构是虚函数,则调用指针所指向对象的虚析构函数
协变
子类和父类虚函数的返回值类型不同,可以构成重写
- 父类虚函数返回父类的指针或者引用
- 子类虚函数返回子类的指针或者引用
返回类型可以不是同一继承体系,如右侧代码
override
C++11提供的关键字
重写的最大限制条件是要保证函数原型一致
函数名不同,在编译阶段是不会报错,只能在运行时候出错
关键字作用:用于修饰子类虚函数,在编译阶段检查子类虚函数是否对父类虚函数进行重写,未重写则报错
final
修饰虚函数:表示类中的虚函数不能被其子类重写
修饰类:表面类不能再被继承
重载、重定义、重写的区别
在继承体系中,如果父类和子类两个 成员函数名相同,不是重写就是重定义
参数列表:参数个数,类型,类型次序
不能作为虚函数的函数
虚函数是为了实现多态,通过虚表进行调用
构造函数
- 构造函数作用是为了初始化对象,给对象空间赋合适的初始值;
- 如果构造函数可以做虚函数,那么其调用就需要在虚表中提取;
- 但是虚表是对象构造完整之后才出现的,自相矛盾
静态成员函数
没有this指针,不能通过对象调用,没有对象就没有虚表;
友元函数
不属于类成员函数
内联函数
从语法层面上可以,被inline修饰函数会在编译阶段尽量被展开,被展开就不能实现多态,virtual关键字就没有意义了