C++-类的继承和派生ppt课件_第1页
C++-类的继承和派生ppt课件_第2页
C++-类的继承和派生ppt课件_第3页
C++-类的继承和派生ppt课件_第4页
C++-类的继承和派生ppt课件_第5页
已阅读5页,还剩52页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第四章继承和派生-4.1继承和派生的概念面向对象程序设计有4个主要特点: 抽象、封装、继承和多态性。在本章中主要介绍有关继承的知识,在下一章中将介绍多态性。C+语言提供了类的继承机制,解决了软件重用问题。4.1.1 继承与派生的概念一个类中包含了若干数据成员和成员函数。在不同的类中,数据成员和成员函数是不相同的。但有时两个类的内容基本相同或有一部分相同。-类的继承:一个新类从已存在的类那里获得该类已有的特性叫作类的继承。已存在的类叫作父类,也叫作基类。产生的新类叫作子类或派生类。类的派生:从一个已有的类那里产生一个新类的过程叫类的派生。已存在的类叫作父类,也叫作基类。产生的新类叫作派生类或子类

2、。类的继承和派生是同一概念,前者是从子类的角度来说,后者是从父类的角度来说的。我们通常说子类继承了父类。父类派生了子类。-描述各级学生的类的继承关系如下图:基类与派生类的关系:派生类是基类的具体化,基类则是派生类的抽象一个派生类的对象也是一个基类的对象。应该具有基类的一切属性和方法。派生类除了具有基类的一切属性和方法外,还可以有自己所特有的属性和方法。4.1.2 派生类和基类的关系-4.1.3 单继承与多继承单继承:一个派生类只从一个基类继承。多重继承:一个派生类从两个或多个基类继承。-4.2 派生类的声明方式声明派生类的一般形式为class 派生类名: 继承方式 基类名派生类新增加的成员 ;

3、继承方式包括: public(公有的),private(私有的)和protected(受保护的),此项是可选的,如果不写此项,则默认为private(私有的)。如下程序演示了类Rectangle(四边形)由类Point继承而来。-void SetPoint(int x,int y)this-x=x;this-y=y;void MovePoint(int dx,int dy)x+=dx;y+=dy;void ShowPoint()cout(x,y);/Point.h#include #include using namespace std;class Pointprivate:int x,y;p

4、ublic:int GetX()return x;int GetY()return y;-void SetRect(int x,int y,int w,int h)SetPoint(x,y);width=w;height=h;void ShowRect()cout左上角坐标为:;ShowPoint();coutendl;cout宽为:widthendl;cout长为:heightendl;/Rectangle.h#include #include Point.husing namespace std;class Rectangle:public Pointprivate:int width;i

5、nt height;public:int GetWidth()return width;int GetHight()return height;-/main.cpp#include Rectangle.hvoid main()Rectangle r;r.SetRect(0,0,10,20);r.ShowRect();r.MovePoint(10,10);r.ShowRect();-4.3 派生类的构成派生类中的成员包括从基类继承过来的成员和自己增加的成员两大部分。在基类中包括数据成员和成员函数(或称数据与方法)两部分,派生类分为两大部分: 一部分是从基类继承来的成员,另一部分是在声明派生类时增

6、加的部分。每一部分均分别包括数据成员和成员函数。-如果在派生类中定义了和基类中同名函数(函数参数个数和类型可以相同也可以不相同),则派生类中的函数会隐藏基类的同名函数。在派生类中不能直接访问基类中的同名函数。(注意与重载的区别。在同一个类中的同名不同参函数为重载函数)如程序PointRect1所示:-4.4继承方式派生类的继承方式有三种:public,private,protected。不同的继承方式决定了基类成员在派生类中的访问属性。继承方式基类中的访问属性派生类中的访问属性publicpublicpublicprotectedprotectedprivate不可访问privatepubli

7、cprivateprotectedprivateprivate不可访问protectedpublicprotectedprotectedprotectedprivate不可访问-4.4.1类的保护成员前面介绍过类的成员(数据成员和成员函数)的访问属性有私有的(private)的和公有的(public的)。另外还提到类的访问属性也可以有保护的(protected的)。类中的protected成员与private成员一样,只能在本类的成员函数中访问,不能在类外通过对象来访问。但通过上面的表中可以看出当类派生时,基类的private成员在派生类中是不可访问的。而基类的protected成员在派生类中

