[C++] 多态的使用分析: 多态使用相关问题、协变、析构函数的多态、final与override关键词、抽象类分析...
什么是多态?
多态的使用
- 必须是父类的指针 或 父类的引用 来调用 虚函数
- 被调用的函数必须是 虚函数,并且 此 虚函数 必须被 重写
virtual
,用于 解决菱形继承的数据冗余和二义性的问题,将菱形继承改为 菱形虚拟继承
不仅是虚拟继承,虚函数 也是使用 关键字virtual
定义的buyTicket
virtual
,函数名前加 virtual
的函数被称为虚函数
父类中的虚函数,在子类中 如果存在 同名、同返回值类型、同参数的函数,则 构成函数重写,而不构成隐藏什么是重写?
C++ 类成员函数中 加
virtual
的函数被称为 虚函数 如果这个类存在子类,且其子类中 存在与父类中的虚函数 函数名、函数参数、函数返回值类型 都相同 的函数,则称子类重写了父类的虚函数,或 父类虚函数被重写即 重写 是父类虚函数与子类函数的关系,且如果想要构成重写,这需要满足两个必要条件:
- 父类中的函数必须是虚函数,即必须有
virtual
- 子类中的函数 与父类中 虚函数的函数名、函数参数、函数返回值类型 都相等
必须同时满足这两个条件,则称 子类重写了父类的虚函数
子类中的函数 不写
virtual
也同样构成重写,但是建议写上可读性比较强
Person
类中的成员函数 virtual void buyTicket()
分别被 Student
、Elderly
、Soldier
类中的 virtual void buyTicket()
重写 了BuyTicket(Person& per)
使用 父类引用作为参数,在函数体内调用成员函数 buyTicket
相关问题
问题1:如果只是父类对象可不可以多态调用呢?
答:不可以。
示例:
如果 父子类虚函数构成重写,但 使用父类对象调用虚函数,则不构成多态
问题2:如果虚函数不构成重写 构不构成多态?
答:不构成。
示例:
如果 父子类函数之间不构成重写,即使使用 父类指针或父类引用,也是不构成多态的
问题3:为什么子类重写父类的虚函数时,不加 virtual 依旧是虚函数
答:因为在继承体系中,子类已经继承了父类虚函数的接口部分(函数名、函数返回值、函数参数列表等,包括virtual),所以子类中重写父类的虚函数不加virtual也可以
这也说明了,子类重写父类的虚函数时,父类虚函数对于子类来说属于 接口继承
也仅限于重写的时候是接口继承
协变
- 当父类虚函数的返回值类型是父类指针时,子类虚函数返回值类型可以是子类指针,同样构成重写
- 当父类虚函数的返回值类型是父类引用时,子类虚函数返回值类型可以是子类引用,也构成重写
析构函数的多态
~类名()
,怎么对析构函数进行重写呢?~destructor
**,并且 解释说这是为了多态的使用~destructor
为多态做准备C++11: final、override
final
和 override
final
:意为最终,作用也非常的简单:添加在 虚函数函数名之后,可以禁止此虚函数被重写,即表示 此函数已经是最终的函数不能再改变
override
override
的作用,则是 用于子类的虚函数 检查此虚函数是否完成了对父类虚函数的重写,若没有完成重写,则报错
抽象类
=0
那么这个虚函数就变成了纯虚函数继承了抽象类的类,也就继承了抽象类的纯虚函数,所以 抽象类的子类也无法实例化对象
一个纯虚函数的函数内容是无意义的
因为 包含纯虚函数的类 不能实例化对象,其子类也得重写纯虚函数才能实例化对象
所以 纯虚函数的函数内容是无意义的,这一点更加说明了 父类虚函数被继承且重写时,对子类来说是接口继承
接口继承分析
关于接口继承有一题,可以验证对接口继承的理解:
这段代码的输出结果是什么?
答案是:
B->1
如果没有对类的继承、多态、接口继承没有一个明确清晰的了解,这一题是不容易分析出来的
分析:
首先,B类 new了一个对象,并将其指针给了 p(B类指针),即 p 为指向B类对象的B类指针
所以,
p->test();
调用的是 B类对象的A类部分的成员函数,且A类中的虚函数已被重写进入
test()
函数时,this
指针是A*
类型 的,指向的是B类中A类的部分:所以
p->test()
内部调用func(); 其实是 this->func();
而此时
this是A*类型
的,在 B类对象中 使用 A* 调用构成重写的函数是什么?多态调用所以应该执行的语句是:
cout << "B->" << val << endl;
那么问题又来了,为什么调用B类重写的的
func
,val 为 1这就是 接口继承 的体现了
多态调用构成重写的函数,编译器将其认定为 接口继承 即 编译器认定
virtual void func(int val = 1)
被继承到了B类中,所以此函数构成重写的前提下,修改此函数的部分内容是无效的,因为编译器已经认定了接口的组成所以,在多态调用此函数时,其实是:
而不是:
C++ 这样设计真的离谱!!!
而 如果是正常的调用 B类的
func()
而不通过多态调用,又会正常调用函数:是因为正常调用,编译器会只考虑B类本身的内容,即使A类虚函数被重写了,但是没有多态调用 编译器不会将其认定为 接口继承,而是 认定为 实现继承。
所以就 以
调用
作者: 哈米d1ch 发表日期:2022 年 7 月 26 日