导图社区 【CXX程序设计教程】第六章 继承与派生
这是一篇关于【CXX程序设计教程】第六章 继承与派生的思维导图,主要内容有继承与派生的概念、派生类、派生类的构造函数和析构函数、多继承及其二义性问题等。
编辑于2022-12-25 17:22:46 广东Ch6 继承与派生
继承与派生的概念
是从不同角度对同一事物的描述
A派生出B,或B继承了A
A称为父类或基类
B称为子类或派生类
基类和派生类的关系
派生类(子类)是基类的具体化
基类(父类)是派生类的抽象
继承
保持已有类的特性而构造新类的过程
分类
单继承
一个派生类只有一个直接的基类
多继承
某个类的直接基类有2个或2个以上
C++的特点
PS:与多层次继承的区别
派生
在已有类的基础上新增自己的特性而产生新类的过程
派生类
派生类的定义
class 派生类名: 继承方式 基类名 { private: 新增私有成员的描述; public: 新增公有成员的描述; protected: 新增保护成员的描述; };
基类名
必须是一个已经定义了的类
派生类的成员组成
吸收基类成员
构造函数和析构函数除外
需要“自力更生”
新增成员
体现派生类与基类的个性特征
对基类成员进行改造
对基类成员的访问控制方式进行改造
维持现状或访问权限更严苛,通过定义继承方式来确定
定义与基类同名的成员,同名覆盖
继承方式
决定了派生类的成员对其基类的访问权限
派生类的成员或对象对基类成员的访问权限,不仅取决于基类成员本身的访问控制属性,还要受到继承方式的制约
谁更苛刻,就按照谁
公有继承 public
基类中的
public
protected
在派生类中的访问属性保持不变,仍然是public或protected
private
在派生类中是不可见的
注意区分
不可访问成员和私有成员
派生类的成员与派生类的对象
类的成员可以访问所有成员
类的对象只能访问公有成员
保护继承 protected
基类中的
public
protected
都成为派生类的保护成员
private
在派生类中是不可访问
派生类的成员可直接访问基类的public成员和protected成员
派生类的对象无法直接访问基类的任何成员
从派生类再往下派生新的类时,保护继承和私有继承就有区别了
既实现了数据隐藏,又便于继承,实现代码重用
私有继承 private
未说明时默认继承方式
基类中的
public
protected
都变为派生类的private成员
private
不可访问成员
在派生类中不可访问
只能“传递一代”,基类成员无法在进一步的派生中发挥作用
派生类的成员可直接访问基类的public成员和protected成员
派生类的对象无法直接访问基类的任何成员
派生类的构造函数和析构函数
定义构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化,自动调用基类构造函数完成
派生类的构造函数需要给基类的构造函数传递参数
派生类的构造函数
基类→ 内嵌→ 新增成员
最简单的派生类的构造函数
派生类构造函数名(总参数列表):基类构造函数名(参数列表) { 派生类中新增数据成员初始化语句 };
创建派生类的对象时构造函数的执行顺序
先调用基类的构造函数,再执行派生类构造函数
有内嵌对象的派生类的构造函数
派生类构造函数名(总参数表列):基类构造函数名(基类参数表列),内嵌对象名(内嵌对象参数) { 派生类中新增数据成员初始化语句;}
构造函数的执行顺序与派生类构造函数的初始化列表中的排列次序无关
多层派生的派生类的构造函数
类A派生出类B,类B又派生出类C时,形成 “多层派生”
每一个派生类仅负责给它的“顶头上司”准备参数,不需要给间接基类准备参数(虚基类的情况除外)
主函数调用派生类时,按照继承的顺序依次调用其基类构造函数
#include<iostream.h> class A1 { public: A1(int i){ cout<<"A1 "<<i<<endl; } }; class A2 { public: A2(int j){ cout<<"A2 "<<j<<endl; } }; class A3 { public: A3(){ cout<<"A3 "<<'*'<<endl; } };
class B:public A2,public A1,public A3 //按照继承的顺序调用基类的构造函数 { private: A3 memberA3; A2 memberA2; A1 memberA1; int z; public: B(int x,int y,int m,int n): A1(x),A2(y),memberA1(m),memberA2(n) { z=x+y+m+n; cout<<"B "<<z<<endl; } }; void main() { B object(1,2,3,4); }
输出结果: 结果: A2 2 A1 1 A3 * A3 * A2 4 A1 3 B 10
派生类的析构函数
派生类的析构函数的定义方法与一般(无继承关系时)类的析构函数相同
系统会自动调用基类的析构函数
析构函数的调用次序与构造函数正好相反
派生类新增数据成员的清理
内嵌对象数据成员的清理
基类数据成员的清理
多继承及其二义性问题
多继承
指派生类的直接父类多于一个
定义格式
class 派生类名: 继承方式1 基类名1,继承方式2 基类名2,...... { private: 新增的私有数据成员和函数成员的描述; public: 新增的公有数据成员和函数成员的描述; protected: 新增的保护数据成员和函数成员的描述; };
二义性
造成对基类中某个成员的访问出现歧义
如果派生类的成员与基类的成员同名,遵循“同名覆盖”的原则
不会产生二义性
在派生类中使用的某个成员名在多个基类中出现,而在派生类中没有重新定义
可能会产生二义性
如果不加限制条件,编译系统无法区分使用的是哪一个基类的同名成员
解决方法
通过"类名::"指出访问的是哪一个基类中的成员
<对象名>.<基类名>::<数据成员名>
<对象名>.<基类名>::<函数成员名>(<参数表>)
在类中定义同名成员
缺点
数据的冗余问题
多个副本导致维护数据一致性很困难,增加了程序出错的几率
同名覆盖原则
当派生类与基类中有相同成员时,即使两个函数的参数不同,从基类继承的同名函数也会被屏蔽
虚基类
·一个类不能多次继承同一个类 ·二义性检查先于访问控制权限或类型检查,所以访问控制权限不同或类型不同并不能解决二义性问题
虚基类
声明格式
class 派生类名: virtual 继承方式 基类名
在“1-X-1”型(“橄榄型”)的多继承中,可使用虚基类
虚基类及其派生类的构造函数
派生类必须向虚基类的构造函数传递参数
在成员初始列表中列出对虚基类构造函数的调用
构造函数的执行次序有变化
虚基类→非基类→该派生类
初始化列表中出现对虚基类和非虚基类的调用
虚基类的构造函数先于非虚基类的构造函数
初始化列表中有多个虚基类
按照定义派生类时出现的先后次序调用
虚基类的构造函数不会被多次调用
只有最远派生类的构造函数才调用虚基类的构造函数
建立对象阿类为最远派生类
该派生类的基类中所列出的对这个虚基类的构造函数的调用在执行中被忽略
基类与派生类的赋值兼容
赋值兼容
不同类型数据间的自动转换和赋值的能力
基类与派生类的赋值兼容规则
在需要基类对象的任何地方都可以使用公有派生类的对象来替代(三种情况)
原因
通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员,而且所有成员的访问控制属性也和基类完全相同。所以,公有派生类实际就具备了基类的所有功能
三种替代
派生类的对象可以赋值给基类对象
派生类的对象可以初始化基类的引用,或者说基类对象可以作为派生类对象的别名
派生类对象的地址可以赋给指向基类的指针
派生类对象可以当作基类的对象使用,只能使用从基类继承下来的成员,而不能使用那些派生类新增的成员和功能
用途
自动进行类型转换
在基类对象出现的场合使用派生类对象代替
使用多态的基础(第7章)
总结
派生类对象向基类对象赋值时,舍弃派生类新增成员,只对基类对象的数据成员赋值
基类与派生类的赋值关系是单向的。只能用派生类对象对其基类对象赋值,反之不行
同一基类的不同派生类的对象之间不能相互赋值,即不是赋值兼容
具体对象可以向其上层抽象对象赋值,而新增的数据成员无法存放,被自动丢弃;不同的类的对象之间不能互相赋值(包括有共同父类的兄弟之间)