《NO8类的继承与派生电子课件C++程序设计案例教程.ppt》由会员分享,可在线阅读,更多相关《NO8类的继承与派生电子课件C++程序设计案例教程.ppt(39页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、目录类的继承与派生 面向对象程序设计的主要特征之一是继承。在程序开发中,一些类的数据成员和成员函数的内容基本或者相当一部分相同,在建立新类时,就可以继承原有类的相同或者相似部分,再加入自己的新内容,实现代码重用,减少重复的工作量,提高软件开发效率。派生类继承的基类数据成员的初始化工作是在派生类中实现的。派生类对象建立时,系统调用派生类构造函数,在执行派生类构造函数前,先调用基类构造函数以及子对象构造函数,再执行派生类构造函数。系统调用析构函数的顺序与调用构造函数的顺序正相反。由于C+语言允许派生类的多重继承,造成派生类继承的相同基类数据成员的副本可能有若干份,C+系统提供了虚基类的方法来解决这
2、个问题。目录8.1 创建派生类 软件开发中,不同的类中的数据成员和成员函数的内容可能不同,也可能相似。如果新建一个类,其部分数据成员或者成员函数内容已经在已有的其他类中存在,就可以采用继承的方式使得已经存在的成员成为自己的一部分,再加上新的内容即可。继承过程中,原有的类称为“基类(base class)”或者“父类(father class)”,新建的类称为“派生类(derived class)”或者“子类(son class)”。目录8.1.1 派生类的定义 Add your text in here在简单图形系统中定义类图形 定义一个基类Graph和派生类Rectangle,其中,主函数内
3、Rectangle对象调用Graph成员函数和Rectangle成员函数。目录8.1.1 派生类的定义源代码展示#include using namespace std;/定义基类class Graphprotected:int g1,g2;public:void getValue()coutPlease input numbers to g1 and g2:g1g2;void display()coutg1=g1,g2=g2endl;目录8.1.1 派生类的定义运行结果目录8.1.1 派生类的定义程序分析目录知识讲解派生类声明的方式 派生类的构成 假定已经存在一个基类,在此基础上通过继承建立
4、一个派生类,声明的方式如下:class 派生类名:继承方式 基类名 派生类新增的成员;继承的方式包括public(公有的)、private(私有的)和protected(受保护的)。继承方式是可选的,如果不写此项,则系统默认为private。在为派生类选择基类时,应尽量使冗余量最小。派生类虽然从基类接受了所有的成员,但并非可以访问所有成员,因为不同的继承方式使基类的成员到派生类后访问属性发生了变化。如果派生类新增的成员函数与继承的成员函数不仅同名,而且函数的参数表也完全相同,即非函数重载,则派生类新增的成员函数将覆盖继承的成员函数。目录8.1.2 派生类成员的访问属性 Add your tex
5、t in here通过为基类成员定义不同的访问属性,了解访问基类成员的方式 在基类中定义3种不同访问属性的成员,在派生类成员函数内或通过派生类对象调用这些基类成员,观察其区别。目录8.1.2 派生类成员的访问属性源代码展示#include using namespace std;class Graphprivate:int g1,g2;protected:int g3,g4;public:int g5,g6;void getValue()coutInput private data g1 and g2g1g2;coutInput protected data g3 and g4g3g4;cou
6、tInput public data g5 and g6g5g6;coutendl;void display()coutprivate data:g1=g1,g2=g2endl;class Triangle:public Graphprivate:int base,height;public:void getTriValue()coutif(base=g3,height=g4)endl;base=g3;height=g4;目录8.1.2 派生类成员的访问属性运行结果目录8.1.2 派生类成员的访问属性程序分析目录8.1.2 派生类成员的访问属性程序分析目录8.1.2 派生类成员的访问属性程序分
7、析目录知识讲解公有继承 公有继承方式 方式派生类以公有方式继承获得的基类成员,保持了原有的访问属性。其中,获得的私有成员在派生类中不可访问,但可以通过调用基类的公有或者保护属性的函数访问;获得的保护成员可以在派生类中使用,但不能在类外通过对象名调用;获得的公有成员既可以在派生类中使用,也可以在类外调用。保护继承 保护继承方式 方式派生类以保护方式继承获得的基类成员中,私有成员同样不能在派生类中使用,而其他成员在派生类中的访问属性都是protected,也就是说,它们只能在派生类中使用,不能在类外用对象名调用。私有继承 私有继承方式 方式派生类以私有方式继承的基类成员,除了私有成员不能访问外,其
8、他成员的访问属性都是private,即它们只能在派生类中使用,不能在类外被调用。这一特点与保护派生类是一样的,但是保护派生类中的成员可以被新的派生类继承使用。目录8.2 派生类的构造函数和析构函数 Add your text in here观察单继承时,基类和派生类的构造函数和析构函数被调用的情况 定义基类Graph和派生类Rectangle。观察派生类对象建立时,派生类构造函数和基类构造函数被系统调用的情况;观察程序结束时,派生类和基类析构函数被系统调用的情况。8.2.1 创建单继承的构造函数和析构函数目录8.2.1 创建单继承的构造函数和析构函数源代码展示#include using na
9、mespace std;class Graphprivate:int g1,g2;public:Graph(int i1,int i2):g1(i1),g2(i2)cout 调用基类的构造函数endl;Graph()cout 调用基类的析构函数endl;void display()coutprivate data:g1=g1,g2=g2endl;目录8.2.1 创建单继承的构造函数和析构函数运行结果目录8.2.1 创建单继承的构造函数和析构函数程序分析 程序中包含两个类:基类Graph和派生类Rectangle。基类中设计了带参数构造函数和析构函数。构造函数的形参i1和i2在该构造函数被调用时
10、,将为基类数据成员g1和g2赋初值。基类中还设计了析构函数Graph()。析构函数的作用是释放基类对象所占的空间,析构函数内通常没有实质性的内容。如果类中没有定义析构函数,系统将生成无函数体的默认析构函数。程序执行时,系统调用主函数。主函数中先建立了派生类对象,通常派生类建立时要调用构造函数,而派生类的构造函数是带参数。程序结束前,系统通常释放程序运行中所占据的存储空间,采用调用析构函数的方式。从运行结果可以看出,系统是先调用了派生类的析构函数,再调用基类的析构函数。目录知识讲解派生类构造函数的形式 派生类构造函数名(形式参数表):基类构造函数名(实参数表)派生类新增数据成员初始化语句基类和派
11、生类的构造函数和析构函数的执行顺序 建立派生类对象时,先调用基类构造函数,再调用派生类构造函数。如果基类构造函数重载,系统在调用时根据派生类构造函数声明冒号后面的基类构造函数实参表选择参数匹配的基类构造函数。与调用构造函数的顺序相反,系统调用析构函数的顺序是先调用派生类析构函数,再调用基类析构函数。目录8.2.2 创建多层派生类的构造函数和析构函数 Add your text in here观察多层继承后,系统调用派生类和基类构造函数、析构函数的情况 建立3个类,实现多层继承。观察最后一层派生类建立对象时,基类和派生类的构造函数被系统调用的情况;观察程序结束时,基类和派生类的析构函数被系统调用
12、的情况。目录8.2.2 创建多层派生类的构造函数和析构函数 源代码展示#include using namespace std;class Graphprivate:int g1;public:Graph(int i1)g1=i1;Graph()cout 调用Graph 析构函数endl;void display()coutg1=g1endl;目录8.2.2 创建多层派生类的构造函数和析构函数 运行结果目录8.2.2 创建多层派生类的构造函数和析构函数程序分析 程序中包含两层派生:Rectangle类是Graph类的派生类,Square类是Rectangle类的派生类。3个类中各自拥有构造函数
13、。在系统建立Square类的对象时,首先调用Square类的构造函数,在执行Square类的构造函数时,先调用Rectangle类的构造函数;在执行Rectangle类的构造函数时,先调用Graph类的构造函数。每层派生类的构造函数声明中,只要写成上一层基类的构造函数即可,不需要写成所有基类的构造函数,即不要写成下面的形式:Square(int s1,int s2):Rectangle(s1,s2),Graph(s1)从程序运行结果来看,系统调用析构函数的顺序与调用构造函数的顺序正好相反,先调用Square类的析构函数,再调用Rectangle类的析构函数,最后调用Graph类的析构函数。目录
14、知识讲解 多层派生类的构造函数和单层派生类的构造函数的被调用方式是一致的:在建立派生类对象,调用派生类的构造函数时,先调用基类的构造函数。如果基类的构造函数的声明中仍然有上一层基类,那么先调用上一层基类的构造函数,再执行本层基类的构造函数,最后执行派生类的构造函数。另外,程序结束时,系统调用析构函数的顺序正好与调用构造函数的顺序相反。目录8.2.3 创建包含子对象的派生类的构造函数和析构函数 Add your text in here派生类若包含子对象,观察基类和派生类的构造函数和析构函数被调用的情况 如果派生类的数据成员包含子对象,观察在建立派生类对象时,基类和派生类的构造函数被系统调用的情
15、况;观察程序结束时,基类和派生类的析构函数被系统调用的情况。目录8.2.3 创建包含子对象的派生类的构造函数和析构函数源代码展示#include using namespace std;class Graphprivate:int g1;public:Graph(int i1)g1=i1;Graph()cout 调用Graph 析构函数endl;void display()coutg1=g1endl;目录8.2.3 创建包含子对象的派生类的构造函数和析构函数运行结果目录8.2.3 创建包含子对象的派生类的构造函数和析构函数程序分析 构造函数名后面跟着带实参的基类构造函数名和带实参的子对象名。因
16、此,子对象的初始化是在建立派生类时通过调用派生类构造函数来实现的。在执行派生类Square的构造函数之前,先调用基类Graph的构造函数,对基类数据成员初始化;再用子对象名调用Graph类的构造函数,对子对象数据成员进行初始化。目录知识讲解 定义包含子对象的派生类的构造函数的一般形式如下:派生类构造函数名(形参表):基类构造函数名(实参表),子对象名(实参表)派生类中新增数据成员的初始化语句 需要注意的是,派生类构造函数的形参表中的参数,应当包括基类的构造函数和子对象参数表中的参数。同时,基类构造函数名和子对象名的书写次序可以是任意的,系统按照这个书写顺序调用构造函数;而调用析构函数的顺序正好
17、与调用构造函数的顺序相反。目录8.2.4 创建多重继承的构造函数和析构函数 Add your text in here多重继承后,观察基类和派生类的构造函数和析构函数被调用的情况 定义一个派生类,实现多重继承。当派生类对象建立时,观察各基类和派生类的构造函数被调用的情况;观察程序结束时,各基类和派生类的析构函数被系统调用的情况。多重继承是指一个类从多个基类中派生而来。对于多重继承的派生类的构造函数和析构函数,其形式与单继承时的构造函数的形式基本相同,只是在派生类的构造函数后面列出多个基类的构造函数名,列出基类构造函数名的顺序任意。目录8.2.4 创建多重继承的构造函数和析构函数源代码展示#in
18、clude using namespace std;class Graphprivate:int g1;public:Graph(int i1)g1=i1;Graph()cout 调用Graph 析构函数endl;void display()coutg1=g1endl;目录8.2.4 创建多重继承的构造函数和析构函数运行结果目录8.2.4 创建多重继承的构造函数和析构函数程序分析 程序中有一个派生类Square,分别继承了Graph类和Rectangle类。在派生类的构造函数声明中,也列出了两个基类的构造函数,同时,调用两个构造函数的实参分别从派生类Square的构造函数的参数表中获得。主函数
19、中,建立了派生类对象,并设定实参为1和2,系统调用派生类的构造函数,实参分别传递给派生类的构造函数的形参s1和s2。因为派生类的构造函数后面列出了基类构造函数名,系统在执行派生类的构造函数前,按书写顺序先后调用了基类Graph的构造函数和基类Rectangle的构造函数。通过从派生类的构造函数形参表中获得的数据,调用基类的构造函数并为基类的数据成员进行初始化。最后调用派生类的构造函数,并为派生类新增的数据成员进行初始化。目录知识讲解 多重继承派生类的构造函数的声明形式如下:派生类构造函数名(形参表):基类1 构造函数(实参表),基类2 构造函数(实参表),派生类中新增数据成员的初始化语句 声明
20、中,各基类的构造函数名的书写顺序任意,系统将按照这个顺序调用基类的构造函数;程序结束时,系统同样会按照这个顺序的相反顺序调用析构函数。目录8.3 虚基类的使用 Add your text in here虚基类的使用 定义虚基类,观察在多重继承中,最终的派生类的数据成员情况。如果一个派生类有多个直接基类,而这些直接基类又都派生于同一个基类,则在最终的派生类中会保留间接基类中的多份数据成员,从而导致在派生类中访问这些数据成员时可能产生错误,而且多份数据成员的副本占据了较多的存储空间。C+提供了虚基类(virtual base class)的方法来解决这个问题,使得派生类在获得间接基类的数据成员时只
21、保留一份副本。目录8.3 虚基类的使用源代码展示#include#include using namespace std;class Graphprotected:string name;public:Graph(string s)name=s;Graph()cout 调用Graph 析构函数endl;目录8.3 虚基类的使用运行结果目录8.3 虚基类的使用程序分析 程序中首先定义的基类Graph中包含一个受保护的数据成员,可供派生类继承使用。Graph类派生了两个子类Rectangle和Triangle,它们都有一份基类数据成员name的副本。这两个派生类在声明时,继承方式前都加上关键字vi
22、rtual,使基类Graph成为这两个派生类的虚基类。经过这样的声明,继承Rectangle类和Triangle类的最终派生类UserGraph只保留间接基类Graph的数据成员的一份副本。基类Graph的两个派生类Rectangle和Triangle的构造函数中都列出了基类的构造函数Graph(),但是C+编译系统只执行最终派生类UserGraph对虚基类构造函数的调用,而忽略虚基类的其他派生类对虚基类的调用,保证虚基类的数据成员只被初始化一次。目录知识讲解虚基类的声明方式 class 派生类名:virtual 继承方式 基类名 一个基类可生成多个派生类,基类可作为其中部分派生类的虚基类。但
23、是,如果虚基类的派生类和非虚基类的派生类产生了同一个最终派生类,那么这个最终派生类很可能获得多份这个基类的数据成员的副本。因此,为保证最终派生类中只继承一次,通常声明该基类为所有直接派生类的虚基类。虚基类的初始化如果虚基类中定义了带参数的构造函数,则在其所有的派生类(直接派生类或者间接派生类)中,必须通过这些派生类的构造函数对虚基类的构造函数进行调用。程序执行中,其实只有最后的派生类成功地调用了虚基类的构造函数,并对虚基类的数据成员进行了初始化,而其余派生类中对虚基类的调用只是形式,以形成派生类构造函数声明格式的一致性,并没有真正调用虚基类的构造函数。这就保证了虚基类的数据成员不会被多次初始化。