第04章 类的继承与派生
C++面向对象程序设计
2013年9月9日星期一
4.1.1 基类与派生类
以原有的类为基础产生新类,我们就说 从原有的类派生出新类。在这个过程中 原有的类称为基类,新类称为派生类。
汽车类派生出卡车类。在此过程中,我们 称汽车类为基类,卡车类为汽车类的派生 类。
面向对象程序设计提供了类的继承机制 ,该机制自动的为派生类提供了其基类 的操作和属性,大大的提高了代码的重 用率。
2013年9月9日星期一
C++面向对象程序设计
4.1.2 派生类的声明
C++中声明派生类的形式为:
class 派生类名: 继承方式 基类名1, 继承方 式 基类名2, …,继承方式 基类名n { 派生类的成员声明; };
声明中的“基类名”是已经存在的类的名称, “派生类名”是在已存在的类的基础上通过添 加成员而得到的新类。当派生类只有一个直接 基类的继承情况,称为单继承。当派生类同时 有多个基类的继承情况,称为多继承。C# B
从基类CMeter派生的CStick类除具有CMeter所有公有成员和保护成员 外,还有自身的私有数据成员m_nStickNum和公有成员函数DispStick()。
注意:派生类中或派生类的对象可以使用基类的公有成员(包括保护成 员),例如CStick的成员函数DispStick中调用了基类CMeter的GetPos函数
C++面向对象程序设计
2013年9月9日星期一
4.3.2 析构函数
派生类析构函数应用举例。
class GraduateStudent: public Student { protected: int gCount; public: ~GraduateStudent() { cout<<"Destructing GraduateStudent\n"; } };
C++面向对象程序设计 2013年9月9日星期一
4.4 多继承和虚基类
4.4.1 4.4.2 4.4.3 4.4.4
派生类成员的标识与访问 作用域分辨 多继承和虚基类 虚基类及其派生类的构造函数
C++面向对象程序设计
2013年9月9日星期一
4.4.1 派生类成员的标识与访问
派生类吸收了基类除构造函数和析构函数 以外的所有成员,并添加新成员得到的类 。按照派生类对象能否直接访问可以把派 生类数据成员分为两种。
C++面向对象程序设计 2013年9月9日星期一
4.2.2 私有继承
类的继承方式为私有继承的时候,基类 中的公有和保护成员被吸收后成为派生 类的私有成员,而基类的私有成员在派 生类中不可直接访问。 私有继承应用举例。P125
C++面向对象程序设计
2013年9月9日星期一
[例Ex_ClassPrivateDerived] 派生类的私有继承示例。
C++面向对象程序设计 2013年9月9日星期一
例如: class A {...} class B {...} class C:public A,private B {...} 派生类C的成员包含了基类A中成员和B中成员 以及该类本身的成员。 允许一个基类有多个派生类以及从一个基类 的派生类中再进行多个层次的派生。
派生类构造函数声明一般语法如下:
2013年9月9日星期一
C++面向对象程序设计
派生类的构造函数和析构函数被执行时,基类相应的构造函数和 析构函数也会被执行。 注意,派生类对象在建立时,先执行基类的构造函数,然后执 行派生类的构造函数。但对于析构函数来说,其顺序刚好相反, 先执行派生类的析构函数,而后执行基类的析构函数。 需要注意的是,如果在对派生类进行初始化时,需要对其基类 设置初值,则可按下列格式进行: <派生类名>(总参表):<基类1>(参数表1), <基类2>(参数表2), „, <基类n>(参数表n), 对象成员1(对象成员参数表1), 对象成 员2(对象成员参数表2), „, 对象成员n(对象成员参数表n) { ... } 构造函数总参表后面给出的是需要用参数初始化的基类名、对 象成员名及各自对应的参数表,基类名和对象成员名之间的顺序 可以是任意的,且对于使用默认构造函数的基类和对象成员,可 以不列出基类名和对象成员名。这里所说的对象成员是指在派生 类中新声明的数据成员,它属于另外一个类的对象。对象成员必 C++面向对象程序设计 2013年9月9日星期一 须在初始化列表中进行初始化。
C++面向对象程序设计 2013年9月9日星期一
4.2.3 保护继承
当类的继承方式为保护继承的时候,基 类中的公有和保护成员被吸收后成为派 生类的保护成员,只能被它的派生类成员 函数或友元访问,而基类中的私有成员在 派生类中不可直接访问。 保护继承的例子。 P126
C++面向对象程序设计
2013年9月9日星期一
作用域分辨解决的是派生类中的同名覆盖 现象。
派生类声明了和基类同名的新成员(如果是 函数成员则所带参数必须相同,否则就是函 数重载),派生类的新成员就覆盖了从基类 继承的同名成员。直接访问只能访问派生类 新增成员。为了能够访问基类同名成员,我 们采用了作用域分辨符来完成这个任务。
作用域分辨符的使用形式:
可见成员。派生类的可见成员也就是派生类的 公有成员:派生类从基类继承的公有成员和派 生类新增的公有成员. 不可见成员。派生类的不可见成员就是派生类 的私有成员和保护成员:派生类从基类继承的 私有、保护成员和派生类新增的私有、保护成 员。
C++面向对象程序设计 2013年9月9日星期一
4.4.2 作用域分辨
2013年9月9日星期一
4.3.2 析构函数
派生类的析构函数是在派生类对象生命期结 束之前对派生类新增的数据成员完成必要的 清理工作。
析构函数的作用与构造函数几乎正好相反,它在 对象删除前被自动调用,来完成一些清理工作, 也就是一些扫尾工作。 派生类析构函数执行顺序和构造函数刚好相反: 首先对派生类新增一般数据成员进行清理工作, 接着对派生类内嵌对象成员进行清理工作,最后 才是对基类继承的成员进行清理。这一点,大家 可以根据栈区“先进后出” 的特性来理解。
4.3 派生类的构造函数和析构函数B
4.3.1 构造函数 4.3.2 析构函数
C++面向对象程序设计
2013年9月9日星期一
4.3.1 构造函数
派生类对象在定义的时候必须调用派生类的 构造函数进行初始化工作,也就是为派生类 对象的数据成员赋初值。
构造函数的作用是在对象被创建时用特定的方式 构造对象,将对象初始化为一个特定的状态,使 此对象具有区别于其它对象的特征。 派生类对象的数据成员包括从基类继承的数据成 员和派生类新增的数据成员,如果派生类有内嵌对 象作为数据成员,那么派生类的数据成员还包括内 嵌对象的数据成员。派生类对象定义的时候,构 造函数需要完成上述数据成员的初始化工作。
基类名::成员名; //数据成员 基类名::成员名(参数表); //函数成员
C++面向对象程序设计 2013年9月9日星期一
4.4.3 多继承和虚基类
多继承是指一个派生类同时有多个直接基类,即一个 派生类对象含有多个基类数据成员的拷贝。由于多层 次的交叉派生关系,多继承中一个派生类可能保留某 个基类的多个实例。这种多继承的方式可使派生类具 有多个基类的特性,大大提高了程序代码的可重用性 。 多继承下派生类的定义是按下面的格式: class <派生类名> : [<继承方式1>] <基类名1>,[< 继承方式2>] <基类名2>,... { [<派生类的成员>] }; 其中的继承方式还是前面的三种:public、private 和protected。
派生类构造函数初始化时先调用基类
构造函数完成从基类继承数据成员的 初始化,接着调用内嵌对象的构造函 数完成内嵌对象数据成员的初始化, 最后才调用派生类构造函数完成新增 数据成员的初始化。
C++面向对象程序设计
2013年9月9日星期一
4.3.1 构造函数
派生类构造函数应用举例。P128
class GraduateStudent: public Student { protected: int gCount; public: GraduateStudent(int gN,float gG,int gC): Student(gN,gG) { cout<<"Constructing GraduateStudent\n"; gCount=gC; } }; C++面向对象程序设计
#include <iostream.h>
class CMeter
{public: CMeter(int nPos = 10) ~CMeter() { } { m_nPos = nPos; }
void StepIt(){ m_nPos++; }
int protected: void SetPos(int nPos) { m_nPos = nPos; } private: GetPos(){ return m_nPos; }
C++面向对象程序设计
2013年9月9日星期一
class CStick : public CMeter { int public: void DispStick(); }; void CStick:: DispStick() { 数 cout<<m_nStickNum<<’ ’; } m_nStickNum = GetPos(); // 调用基类CMeter的成员函 // 声明一个公有成员函数 // 注意分号不能省略 m_nStickNum; // 声明一个私有数据成员