【学习笔记】C++语言程序设计(郑莉):继承与派生


1. 类的继承与派生

所谓继承就是从先辈处得到属性和行为特征。类的继承,是新的类从已有类那里得到已有的特性。从另一个角度来看,从已有类产生新类的过程就是类的派生。由原有类产生新类时,新类便包含了原有类特征,同时也可以加入自己所特有的新特征。原有的类称为基类或父类,产生的新类称为派生类或子类

  • 继承的目的:实现代码与设计的重用
  • 派生的目的:当新的问题出现时,原有程序无法解决时,需要对原有程序进行改造

1.1 派生类的定义

派生类的一般定义语法为:

class 派生类名:继承方式 基类名1,继承方式 基类名2,...,继承方式 基类名n{
	派生类成员声明;
}
//假设基类Base1和Base2是已经定义的类,则下面定义了Derived的派生类,该类从基类Base1和Base2派生而来
class Derived:public Base1,private Base2{
public:
	Derived();
	~Derived();

一个派生类只有一个直接基类的情况,称为单继承;一个派生类,可以同时有多个基类,这种情况称为多继承。在类族中,直接参与派生出某类的基类称为直接基类,基类的基类甚至更高层的基类称为间接基类

在派生类的定义中,处理要指定基类外,还需要指定继承方式。继承方式规定了如何访问从基类继承的成员。在派生类的定义语句中,每一个继承方式,只限定紧随其后的基类。继承方式关键字为:public, protected和private,分别表示公有继承、保护继承和私有继承。如果不显式的给出继承方式关键字,系统的默认值为私有继承(private)。

前面的例子中对Base1是公有继承,对Base2是私有继承,同时声明了派生类自己新的构造函数和析构函数。派生类成员是指除了从基类继承来的所有成员之外,新增加的数据和函数成员

1.2 派生类生成过程

派生新类的过程实际经历了3个步骤:吸收基类成员、改造基类成员和添加新的成员

  • 吸收基类成员:类继承中,第一步是将基类的成员全盘接收,这样派生类实际上就包含了它的全部基类中除构造和析构函数之外的所有成员
  • 改造基类成员:对基类成员的改造包括两个方面:一是基类成员的访问控制问题主要依靠派生类定义时的继承方式来控制;另一个是对基类数据或函数成员的覆盖或隐藏。如果派生类声明了一个和某基类成员同名的新成员(如果是成员函数,参数表也要相同,参数不同的情况属于重载),派生的新成员就隐藏了外层同名成员
  • 添加新的成员:根据实际情况的需要,给派生类添 加适当的数据和函数成员,来实现必要的新增功能。

2. 访问控制

基类的自身成员可以对基类中任何一个其它成员进行访问,但是通过基类的对象,就只能访问该类的公有成员。类的继承方式有public、protected和private三种。不同的继承方式,导致原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同。这里的访问分为:一是派生类中的新增成员访问从基类继承的成员。二是在派生类外部,通过派生类的对象访问从基类继承的成员。

  • 公有继承:当类的继承方式为公有继承时,基类的公有成员和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问。在类族之外只能通过派生类的对象访问从基类继承的公有成员。无论是派生类的成员还是派生类的对象都无法直接访问基类的私有成员。
  • 私有继承:当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可直接访问。派生类的其它成员可以直接访问从基类继承而来的公有和保护成员,但是在类族外部通过派生类的对象无法直接访问它们。
  • 保护继承:保护继承中,基类的公有成员和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可直接访问。派生类的其它成员可以直接访问从基类继承而来的公有和保护成员,但在类外部通过派生类的对象无法直接访问它们。
  • 私有继承和保护继承的异同
    • 在直接派生类中,所有成员的访问属性都是完全相同的
    • 如果派生类作为新的基类继续派生时,两者就有了区别。假设Rectangle类以私有方式继承了Point类后,Rectangle类又派生出Square类,那么Square类的成员和对象都不能访问间接从Point类中继承来的成员。如果Rectangle类是以保护方式继承了Point类,那么Point类中的公有和保护成员在Rectangle类中都是保护成员。Rectangle类再派生出Square类后,Point类中的公有成员和保护成员被Square类间接继承后,有可能是保护成员或私有成员(按具体派生方式可知)。因此,Square类的成员有可能可以访问间接从Point类中继承来的成员。即Point类中的保护成员有可能被它的派生类访问,但是绝不可能被其它外部使用者访问

3. 类型兼容规则

类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。类型兼容规则中所指的替代包括以下的情况:

  • 派生类的对象可以隐含转换为基类对象
  • 派生类的对象可以初始化基类的引用
  • 派生类的指针可以隐含转换为基类的指针

在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员

4. 派生类的构造和析构函数

由于基类的构造和析构函数不能被继承,在派生类时中,如果对派生类新增的成员进行初始化,就必须为派生类添加新的构造函数。但是派生类的构造函数只负责对派生类新增的成员进行初始化,对所有从基类继承下来的成员,其初始化工作还是由基类的构造函数完成,同样,对派生类对象的扫尾、清理工作也需要加入新的析构函数。

4.1 构造函数

构造派生类的对象时,就要对基类的成员对象和新增成员对象进行初始化。在构造派生类的对象时,会首先调用基类的构造函数来初始化它们的数据成员,然后按照构造函数初始化列表中指定的方式初始化派生类新增的成员对象,最后才执行派生类构造函数的函数体。派生类构造函数的一般语法形式为:

派生类名::派生类名(参数表):基类名1(基类1初始化参数表),...,基类名n(基类n初始化参数表),成员对象名1(成员对象1的初始化参数表),...,成员对象名 m(成员对象m初始化参数表){
	派生类构造函数的其他初始化操作;
}

如果对基类初始化时,需要调用基类的带有形参表的构造函数时,派生类就必须声明构造函数。派生类构造函数执行的一般次序为:

  • 调用基类构造函数,调用顺序按照它们被继承时声明的顺序
  • 对派生类新增的成员对象初始化,调用顺序按照他们在类中声明的顺序
  • 执行派生类的构造函数体

4.2 析构函数

在派生过程中,基类的析构函数也不能继承下来,如果需要析构,就要在派生类中声明新的构造函数。派生类的析构函数的功能是在该类对象消亡之前进行一些必要的清理工作。析构函数没有类型,也没有参数。


5. 派生类成员的标识与访问

在派生类中,成员可以按访问属性划分为:不可访问的成员、私有成员、保护成员和公有成员。

5.1 作用域分辨符

作用域分辨符 “::” 用来限定要访问的成员所在的类别名称。一般的使用形式为:

类名::成员名                 //数据成员
类名::成员名(参数表)         //函数成员

如果派生类中声明了与基类成员函数同名的新函数,即使函数的参数表不同,从基类继承的同名函数的所有重载形式也都会被隐藏。如果派生类的多个基类拥有同名的成员,同时,派生类又新增这样的同名成员,在这种情况下,派生类成员将隐藏所有基类的同名成员。

5.2 虚基类

当某类的部分或全部直接基类是从另一个共同基类派生而来时,在这些直接基类中从上一级共同基类继承来的成员就拥有多个相同的名称。在派生类的对象中,这些同名数据成员在内存中同时拥有多个副本,同一个函数名会有多个映射。可以使用作用域分辨符来唯一标识并分别访问它们,也可以将共同基类设置为虚基类,这时从不同的路径继承过来的同名数据成员在内存中就只有一个副本,同一个函数名也只有一个映射。虚基类的声明是在派生类的定义过程中进行的,语法格式为:

class 派生类名:virtual 继承方式 基类名

课程链接:https://www.bilibili.com/video/BV1iF411Y74v

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值