继承
允许用户依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更加容易。
11.1 继承与派生
11.1 继承概述
继承
可以理解为一个类从另一个类获取成员变量和成员函数的过程。
例如类B继承于类A,那么B就拥有A的成员变量和成员函数。- 被继承的类称为
父类或基类
。 - 继承的类称为
子类或派生类
。
11.1.1 什么是继承
继承
就是指某类事物具有比其父辈事物更一般性的某些特征。
当用户创建的新类与现有的类相似,只是多于若干成员变量或成员函数时,可以使用继承,这样不但会减少代码量,而且新类会拥有基类的所有功能。
当用户需要创建多个类,它们拥有很多相似的成员变量或成员函数时,也可以使用继承。可以将这些类的共同成员提取出来,定义为基类,然后从基类继承,既可以节省代码,也方便后续修改成员。
11.1.2 基类与派生类
- 所谓
继承
就是在一个已存在的类的基础上建立一个新的类。 - 已存在的类称为
基类或父类
。 - 新建的类称为
派生类或子类
。
11.1.3 C++派生语法
- 继承方式包括
public公有的
、private私有的
、protected受保护的
,此项是可选的,如果不写,那么默认为private
。
11.1.4 C++继承方式与访问属性
1. 公有继承public
- 在定义一个派生类时将基类的继承方式指定为public的,称为
公有继承
,用公有继承方式建立的派生类称为公有派生类
(public derived class),其基类称为公有基类
。 - 采用公有继承方式时,基类的public成员和protected成员在派生类中仍然保持其公有和保护的属性,而基类的private成员在派生类中并没有成为派生类的私有成员,它仍然是基类的私有成员,只有基类的成员函数可以引用它,而不能被派生类的成员函数引用,因此就成为派生类中不可访问的成员。
2. 私有继承private
- 在声明一个派生类时将基类的继承方式指定为private的,称为
私有继承
,用私有继承方式建立的派生类称为私有派生类
,其基类称为私有基类
。 - 私有基类的public成员和protected成员在派生类中的访问权限相当于派生类中的私有成员,即派生类的成员函数能访问它们,而在派生类外不能访问它们。私有基类的private成员在派生类中成为不可访问的成员,只有基类的成员函数可以引用它们。
- 既然声明为私有继承,就表示将原来能被外界引用的成员隐藏起来,不让外界引用,因此私有基类的公有成员和保护成员理所当然地成为派生类中的私有成员。
3. 保护成员和保护继承protected
- 由protected声明的成员称为“
受保护的成员
”,或简称“保护成员
”。从类的用户角度来看,保护成员等价于私有成员。但有一点与私有成员不同,保护成员可以被派生类的成员函数引用,但是不能访问私有成员。 - 在定义一个派生类时将基类的继承方式指定为protected的,称为
保护继承
,用保护继承方式建立的派生类称为保护派生类
,其基类称为受保护的基类
,简称保护基类
。 - 特点是:保护基类的公有成员和保护成员在派生类中都成了保护成员,其私有成员仍未基类私有。也就是把基类原来的公有成员也保护起来,不让类外任意访问。
- 保护基类的所有成员在派生类中都被保护起来,类外不能访问,其公有成员和保护成员可以被其派生类的成员函数访问。
11.1.5 继承中的构造顺序
- 在声明一个类后,用户其实可以不定义构造函数的,因为系统会自动设置一个默认的构造函数,在定义类对象时会自动调用这个默认的构造函数。这个构造函数实际上是一个空函数,不执行任何操作。
构造函数
的主要作用是对数据成员初始化。- 基类的构造函数是不能继承的,在声明派生类时,派生类并没有把基类的构造函数继承过来,因此,对继承过来的基类成员初始化的工作也要由派生类的构造函数承担。
- 希望在执行派生类的构造函数时,使派生类的数据成员和基类的数据成员同时都要被初始化。解决这个问题的思路是,在执行派生类的构造函数时,调用基类的构造函数。
- 实际上,在派生类构造函数中对基类成员初始化,就是普通类构造函数初始化表。
- 在建立一个对象时,执行构造函数的顺序是:
派生类构造函数先调用基类构造函数;
再执行派生类构造函数本身(即派生类构造函数的函数体)。
11.1.6 继承中的析构顺序
- 在派生时,派生类是不能继承基类的析构函数的,也需要通过派生类的析构函数去调用基类的析构函数。
- 在派生类中可以根据需要定义自己的析构函数,用来对派生类中所增加的成员进行清理工作。
- 基类的清理工作仍然由基类的析构函数负责。
- 在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象进行清理。
- 调用的顺序与构造函数正好相反:
先执行派生类自己的析构函数,对派生类新增加的成员进行清理;
然后调用子对象的析构函数,对子对象进行清理;
最后调用基类的析构函数,对基类进行清理。
11.2 基类与派生类的转换
- 不同类型数据之间的自动转换和赋值,称为
赋值兼容
。
对基类的赋值
- 在赋值时舍弃派生类自己的成员。
- 对基类的赋值只是对数据成员赋值,对成员函数不存在赋值问题。
- 只能用子类对象对其基类对象赋值,而不能用基类对象对其子类对象赋值。
- 派生类对象可以替代基类对象向基类对象的引用进行赋值或者初始化。
- 如果函数的参数是基类或者是基类对象的引用,则相应的实参可以是派生类对象。
- 派生类对象的地址可以赋给指向基类对象的指针变量,也就是说,指向基类对象的指针变量也可以指向派生类对象。