第10章 继承

    第8章和第9章的内容主要围绕面向对象编程的“抽象性”(如何将抽象的事物构建成类,这是个建模的过程)和“封装性”(保证数据的私密性,只提供外部访问的接口),第10章和第11章将探讨“继承性”和“多态性”。继承的概念不难理解,多少有点“不劳而获”的意思,实际也是如此,面向对象程序设计的一个重要特点就是可以在现有的类的基础上定义新的类,而不必将现有的类的内容重新书写一遍,这称为“继承”(Inheritance),现有类称为“父类”或“基类”,在它的基础上建立的类称为“派生类”、“导出类”或“子类”,在本章的描述中,统一使用“基类”和“派生类”的名称。

    本章主要涉及以下知识。

    ❑类的继承:介绍类的继承的概念及其示例。

    ❑派生类:介绍类与类之间的各种派生关系。

    ❑派生:介绍一个类如何声明和定义多个基类。

    ❑虚基类:介绍虚基类的概念及其优点。

    ❑派生类的构造和析构:介绍各种派生类的构造和析构函数的实现。

    ❑组合:介绍组合的概念及其特点。

    ❑基类与派生类对象之间的转换:介绍基类与派生类之间如何进行转换。

    10.1 什么是继承

    继承的概念广泛存在于现实世界中,对面向对象的程序设计而言,继承性的引入意义巨大。首先,程序员可以按现实生活中自然的方式去思考和解决问题,组织信息以提高效率;其次,可以重复使用基类的代码,并可以在继承类中增加新代码或者覆盖基类的成员函数,为基类成员函数赋予新的意义,实现最大限度的代码复用。

    10.1.1 简单示例

    通过例子来理解什么是继承,如代码10.1所示。

    代码10.1 什么是继承InheritanceSample


    <———————————文件名:example1001.cpp———————————————> 01 #include<iostream> 02 using namespace std; 03 class point//point类定义 04 { 05 private://private成员列表,表示点的坐标信息 06 int xPos; 07 int yPos; 08 public: 09 point(int x=0,int y=0)//构造函数,带默认参数 10 { 11 xPos=x; 12 yPos=y; 13 } 14 void disp()//成员disp函数,用来输出点的信息 15 { 16 cout<<"("<<xPos<<","<<yPos<<")"<<endl; 17 } 18 int GetX()//读取private成员xPos 19 { 20 return xPos; 21 } 22 int GetY()//读取private成员yPos 23 { 24 return yPos; 25 } 26 }; 27 class point3D:public point//三维点类point3D,从point类继承而来 28 { 29 private: 30 int zPos;//在point类基础上增加了zPos坐标信息 31 public: 32 point3D(int x,int y,int z):point(x,y)//派生类构造函数,初始化表中调用基类构造函数 33 { 34 zPos=z; 35 } 36 void disp()//隐藏了基类中的同名disp函数 37 { 38 cout<<"("<<GetX()<<","<<GetY()<<","<<zPos<<")"<<endl; 39 } 40 int calcSum()//增添了计算3个数据成员和的函数 41 { 42 return GetX()+GetY()+zPos; 43 } 44 }; 45 int main() 46 { 47 point pt1(7,8);//建立point类对象pt1 48 pt1. disp();//显示pt1的信息 49 point3D pt2(3,4,5);//建立point3D类对象pt2 50 pt2. disp();//显示pt2的信息 51 int res=pt2. calcSum();//计算pt2中3个坐标信息的加和 52 cout<<res<<endl;//输出结果 53 return 0; 54 }

    输出结果如下所示。


    (7,8) (3,4,5) 12

    【代码解析】代码10.1中,point类是二维点类,现在要构建一个三维点类point3D,则point3D类可以从point类继承而来(见代码第27行),point类称为“基类”,point3D类称为“派生类”。在point3D类内不用再对xPos和yPos进行定义性声明,只要增加一个private成员zPos即可,还可在point3D类内定义与point类某个成员函数同名的函数,以实现功能覆盖,如point3D中的disp函数实现了point类中disp函数不同的功能。根据需要可在point3D类增加其他一些成员函数和数据成员,如calcSum函数。

    在任何情况下,派生类内都无法访问基类的私有成员,因此,基类数据的初始化要通过基类的构造函数,而且,它要在派生类数据之前初始化,所以基类构造函数在派生类构造函数的初始化表中调用,也就是说,对基类(point类)中xPos和yPos的初始化必须放在point3D类的初始化列表中,采用诸如“point3D(int x,int y,int z):point(x,y)”的形式完成。

    由此可见,派生类生成过程包含以下3个步骤。

    ❑吸收基类的成员。

    ❑改造基类的成员。

    ❑添加新的成员。