8、随继承方式的不同而不同。-class Drived:public Baseprotected:int j;public:void Fun()i=20;#include using namespace std;class Baseprotected:int i;public:void F();void main()Drived d;-4.5 派生类的构造函数和析构函数构造函数的主要作用是对数据成员初始化。在设计派生类的构造函数时,不仅要考虑派生类所增加的数据成员的初始化,还应当考虑基类的数据成员初始化。也就是说,希望在执行派生类的构造函数时,使派生类的数据成员和基类的数据成员同时都被初始化。解决

9、这个问题的思路是: 在执行派生类的构造函数时,调用基类的构造函数注意:派生类继承基类的除构造函数和析构函数以外的所有函数。-4.4.1 简单的派生类的构造函数简单的派生类:只有一个基类,而且只有一级派生(只有直接派生类,没有间接派生类),在派生类的数据成员中不包含基类的对象(即子对象)。简单派生类中我们一般采用在派生类的构造函数初始化列表中调用基类的构造函数来对继承基类的数据成员进行初始化。其一般形式为:派生类构造函数名(总参数表列): 基类构造函数名(参数表列) 派生类中新增数据成员初始化语句、简单派生类的构造函数的形式-#include #includeusing namespace st

10、d;class Student public: Student(int n,string nam,char s) num=n;name=nam;sex=s; Student( ) protected: int num; string name; char sex ; ;-class Student1: public Student public: Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s) age=a; addr=ad;void show( )coutnum: numendl;coutname: name

11、endl;coutsex: sexendl;coutage: ageendl;coutaddress: addrendlendl;Student1( ) private: int age; string addr; ;-在main函数中,建立对象stud1时指定了5个实参。它们按顺序传递给派生类构造函数Student1的形参。然后,派生类构造函数将前面3个传递给基类构造函数的形参。-、简单派生类的构造函数的几点说明a.定义派生类的对象时系统自动调用派生类构造函数之前会先调用其基类的构造函数。基类的构造函数是在派生类的构造函数的初始化列表中给出。如果在初始化列表中没显式给出调用语句则调用基类的默

12、认构造函数。 例如前例中派生类的构造函数为:Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s)如果在main函数中定义一个Student1类的对象时。系统会先调用基类的构造函数。然后执行Student1的构造函数体内的代码完成对派生类成员的构造。如果Student1类的构造函数改为Student1(int n,string nam,char)则会先调用基类的默认构造函数。-.当派生类构造函数在类外定义时,则只在类外的函数定义处加上调用基类的初始化列表。在类内申明的地方不加.由前面例题中的构造函数Student1(

13、int n,string nam,char s,int a,string ad):Student(n,nam,s)可以看出派生类的构造函数的初始化列表中是在调用基类的构造函数而不是在申明或定义基类的构造函数。所以Student1中的五个参数是形参(带参数类型),而其初始化列表中的Student的三个参数是实参(不带有类型),这些实参取自Student1,所以Studen中的三个参数也可以为常数。例如可将Student1的构造函数改为: Student1(string nam,char s,int a,string ad):Student(10010,nam,s);.在派生类对象释放时先执行派生

14、类的析构函数,然后执行基类的析构函数。-例题:定义一个点类Point.由Point派生出一个圆类Circle/Point.h文件#ifndef POINT_H#define POINT_Hclass Pointprotected:float x;float y;public:Point()x=0; y=0;Point(float x,float y);void Show();#endif/Point.cpp文件#include #include Point.husing namespace std;Point:Point(float x,float y)this-x=x;this-y=y;vo

15、id Point:Show()cout(x,y)endl;-/Circle.h文件#ifndef CIRCLE_H#define CIRCLE_H#include #include Point.husing namespace std;class Circle:public Pointprotected:float r;public:Circle(float x,float y,float r);void Show();float GetArea();float GetLength();#endif/Circle.cpp文件#include #include CirCle.husing nam

16、espace std;Circle:Circle(float x,float y,float r):Point(x,y)this-r=r;void Circle:Show()cout圆心为:;Point(x,y).Show();cout半径为:rendl;float Circle:GetArea()return 3.14159*r*r;float Circle:GetLength()return 3.14159*2*r;-4.5.2 有子对象的派生类的构造函数类的数据成员中还可以包含类对象。例如前面的Student1类继承自Student类,我们可以在Student1类中加入一个Student

17、类的对象来表示该同学所在班的班长。如下程序所示:#include #include using namespace std;class Studentpublic: Student(int n, string nam ) num=n;name=nam; void display( ) coutnum:numendlname:nameendl;protected: int num; string name;-class Student1: public Student public:Student1(int n, string nam,int n1, string nam1,int a, str

