一、继承的概念
- 继承机制是面向对象程序设计复用代码的重要手段
- 它还允许程序员在保持原有类特性的基础上进行扩展, 这样产生的新的类叫做派生类
- 继承呈现了面向对象程序设计的层次结构, 继承是类设计层次的复用
二、继承的定义
1.定义格式

2.继承关系和访问限定符

1. 公有继承(public)
基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的,不能被这个派生类的子类所访问。
2. 私有继承(private)
基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。
3. 保护继承(protected)
基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。
- 基类中private成员在派生类中无论以什么方法继承都是不可见的, 不可见指基类中的私有成员也被继承到了派生类中, 但无法访问
- 使用关键字class时默认的继承方式是private, 使用关键字struct是默认的继承方式是public, 不过最好在使用的时候写出继承方式, 以便于观察
- 实际运用的时候一般采用public继承, 不提倡使用protetced / private继承, protetced / private继承后的成员只能在派生类中使用, 维护性低
3.基类和派生类对象赋值转换
- 派生类对象可以赋值给基类对象/指针/引用
- 基类对象不能赋值给派生类对象
- 基类的指针可以通过强制类型转换赋值给派生类的指针, 但是可能会存在越界访问问题
三、继承中的作用域
- 在继承体系中基类和派生类都有独立的作用域
- 如果基类和派生类中有同名的成员, 派生类成员将屏蔽基类对同名成员的直接访问, 这种现象叫做隐藏(或重定义),
- 在子类成员函数中, 可以使用 基类::基类成员 显示访问
- 如果是成员函数隐藏, 只需要函数名相同就可以构成隐藏
四、派生类的默认成员函数
1.构造函数
派生类的默认构造函数必须先调用基类的默认构造函数, 如果基类没有默认构造函数, 则必须在派生类的构造函数的初始化列表阶段显示调用
2.拷贝构造函数
派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化
3.operator=
派生类的operator=必须要调用基类的operator=完成基类的复制
4.析构函数
派生类的析构函数会在被调用完后自动调用基类的析构函数, 这样才能保证必须在清理完派生类成员之后, 再清理基类成员的顺序
5.对象的初始化
派生类对象的初始化先调用基类构造再调用派生类构造
6.对象的析构
派生类对象的析构清理先调用派生类析构再调用基类析构
五、继承与友元
友元关系不能被继承, 基类友元不能访问子类私有和保护成员
六、继承与静态成员
基类中如果定义了一个static静态成员, 则整个继承体系中只有一个静态成员
在基类中声明一个静态成员, 系统就会自动将其放到静态数据段中, 基类和对应的子类都可以访问到这个数据, 所有只有一个静态成员
七、菱形继承及菱形虚拟继承
1.菱形继承
菱形继承是多继承的一种特殊情况
两个派生类继承基类, 还有一个派生类继承了之前的两个类, 这样的四个类形成的继承步骤叫做菱形继承

2.菱形继承中的问题
菱形继承会产生数据冗余和二义性的问题
- 数据冗余: 在第一个阶段继承了基类的两个类有相同的基类数据, 如果再次继承, 一个数据被多次继承, 就造成了数据冗余
- 数据二义: 在继承两个派生类的子类中, 如果需要取数据, 就不知道从哪一个父类中创建对象来应用其对应的成员函数
3.菱形虚拟继承
虚拟继承可以解决菱形继承的数据冗余和二义性问题
例如上面的继承关系中, 在Student和Teacher的继承 Person时使用虚拟继承, 就可以解决这两个问题,
给对应的成员函数加上vritual关键字, 使其变为虚拟继承, 此时内部存储就会自动生成对应的虚基表, 存储对应的偏移量, 从而消除数据二义
八、继承与组合
1.继承允许你根据基类的实现来定义派生类的实现, 在继承方式中, 基类的内部细节对子类可见, 这种复用风格被称为白箱复用
优点: 子类能自动继承父类所拥有的接口
创建子类对象无需创建父类对象
缺点: 子类不能改变父类的接口, 但可以对其覆盖
不支持动态继承, 运行时无法选择不同的父类
可以进行扩展, 但会增加系统的复杂度
继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响
派生类和基类间的依赖关系很强,耦合度高
2.对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。
对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用, 因为对象的内部细节是不可见的。
缺点: 整体类不能有和局部类相同的接口
创建整体类的对象时需要创建局部类的对象
优点: 整体类可以对于局部类进行封装形成新的接口使用
支持动态组合, 在运行时可以选择不同的局部类
扩展性能好
组合类之间没有很强的依赖关系, 耦合度低。
优先使用对象组合有助于你保持每个类被封装, 并被集中在单个任务上
实际应用
优先使用对象组合, 而不是类继承, 实际中尽量多去用组合。
组合的耦合度低,代码维护性好。不过继承也有用武之地, 有些关系就适合继承那就用继承, 另外要实现多态, 也必须要继承
继承适用的场景: 一般编码规范都要求类的层次不超过3层
组合适用的场景: 较大型的程序

浙公网安备 33010602011771号