外观
14.父子间的冲突、同名覆盖引发的问题、多态的概念和意义
约 1572 字大约 5 分钟
继承C++编程语言个人
2022-06-16
四十七、父子间的冲突
1、思考:
子类中是否可以定义父类中的同名成员? 可以
如果可以,如何区分?如果不可以,为什么? 可以
2、编程实验:同名成员变量

3、父子间的冲突
- 子类可以定义父类中的同名成员
- 子类中的成员将隐藏父类中的同名成员(同名覆盖)
- 父类中的同名成员依然存在于子类中
- 通过作用域分辨符( :: )访问父类中的同名成员
4、访问父类中的同名成员

5、编程实验:同名成员变量深度分析

6、再论重载
- 类中的成员函数可以进行重载
- 重载函数的本质为多个不同的函数
- 函数名和参数列表是唯一的标识
- 函数重载必须发生在同一个作用域中
7、问题
子类中定义的函数是否能重载父类中的同名函数?
8、编程实验:父子间的函数重载

9、父子间的冲突
- 子类中的函数将隐藏父类的同名函数(同名覆盖)
- 子类无法重载父类中的成员函数(作用域不同)
- 使用作用域分辨符访问父类中的同名函数
- 子类可以定义父类中完全相同的成员函数
10、小结
- 子类可以定义父类中的同名成员
- 子类中的成员将隐藏父类中的同名成员(同名覆盖)
- 子类和父类中的函数不能构成重载关系(作用域不同)
- 子类可以定义父类中完全相同的成员函数
- 使用作用域分辨符访问父类中的同名成员
四十八、同名覆盖引发的问题
1、父子间的赋值兼容
- 子类对象可以当作父类对象使用(兼容性)
- 子类对象可以直接赋值给父类对象
- 子类对象可以直接初始化父类对象
- 父类指针可以直接指向子类对象
- 父类引用可以直接引用子类对象
2、编程实验:子类对象的兼容性

3、父子间的赋值兼容
- 当使用父类指针(引用)指向子类对象时
- 子类对象退化为父类对象
- 只能访问父类中定义的成员
- 可以直接访问被子类覆盖的同名成员
4、特殊的同名函数
- 子类中可以重定义父类中已经存在的成员函数
- 这种重定义发生在继承中,叫做函数重写
- 函数重写是同名覆盖的一种特殊情况

5、思考:当函数重写遇上赋值兼容会发生什么?
6、编程实验:赋值兼容的问题

7、父子间的赋值兼容
- 问题分析
- 编译期间,编译器只能根据指针的类型判断所指向的对象
- 根据赋值兼容,编译器认为父类指针指向的是父类对象
- 因此,编译结果只可能是调用父类中定义的同名函数

在编译这个函数的时候,编译器不可能知道指针p究竟指向了什么。
但是编译器没有理由报错。于是,编译器认为最安全的做法是调用父类的print函数,因为父类和子类肯定都有相同的print函数。
8、问题
编译器的处理方法是合理的吗?是期望的吗?
9、小结
- 子类对象可以当作父类对象使用(赋值兼容)
- 父类指针可以正确的指向子类对象
- 父类引用可以正确的代表子类对象
- 子类中可以重写父类中的成员函数
四十九、多态的概念和意义
1、函数重写回顾
- 父类中被重写的函数依然会继承给子类
- 子类中重写的函数将覆盖父类中的函数
- 通过作用域分辨符( :: )可以访问到父类中的函数

2、多态的概念和意义
面向对象中期望的行为
- 根据实际的对象类型判断如何调用重写函数
- 父类指针(引用)指向
- 父类对象则调用父类中定义的函数
- 子类对象则调用子类中定义的重写函数
面向对象中的多态的概念
- 根据实际的对象类型决定函数调用的具体目标
- 同样的调用语句在实际运行时有多种不同的表现形态

- C++语言直接支持多态的概念
- 通过使用virtual关键字对多态进行支持
- 被virtual声明的函数被重写后具有多态特性
- 被virtual声明的函数叫做虚函数
3、编程实验:多态的初体验

4、多态的概念和意义
多态的意义
- 在程序运行过程中展现出动态的特性
- 函数重写必须多态实现,否则没有意义
- 多态是面向对象组件化程序设计的基础特性
理论中的概念
静态联编
- 在程序的编译期间就能确定具体的函数调用
- 如:函数重载
动态联编
- 在程序
- 如:函数重写
5、实例分析:动态联编与静态联编

6、编程实验:江湖恩怨

7、小结
- 函数重写只可能发生在父类与子类之间
- 根据实际对象的类型确定调用的具体函数
- virtual关键字是C++中支持多态的唯一方式
- 被重写的虚函数可表现出多态的特性
