围绕继承与多态,对相关的面向对象知识点进行学习,这些内容也是最重要的内容。
1. 继承
1.1 基础使用
继承就是在一个已经存在的类的基础上新建立一个类,新创建的类拥有之前类的特性。继承是面向对象的三大特性之一,体现了代码复用的思想。
- 已经存在的类被称为“基类 Base Class”或“父类”
- 新创建的类被称为“派生类”或“子类Sub Class”
下面是一个最简单的单一继承:
上面的继承在实际开发中没有任何意义,因为派生类对基类没有做出任何改变。通常派生类都会在基类的基础上做出一些代码修改或增加。
基类和派生类是相对的,一个类既可以作为基类又可以作为派生类,取决于两个类之间的关系。派生类是基类的具象化,基类是派生类的抽象化。
对于基类的私有成员,派生类可以继承,但是无法直接访问,访问需要通过基类提供的接口。
1.2 构造函数
派生类的构造函数必须直接或间接调用基类的任意一个构造函数。如果程序员不手动在派生类的构造函数中调用基类的构造函数,编译器会尝试调用基类的无参构造函数。
编译器无法处理所有情况,例如基类没有无参构造函数。
上面的情况必须程序员手动在派生类的构造函数中直接或间接调用基类的构造函数:
- 透传构造
- 委托构造
- 继承构造(C++11)
注:构造函数和析构函数是不能被继承的,继承构造仅仅是一种用法的名称,并没有继承构造函数。
1.2.1 透传构造
透传构造属于在派生类的构造函数中直接调用基类的构造函数。
1.2.2 委托构造
委托构造本身可以脱离继承使用,指的是在某个类中构造函数A可以调用构造函数B。在继承中就可以让构造函数A调用构造函数B,构造函数B透传调用基类的构造函数,这样构造函数A就间接调用了基类的构造函数。
委托构造要避免委托闭环。
在上面代码中,程序会卡在第46行,因为构造函数执行不完,互相委托形成闭环。
1.2.3 继承构造(熟悉)
继承构造是C++11的新特性,并不是表示能继承构造函数,而是一种简便的写法,可以一句话实现一种透传构造。
在派生类中使用下面的语句,可以让派生类生成n(n为基类的构造函数数量)个构造函数,同时这n个构造函数参数与基类的n个构造函数相同,每个派生类的构造函数都透传参数相同的基类构造函数。
1.3 对象的创建与销毁流程
在上面的结果中可以得到如下规律:
1. 创建与销毁过程是对称的。
2. 静态的周期贯穿整个程序。
3. 创建过程中,先执行基类;销毁过程中,后执行基类。因为派生类依赖于基类。
虽然推荐理解记忆,但是也可以直接记忆,根据自己的学习习惯自行选择。
1.4 多重继承
1.4.1 基础使用(掌握)
C++支持多继承,即一个派生类可以有多个基类。
1.4.2 二义性
1.4.2.1 基类拥有同名成员
当多重继承的两个基类拥有同名成员时,编译器会无法区分,因此出现二义性问题。
解决方法:在二义性的成员前使用 类名:: 修饰。
1.4.2.2 菱形继承(钻石继承)
如果一个类A有两个派生类B和C,类D同时继承B和C,此时就出现了菱形继承,当对象D调用A的成员时,会产生二义性。
解决方法1:使用类名::进行区分。
解决方法2:虚继承。
当使用虚继承时,Furniture类内部会生成一张虚基类表(程序运行时加载进内存),内部存储Furniture的成员的调用地址,每个Sofa和Bed类对象内部都会有一个隐藏成员指向虚基类表。
SofaBed对象同时继承了Sofa和Bed类的虚基类表指针,调用func()函数时查表比对,防止二义性的出现。
腾讯文档-流程图插件
虚继承的本质是查表,因此会降低调用效率。
1.5 权限
1.5.1 权限修饰符
本类中
派生类中
全局(例如主函数中)
private 私有(默认)
√
X
X
java继承与多态基础训练protected 保护
√
√
X
public 公有
√
√
√
1.5.2 不同权限的继承
三种权限修饰符可以修饰继承:
- 公有继承
- 保护继承
- 私有继承
1.5.2.1 公有继承
使用的最多的一种继承,之前的继承都是公有继承。在公有继承中,基类的所有成员均可以被派生类继承,但是基类的私有成员无法被派生类直接访问,基类的保护成员和公有成员继承到派生类中作为派生类的保护成员和公有成员(权限不变)。
1.5.2.2 保护继承(掌握)
在保护继承中,基类的所有成员均可以被派生类继承,但是基类的私有成员无法被派生类直接访问,基类的保护成员和公有成员继承到派生类中作为派生类的保护成员。
1.5.2.3 私有继承(掌握)
在私有继承中,基类的所有成员均可以被派生类继承,但是基类的私有成员无法被派生类直接访问,基类的保护成员和公有成员继承到派生类中作为派生类的私有成员。
2. 多态
2.1 概念
从广义上讲,多态可以分为静态多态和动态多态。
静态多态(编译时多态)发生在程序的编译阶段,主要包括函数重载和运算符重载,在编译时就能确定调用关系。
动态多态(运行时多态),本章讨论的主要是动态多态。因此从狭义上讲,多态指的是动态多态。
多态(polymorphism)按照字面的意思是“多种状态”,简单的概括为“一个接口,多种状态”,一个函数接口,在运行时根据传入的参数类型执行不同的策略。
多态的实现需要有三个前提条件:
- 公有继承
- 函数覆盖(函数重写)override
- 基类引用/指针指向派生类对象
2.2 函数覆盖
函数覆盖(函数重写)的前提是虚函数,虚函数使用关键字virtual修饰成员函数实现,普通的虚函数目的是实现函数覆盖。
虚函数的格式:
virtual 返回值类型 函数名 (参数表){}
一句话表达:在之前函数隐藏的前提下,把被隐藏的基类函数使用virtual修饰,就变成了函数覆盖。
虚函数具有以下性质:
- 在Qt Creator中斜体表示虚函数
- 虚函数具有传递性,基类被覆盖的虚函数会自动传递给派生类覆盖的新函数,使后者也变为虚函数。
- 成员函数和析构函数可以设置为虚函数,静态成员函数和构造函数不能设置为虚函数。
- 如果函数的声明与定义分离,virtual只需要修饰在声明处。
- C++11中,可以在派生类新覆盖的函数后面使用override关键字修饰,如果函数覆盖成功则不会报错。
2.3 实现
多态往往伴随着函数的调用和传参,基类引用/指针指向派生类对象通常出现在函数传参中。
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/18453.html