C++重点基础知识整理(二)--- 继承与多态(虚函数)

面向对象程序设计有4个主要特点:抽象、封装、继承和多态性

1、继承性

继承性是面向对象程序设计中最重要的机制,是软件可重用的一种途径

1、类的继承

在这里插入图片描述继承是纵向的,组合是横向的
“继承”是在一个已存在的类的基础上,创建一个新的类
1、已存在的类称为“基类(base class)”或“父类(father class)”
2、新建立的类称为“派生类(derived class)”或“子类(child class)”
3、在建立派生类的过程中,基类不会做任何改变
4、派生类除了继承基类的所有可引用的成员变量和成员函数外,还可增加新的成员变量和函数
单继承:一个派生类只从一个基类派生,这种继承关系所形成的层次是一个树形结构
在这里插入图片描述
多重继承:一个派生类有两个或多个基类
在这里插入图片描述

2、派生类的成员

1、 继承基类的成员数据或成员函数。
派生类把基类全部的成员(不包括构造函数和析构函数)接收过来,不能只选择接收其中一部分成员,而舍弃另一部分成员。
2、增加新的成员数据和新的成员函数。
在声明派生类时,一般还应当定义派生类的新的成员,包括构造函数和析构函数,因为构造函数和析构函数是不能从基类继承的。
3、重新定义已有的成员函数。
覆盖基类的同名成员。

继承方式 :共有派生、私有派生(默认)、保护派生
在这里插入图片描述

3、派生类的构造函数和析构函数

1、构造函数不能被继承
设计派生类的构造函数时,不仅要考虑派生类所增加的数据成员的初始化,还应当考虑基类的数据成员初始化。在执行派生类的构造函数时,使派生类的数据成员和基类的数据成员同时都被初始化。
解决方法: 在执行派生类的构造函数时,调用基类的构造函数来初始化基类成员和子对象。
在这里插入图片描述
2、多重继承
派生类构造函数名(总参数表列) : 基类1构造函数(参数表列) , 基类2构造函数(参数表列) , 基类3构造函数(参数表列){派生类中新增数成员据成员初始化语句}
各基类的排列顺序任意。
派生类构造函数的执行顺序:先调用基类的构造函数,再执行派生类构造函数。
调用基类构造函数的顺序:按照声明派生类时基类出现的顺序。
在这里插入图片描述在这里插入图片描述
当派生类中新增加的数据或函数与基类中原有的同名时,若不加限制,则优先调用派生类中的成员

3、虚基类

在多重派生的过程中,若使公共基类在派生类中只有一个拷贝,可将公共基类说明为虚基类。
在派生类的定义中,在基类的类名前加上关键字virtual,将基类说明为虚基类。
在这里插入图片描述由虚基类派生出的对象初始化时,直接调用虚基类的构造函数。因此,若将一个类定义为虚基类,则一定有正确的构造函数可供所有派生类调用。
用虚基类进行多重派生时,若虚基类没有缺省的构造函数,则在每一个派生类的构造函数中都必须有对虚基类构造函数的调用 (且首先调用)。
在这里插入图片描述使用多重继承时要十分小心,经常会出现二义性问题。
建议:不要在程序中过度使用多重继承,只有在比较简单和不易出现二义性的情况或实在必要时才使用多重继承,能用单一继承解决的问题就不要使用多重继承。
由于这个原因,有些面向对象的程序设计语言(如Java, Smalltalk)不支持多重继承。

2、多态

多态性(polymorphism)是面向对象程序设计的关键技术。
多态性:
向不同的对象发送同一个消息,不同的对象会产生不同的行为。 调用同一个函数名,根据需要实现不同的功能
在这里插入图片描述
1、函数重载运算符重载实现的多态性属于静态多态性,在程序编译时系统就能决定调用的是哪个函数,静态多态性又称编译时的多态性。静态多态性是通过函数的重载实现的(运算符重载实质上也是函数重载)
2、运行时的多态性是指在程序执行之前,根据函数名和参数无法确定应该调用哪一个函数,必须在程序的执行过程中,根据具体的执行情况来动态地确定。动态多态性是通过虚函数(virtual function)实现的。

3、虚函数

