导图社区 继承(inhert)
这是一篇关于继承(inhert)的思维导图,主要内容包括:多级继承和多重继承,继承过程中的特殊成员函数,子类继承父类的成员,继承步骤,继承格式,承的引入。
编辑于2024-09-25 19:27:56继承(inhert)
面向对象的三大特征:封装、继承、多态 继承:所谓继承,就是基于一个已有的类,去创造一个新类的过程,他表述了类与类之间的关系。
承的引入
1> 程序员有时候定义类的过程中,会使用到其他类中的所有属性和行为。如果再重新写这些属性和行为。会显得程序非常的冗余拖沓。例如:定义学生类的时候,很多属性和行为都跟人这个类中重复。此时我们就引入了继承
2> 继承是多态的必要条件,没有继承就没有多态
继承格式
class 子类名 : 继承方式 父类名1, 继承方式 父类名2 .。。。 { 子类扩充成员; }; 举个例子: class B: A { };
1> 一个类B继承自类A:我们称类A为父类、基类
类B为子类、派生类
2> 继承方式有三种:public、protected、private 继承方式的作用:父类中的不同访问权限下的成员,通过不同的继承方式,到子类中的访问权限也有所不同
3> 默认的继承方式是private,而常用的继承方式是public
继承步骤
1> 子类全盘吸收父类的所有内容,包括私有成员
2> 改造父类继承下来的相关成员权限(继承方式、
using关键字改造 ),只能改造子类能够访问的权限下的成员
3> 拓展新成员
子类继承父类的成员
1、is a:继承关系模型,子类就是父类的一种具体体现。是一种特殊的包含关系模型 2、has a:包含关系模型,就是在一个类中包含了其他类的成员子对象 3、use a:友元关系模型,在一个类中,可以使用另一个类的所有成员
1> 子类会继承父类的所有成员,包括私有成员,只是私有成员无法访问。如果非要访问继承的私有成员,需要借助父类中的公共成员函数来进行操作
2> 父子类中允许出现同名的成员,此处为
覆盖 。如果使用子类对象,调用该成员时,优先调用的是子类自己定义的该成员。如果非要调用父类继承下来的该成员,需要加上父类名和作用域限定符
3> 子类中需要在子类的初始化列表中显性调用父类的有参构造来完成对子类从父类中继承下来成员的初始化工作,如果没有显性调用父类的有参构造,那么系统就会自动调用父类的无参构造来完成对继承下来成员的初始化工作
4> 所谓的继承关系,本质上就是一种特殊的包含关系;
类与类之间的关系模型
5> 虽然在构造子类对象的过程中,使用了父类的构造函数,但是在整个过程中,并没有构造出父类对象的实体,仅仅只是用父类的构造函数帮助子类完成构造功能。最终的对象就只有子类对象一个。
6> 析构子类对象时,先析构子类再析构父类
继承过程中的特殊成员函数
1> 构造函数:父子类中拥有不同的构造函数。但是,需要在子类的构造函数的初始化列表中,显式调用父类的有参构造来完成对子类从父类中继承下来成员的初始化工作,如果没有显性调用父类的有参构造,那么系统会自动调用父类的无参构造。
构造顺序:先构造父类,后构造子类
2> 析构函数:父子类中拥有不同的析构函数。但是,无需在子类的析构函数中,显式调用父类的析构函数。系统会在子类进行析构时,自动调用父类的析构函数来完成对子类从父类中继承下来成员的内存回收工作
析构顺序:先析构子类,后析构父类
3> 拷贝构造函数:父子类中拥有不同的拷贝构造函数。但是,
需要在子类的拷贝构造函数的初始化列表中,显式调用父类的拷贝构造函数 ,来完成对子类从父类继承下来成员的初始化工作,如果没有显性调用父类的拷贝构造函数,那么系统就会自动调用父类的无参构造来完成初始化工作。
4> 拷贝赋值函数:父子类中拥有不同的拷贝赋值函数。但是,需要在子类的拷贝赋值函数的函数体内,显性调用父类的拷贝赋值函数来完成对子类从父类继承下来成员的赋值工作,如果没有显式调用父类的拷贝赋值函数,则啥也不干,原对象中从父类继承下来的成员保持原值不变
多级继承和多重继承
多级继承
1> C++支持多级继承,父类会继承自爷爷类、子类会继承自父类。。。
2> 在后代类中,会完全继承所有祖先类中的所有成员,包括私有成员
多重继承
1> C++支持多重继承,允许一个子类由多个父类共同派生出来
2> 在子类中,会拥有所有基类的所有成员
3> 继承格式
4> 当多个父类中出现同名的成员时,访问该成员时,容易产生歧义,此时需要加上对应的父类名和作用域限定符
5> 多重继承中,先构造父类,再构造子类,父类的构造顺序与继承顺序有关,跟父类的构造函数调用顺序无关
菱形继承(钻石继承)
1> 问题示意图
2> 菱形继承描述
一个公共基类,可以派生出多个中间子类,再由若干个中间子类,能共同派生出一个汇聚子类。这种继承方式就称为菱形继承。
问题:会造成,公共基类中的成员,通过菱形继承后,在汇聚子类中会保留多份,会造成汇聚子类的类体膨胀,并且访问起也比较麻烦
3>
虚继承
虚继承就是为了解决菱形继承问题,避免汇聚子类的类体膨胀,能够保证在汇聚子类中,只保留一份由公共基类传下来的数据。
格式:在
生成中间子类 时,在 继承方式前 加关键字 virtual ,即可完成虚继承
在汇聚子类中保留的那一份公共基类传递下来的成员,不能确定是通过哪一条路线继承下来的。
一般而言,子类的成员属性都是由直接父类进行构造,在虚继承中,不能确定使用哪一个父类在构造该汇聚子类中从公共基类继承下来的成员。索性,两个都不用。直接使用公共基类的有参构造,来完成对这一份成员的初始化工作。如果没有在汇聚子类的初始化列表中显式调用公共基类的有参构造,那么系统会自动调用公共基类的无参构造来完成初始化工作
多态
面向对象的三大特征:封装、继承、多态 多态:所谓多态,就是多种形态。能够实现一行语句多种用途。属于运行时多态 多态的实现:使用父类的指针或者引用,指向子类的对象,并调用子类中重写的父类的虚函数
虚函数
1> 定义格式:在类中定义函数时,在函数前加关键字 virtual ,那么该函数就是虚函数。
2> 虚函数能够保证,父子类中只有一个该函数
3> 如果子类中没有重写该虚函数。那么父子类空间中使用的都是父类定义的该函数
4> 如果子类中重写了该虚函数,那么父子类空间中使用的都是子类重写的该虚函数
4.3 函数重写(override)
1> 函数重写发生在父子类中
2> 要求父类中提供虚函数,子类中对该虚函数进行重新设计
3> 这个重新设计,仅仅只是对函数体内容进行更改,返回值、函数名、形参列表都与父类中的函数保持一致
虚函数的底层实现
1> 当类中出现虚函数时,类体内会多一个指针的大小 ,虚指针(vir_ptr)
2> 该虚指针指向的是一个虚函数表 (vir_table)
3> 虚函数表中,记录了所有虚函数的入口地址
4> 子类中如果对虚函数进行重写,那么重新更改的是虚函数表中的相关函数,并不是子类部分的相关函数
5> 当调用重写的父类的虚函数时,首先会通过虚函数指针找到虚函数表,遍历该表,如果表中没有该函数,则去调用子类自己的相关函数
多态实例