C++学习:封装,继承和多态
封装,继承和多态
- 封装
- 类是封装的核心
- 继承
- 继承方式和访问限定符
- 继承规则
- 继承的问题
- 继承还是组合
- 多态
- 编译时多态和运行时多态
- 虚函数
- 纯虚函数和抽象类
- 了解
- 概念理解
封装
类是封装的核心
类通过成员变量和成员函数将数据和功能绑定,通过访问限定符实现访问权限控制,至此每一个类都实现特定功能,都是一个模块,相互解耦。
继承
继承方式和访问限定符
基类的访问限定符和派生类的继承方式共同决定继承得来的成员的访问权限
继承规则
- 隐藏关系
当基类成员和派生类成员同名会构成隐藏关系,基类同名成员会被隐藏
- 派生类对象可以赋值给基类指针/引用/对象
基类对象不可赋值给派生类指针/引用/对象
Student stu;//派生类Person pp=stu;//相当于从派生类对象分割出基类对象Person*p1=&stu;Person&p2=stu;- 派生类构造和析构
派生类构造需要调用基类构造函数,若无默认构造则需要在初始化列表中显示调用(基类拷贝构造需要在初始化列表中调用)
Student(Student&stu):Person(stu){//深拷贝}派生类析构
函数调用顺序:构造时:派生类初始化可能依赖基类成员,需要先初始化基类才能使用基类方法
析构时:派生类析构可能依赖基类成员,需要先进行派生类析构,防止基类先析构导致访问出错
- 友缘关系不可继承
基类的友缘不可直接访问派生类的私有和保护成员
- 基类定义static变量全局唯一
无论有多少派生类,在继承体系中只有唯一一个static实例
继承的问题
- 菱形继承
在多继承场景下,可能会出现这种场景:
此时会出现数据冗余和二义性:1.Assistant会有多个Person对象 2.两个Person类需要确定操作对象
解决方法:
- 采用以下形式进行指定对象(数据冗余为解决)
a.Student::_name="yyy";a.Teacher::_name="xxx";
- 虚继承
classStudent:virtualpublicPerson虚继承会将公共类单独放置,最后调用构造
继承还是组合
继承:内部可见,依赖程度高,耦合度高
组合:内部不可见,依赖程度低,耦合度低
就比如一个基类,有90个私有成员,10个公有成员,如果采用继承,内存中会包含所有基类成员,基类发生修改可能会影响到派生类;组合只会感知到这10个成员,围绕着这10个成员访问,耦合度降低。
多态
编译时多态和运行时多态
编译时多态
函数重载和函数模版(实参传参进行参数匹配是在编译时进行的)运行时多态
对于某一行为(函数),传入不同参数完成不同行为
条件:
1.必须是基类的指针或者引用调用虚函数
2.被调函数必须是虚函数,并且完成虚函数的重写/覆盖
虚函数
虚函数是实现运行时多态的基础,对虚函数进行重写,从而达到多态
virtualvoidfunc(){}//虚函数- 虚函数表和虚函数表指针
对于存在虚函数(virtual)的类,会生成一张虚函数表,虚函数表中会记录所有虚函数地址,同类对象会共用一张虚函数表;派生类会继承基类的虚函数表,并对重写的虚函数进行地址覆盖,添加派生类虚函数地址
虚函数表指针则是指向虚函数表的指针:传入不同参数完成不同实现(多态的实现)是通过调用传入参数的虚函数表指针所指向不同虚表对应函数实现的
注:析构函数在编译时会统一命名为destructor导致派生类析构和基类析构形成隐藏关系,一般需要加上virtual
纯虚函数和抽象类
包含纯虚函数的类为抽象类,纯虚函数一般不需要定义,因为抽象类不可实例化
classConnnect{public:vritualvoidfunc()=0;//函数定义后=0表示虚函数};了解
- 多态控制
override:希望多态重写没有报错(如名字拼写错误)
final:不允许重写虚函数
概念理解
- 重载
1.在同一作用域
2.函数名相同,参数不同,参数个数或类型不同,返回值同或不同
- 重写/覆盖
1.函数分别在基类和派生类两个不同的作用域中
2.函数名,参数,返回值都相同,协变例外
3.函数必须为虚函数
- 隐藏
1.函数分别在基类和派生类两个不同的作用域中
2.函数名相同
3.不是重写就是隐藏
4.基类和派生类的同名成员变量也构成隐藏关系
没有形成多态,同名函数构成隐藏,基类指针只会调用基类成员函数,派生类指针只会调用派生类成员函数