若要访问派生类中相同名字的函数,必须将基类中的同名函数定义为虚函数,这样,将不同的派生类对象的地址赋给基类的指针变量后,就可以动态的根据这种赋值语句调用不同类中的函数。
虚函数的声明:
virtual 函数类型 函数名 (形参表){
函数体
}

1、虚函数的使用

1、在程序运行时,通过调用相同的函数名而实现不同功能的函数称为虚函数
2、一旦把基类的成员函数定义为虚函数,由基类所派生出来的所有派生类中,该函数均保持虚函数的特性
3、在派生类中重新定义基类的虚函数时,可以不用关键字virtual来修饰这个成员函数
4、虚函数是用关键字virtual修饰的某基类中的protected或public成员函数。它可以在派生类中重新定义,以形成不同的版本
5、只有在程序的执行过程中,依据指针具体指向哪个类对象,或者依据引用哪个类对象,才能确定激活哪一个版本,实现动态绑定。
在这里插入图片描述

2、什么时候需要虚析构函数?

如果要通过基类指针调用派生类对象的析构函数(里面包含delete),就需要让基类的析构函数称为虚函数。

3、虚函数的几点说明:

1、当在基类中把成员函数定义为虚函数后,在其派生类中定义的虚函数必须与基类中的虚函数同名,参数的类型、顺序、参数的个数必须一一对应,函数的返回类型也相同。
若函数名相同,但参数的个数不同或者参数的类型不同时,则属于函数重载,而不是虚函数。
若函数名不同,显然是不同的成员函数。
2、实现这种动态的多态性时,必须使用基类类型的指针变量,使该指针指向不同的派生类对象,通过调用指针所指向的虚函数实现动态的多态性。
3、虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态的成员函数。
4、在派生类中如果没有重新定义虚函数时,与一般的成员函数一样,当调用这种派生类对象的虚函数时,则调用基类中的虚函数
5、可把析构函数定义为虚函数,但是,不能将构造函数定义为虚函数。
6、虚函数与一般的成员函数相比较,调用时的执行速度要慢一些。为了实现多态性,在每一个派生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现的。因此,除了要编写一些通用的程序,一定要使用虚函数才能完成其功能要求外,通常不必使用虚函数。
7、一个函数如果被定义成虚函数,则不管经历多少次派生,仍将保持其虚特性,以实现“一个接口,多个形态”。

4、虚函数的访问

用基类指针访问虚函数时,指向其派生类对象重新定义的函数,实现动态绑定。
通过对象名访问虚函数时,只能静态绑定。即由编译器在编译的时候决定调用哪个函数。

5、纯虚函数(pure virtual)

在基类中不对虚函数给出有意义的实现,只在派生类中有具体的意义。这时基类中的虚函数只是一个入口,具体的操作由派生类中的对象实现。这种虚函数称为纯虚函数
至少包含一个纯虚函数的类,称为抽象类(abstract classes)。抽象类只能作为派生类的基类,不能用来说明这种类的对象。(与Java中的interface类似 )
在这里插入图片描述1、在定义纯虚函数时,不能定义虚函数的实现部分。
2、把函数名赋于0,本质上是将指向函数体的指针值赋为初值0。与定义空函数不一样,空函数的函数体为空,即调用该函数时,不执行任何动作。在没有重新定义这种纯虚函数之前,是不能调用这种函数的。
3、因为虚函数没有实现部分,所以不能产生对象。但可以定义指向抽象类的指针,即指向这种基类的指针。当用这种基类指针指向其派生类的对象时,必须在派生类中给出纯虚函数的实现,否则会导致程序运行错误。
抽象类的唯一用途是为派生类提供基类,纯虚函数的作用是作为派生类中的成员函数的基础,并实现动态多态性。

6、虚函数与虚基类

C++中继承层次的函数调用遵循以下四个步骤:

  1. 首先确定进行函数调用的对象、引用或指针的静态类型。
  2. 在该类中查找函数,如果找不到,就在直接基类中查找,如此循着类的继承链往上找,直到找到该函数或者查找完最后一个类。如果不能在类或其相关基类中找到该名字,则调用是错误的。
  3. 一旦找到了该名字,就进行常规类型检查,查看如果给定找到的定义,该函数调用是否合法。
  4. 假定函数调用合法,编译器就生成代码。如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定根据对象的动态类型运行哪个函数版本,否则,编译器生成代码直接调用函数。

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值