18、ing ad):Student(n,nam),monitor(n1,nam1) age=a; addr=ad;void show( )cout学生信息为:endl;display(); coutage: ageendl; coutaddress: addrendl; cout班长为:endl;monitor.display( );private: Student monitor; int age; string addr;-int main( )Student1 stud1(10010,Wang-li,10001,Li-sun,19,115 Beijing Road,Shanghai);stu

19、d1.show( ); return 0;派生类构造函数的任务应该包括3个部分: (1) 对基类数据成员初始化;(2) 对子对象数据成员初始化;(3) 对派生类数据成员初始化。其中前两个必须放在派生类的构造函数的初始化列表中进行,第(3)个可以在函数体中也可以在初始化列表中进行。-定义派生类构造函数的一般形式为派生类构造函数名(总参数表列): 基类构造函数名(参数表列),子对象名(参数表列) 派生类中新增数成员据成员初始化语句执行派生类构造函数的顺序是: 调用基类构造函数,对基类数据成员初始化; 调用子对象构造函数,对子对象数据成员初始化; 再执行派生类构造函数本身,对派生类数据成员初始化。以

20、上次序是固定的,不会因为基类构造函数调用写在前面还是子对象名写在前面而改变。-例题:定义一个点类Point.由Point派生出一个圆类Circle/Point.h文件#ifndef POINT_H#define POINT_Hclass Pointprotected:float x;float y;public:Point()x=0; y=0;Point(float x,float y);void Show();#endif/Point.cpp文件#include #include Point.husing namespace std;Point:Point(float x,float y)t

21、his-x=x;this-y=y;void Point:Show()cout(x,y)endl;4.5.3 多层派生时的构造函数-/Circle.h文件#ifndef CIRCLE_H#define CIRCLE_H#include #include Point.husing namespace std;class Circle:public Pointprotected:float r;public:Circle(float x,float y,float r);void Show();float GetArea();float GetLength();#endif/Circle.cpp文件

22、#include #include CirCle.husing namespace std;Circle:Circle(float x,float y,float r):Point(x,y)this-r=r;void Circle:Show()cout圆心为:;Point(x,y).Show();cout半径为:rh=h;void Show();float GetArea();float GetVolume();#endif-/Column.cpp#include Column.h#include using namespace std;void Column:Show()Circle:Sho

23、w();cout高为:hendl;float Column:GetArea()return 2*Circle:GetArea()+GetLength()*h;float Column:GetVolume()return Circle:GetArea()*h;-在多层派生的情况下:派生类的构造函数初始化列表中只须写出其上一层派生类的构造函数,不要再写上其间接子类的构造函数。-4.5.4 派生类的析构函数在派生时,派生类是不能继承基类的析构函数的,也需要通过派生类的析构函数去调用基类的析构函数。在派生类中可以根据需要定义自己的析构函数,用来对派生类中所增加的成员进行清理工作。基类的清理工作仍然由基

24、类的析构函数负责。在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象进行清理。调用的顺序与构造函数正好相反: 先执行派生类自己的析构函数,对派生类新增加的成员进行清理,然后调用子对象的析构函数,对子对象进行清理,最后调用基类的析构函数,对基类进行清理。-4.6 多重继承前面讨论的是单继承,即一个类是从一个基类派生而来的。实际上,常常有这样的情况: 一个派生类有两个或多个基类,派生类从两个或多个基类中继承所需的属性。C+为了适应这种情况,允许一个派生类同时继承多个基类。这种行为称为多重继承(multiple inheritance)。4.6.1 声明多重继承

25、的方式声多重继承子类的方法和单继承相似,只是在标明子类的位置将继承的父类都写上,且以豆号隔开。例如类多重继承了类A,B,C则申明类的方法如下:class D:public A,protected B,private C类新增加的成员-多重继承的子类具有多个父类,子类中具有所有父类的所有成员。且对多个父类可以有不同的继承方式,不同的继承方式决定了继承而来的父类的成员在子类中的访问属性的不同。4.6.2 多重继承的派生类的构造函数多重继承派生类的构造函数形式与单继承时的构造函数形式基本相同,只是在初始列表中包含多个基类构造函数。形式如下:派生类构造函数名(总参数表列): 基类1构造函数(参数表列)

26、, 基类2构造函数(参数表列), 基类3构造函数 (参数表列) 派生类中新增数据成员成员初始化语句-派生类构造函数的执行顺序同样为: 先调用基类的构造函数,再执行派生类构造函数的函数体。调用基类构造函数的顺序是按照声明派生类时基类出现的顺序,与构造函数初始化列表中基类的排列顺序无关。#include #include using namespace std;class Teacherpublic: Teacher(string nam,int a, string t) name=nam;age=a;title=t;void display( ) coutname:nameendl;coutag

27、eageendl;couttitle:titleendl;-protected: string name; int age; string title; ;class Student public:Student(string nam,char s,float sco)name1=nam;sex=s;score=sco; void display1( ) coutname:name1endl;coutsex:sexendl;coutscore:scoreendl;-protected: string name1;char sex;float score; ;class Graduate:pub

28、lic Teacher,public Student public:Graduate(string nam,int a,char s, string t,float sco,float w): Teacher(nam,a,t),Student(nam,s,sco),wage(w) void show( ) coutname:nameendl; coutage:ageendl; coutsex:sexendl; coutscore:scoreendl; couttitle:titleendl; coutwages:wageendl; -private: float wage; ;int main

29、( )Graduate grad1(Wang-li,24,m,assistant,89.5,1234.5);grad1.show( );return 0;在两个基类中分别用name和name1来代表姓名,其实这是同一个人的名字,从Graduate类的构造函数中可以看到总参数表中的参数nam分别传递给两个基类的构造函数,作为基类构造函数的实参。-解决这个问题有一个好方法: 在两个基类中可以都使用同一个数据成员名name,而在show函数中引用数据成员时指明其作用域,如coutname:Teacher:nameendl;这就是惟一的,不致引起二义性,能通过编译,正常运行。通过这个程序还可以发现一个

30、问题: 在多重继承时,从不同的基类中会继承一些重复的数据。如果有多个基类,问题会更突出。在设计派生类时要细致考虑其数据成员,尽量减少数据冗余。4.6.3 多重继承引起的二义性问题-多重继承可以反映现实生活中的情况,能够有效地处理一些较复杂的问题,使编写程序具有灵活性,但是多重继承也引起了一些值得注意的问题,它增加了程序的复杂度,使程序的编写和维护变得相对困难,容易出错。其中最常见的问题就是继承的成员同名而产生的二义性(ambiguous)问题。a)多个基类中有同名成员-#include #include using namespace std;class Aprotected:int a;pu

31、blic:A(int a)this-a=a;void display( )coutA:a=aa=a;void display( )coutB:a=ab=b;void show()A:display(); B:display();coutC:b=bendl;void main()C c(1,2,3);/c.display(); 二义性;c.show();c.B:display();-所以类中数据成员全名应该为下图所示:-b)多个基类和派生类中都有同名成员class C :public A,public Bprivate: int a;public:C(int Aa,int Ba,int Ca):

32、A(Aa),B(Ba)a=Ca;void display()A:display();B:display();coutC:a=aendl;将前面的类改为如下的形式:-这时类的成员构成如左图所示;在类中有三个a,三个display()函数。思考:如下的main函数能否执行:void main()C c(1,2,3);c.display();c.A:display();c.B:display();这时c.dispaly是可以执行的。原因是类中提供的display函数隐藏了基类和基类中的display函数。所以直接访问display函数是在访问类中新增加的成员函数display();-C)如果类A和类

33、B是从同一个基类派生的-前面提到派生类的对象也是基类的对象,因为派生类中继承了基类中的所有成员(除构造函数和析构函数)。准确的说应该是:公有派生类的对象是基类的对象。因为只有公有派生类中成员的访问属性与基类完全相同。基类能实现的功能在公有派生类中一定能够实现。4.7 基类与派生类的转换基本数据类型在一定条件下可以进行类型转化,那么基类对象与派生类对象之间是不是也可以进行转化?由于公有派生类对象也是基类的对象,所以派生类对象可以自动转化为基类对象。表现在以下几方面:-#include #include using namespace std;class Personpublic:Person(s

34、tring nam,char s,int a)name=nam;sex=s;age=a;Person()diplay()cout姓名:nameendl;cout性别:sexendl;cout年龄:ageendl;protected: string name;char sex;int age;-class Teacher: public Person public: Teacher(string nam,char s,int a, string t):Person(nam,s,a)title=t; Teacher() diplay()Person:diplay();cout职称:diplay();用派生类对象的地址来初始化基类指针后,只能通过该指针访问派生类中继承的基类的成员。不能访问派生类中增加的成员。-()

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论