课件第8章继承与多态_第1页
课件第8章继承与多态_第2页
课件第8章继承与多态_第3页
课件第8章继承与多态_第4页
课件第8章继承与多态_第5页
已阅读5页,还剩53页未读 继续免费阅读

付费下载

下载本文档

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

文档简介

程序设计与算法语言

——C++程序设计第8章继承与多态信息科学与工程学院第8章继承与多态2内容概要 类的继承与派生 派生类的构造与析构 多重继承、虚基类 多态性与虚函数 重载(overload)与

同名覆盖(override)信息科学与工程学院第8章继承与多态3内容安排8.1继承与派生的概念8.2派生类的构造函数与析构函数8.3多重继承与派生类成员标识8.4虚基类8.5派生类应用讨论8.6多态性与虚函数

信息科学与工程学院第8章继承与多态48.1继承与派生的概念

继承(inheritance)该机制是面向对象程序设计使代码可以复用的最重要的手段它允许程序员在保持原有类特性的基础上进行调整和扩展这样产生新的类,称之为派生类继承呈现了面向对象程序设计的层次结构(hierarchy)体现了由简单到复杂的认识过程信息科学与工程学院第8章继承与多态58.1继承与派生的概念层次概念通过继承机制可对类分层,提供类型/子类型的关系C++通过类派生(classderivation)的机制来支持继承被继承的类称为基类(baseclass)或超类(superclass)新的类称为派生类(derivedclass)或子类(subclass)基类和派生类的集合称作类继承层次结构如果基类和派生类共享相同的公有接口,则派生类被称作基类的子类型(subtype)派生反映了事物之间的联系,事物的共性与个性之间的关系。派生与独立设计若干相关的类,前者工作量少,重复的部分可以从基类继承来,不需要单独编程。信息科学与工程学院第8章继承与多态68.1继承与派生的概念内容8.1.1类的派生与继承8.1.2公有派生与私有派生信息科学与工程学院第8章继承与多态78.1.1类的派生与继承派生类的定义

class

派生类名:访问限定符

基类名1

《,访问限定符

基类名2,……,访问限定符

基类名n》

{

《《private:》

成员表1;》

//派生类增加或替代的私有成员

《public:

成员表2;》

//派生类增加或替代的公有成员

《protected:

成员表3;》

//派生类增加或替代的保护成员

};如果一个派生类可以同时有多个基类,称为多重继承。这时的派生类同时得到了多个已有类的特征。新增成员给派生类添加了不同于基类的新的属性和功能。更新成员通过屏蔽作用取代基类原有成员。访问控制——对基类成员进一步的限制:公有(public)继承保护(protected)继承私有(private)继承信息科学与工程学院第8章继承与多态88.1.1类的派生与继承派生步骤吸收基类的成员改造基类成员发展新成员重写构造函数与析构函数

不论是数据成员,还是函数成员,除构造函数与析构函数外全盘接收。声明一个与某基类成员同名的新成员,派生类中的新成员就屏蔽了基类同名成员,称为同名覆盖。信息科学与工程学院第8章继承与多态98.1.2公有派生与私有派生访问限定符两方面含义派生类成员(新增的)函数对基类成员的访问(调用和操作)从派生类对象之外对派生类对象中的基类成员的访问信息科学与工程学院第8章继承与多态108.1.2公有派生与私有派生不可直接访问不可直接访问

private

不可直接访问privateprotected不可直接访问privatepublic私有派生不可直接访问不可直接访问

private

不可直接访问protectedprotected可直接访问

publicpublic公有派生在派生类对象外访问派生类对象的基类成员在派生类中对基类成员的访问限定基类中的访问限定派生方式信息科学与工程学院第8章继承与多态118.1.2公有派生与私有派生不可直接访问不可直接访问

private

不可直接访问protectedprotected不可直接访问protected

public保护派生不可直接访问不可直接访问

private

不可直接访问protectedprotected可直接访问

publicpublic公有派生在派生类对象外访问派生类对象的基类成员在派生类中对基类成员的访问限定基类中的访问限定派生方式信息科学与工程学院第8章继承与多态12不可直接访问不可直接访问

private

不可直接访问privateprotected不可直接访问privatepublic私有派生不可直接访问不可直接访问

private

不可直接访问protectedprotected不可直接访问protected

public保护派生不可直接访问不可直接访问

private

不可直接访问protectedprotected可直接访问

publicpublic公有派生在派生类对象外访问派生类对象的基类成员在派生类中对基类成员的访问限定基类中的访问限定派生方式信息科学与工程学院第8章继承与多态132010年5月10日实验内容必作部分实验二十二:“继承与派生的基本概念”中的范例1信息科学与工程学院第8章继承与多态148.2派生类的构造函数与析构函数

派生类构造函数的定义

派生类名::派生类名(参数总表):基类名1(参数名表1)《,基类名2(参 数名表2),……,基类名n(参数名表n)》,成员对象名1(成员对 象参数名表1),……,成员对象名m(成员对象参数名表m)》

{……}在构造函数的声明中,冒号及冒号以后部分必须略去所谓不能继承基类的构造函数,并不是说不能利用,而是把基类的构造函数作为新的构造函数的一部分,或者讲调用基类的构造函数基类名仅指直接基类,写了底层基类,编译器认为出错冒号后的基类名,成员对象名的次序可以随意,这里的次序与调用次序无关信息科学与工程学院第8章继承与多态158.2派生类的构造函数与析构函数派生类构造函数各部分执行次序调用基类构造函数,按它们在派生类定义先后顺序调用调用成员对象的构造函数,按它们在类定义中声明的先后顺序调用派生类的构造函数体中的操作在派生类构造函数中,只要基类不是仅使用无参的默认构造函数都应当显式给出基类名和参数表。如果基类没有定义构造函数,则派生类也可以不定义,全部采用系统给定的默认构造函数。信息科学与工程学院第8章继承与多态168.2派生类的构造函数与析构函数析构函数析构函数的功能是作善后工作只要在函数体内把派生类新增一般成员处理好就可以了而对新增的成员对象和基类的善后工作,系统会自动调用成员对象和基类的析构函数来完成析构函数各部分执行次序与构造函数相反首先对派生类新增一般成员析构然后对新增对象成员析构最后对基类成员析构信息科学与工程学院第8章继承与多态17【例8.1】由在册人员类公有派生学生类classPerson{

stringIdPerson;

//身份证号

stringName; //姓名

TsexSex; //性别

intBirthday;

//生日

string

HomeAddress; //家庭地址public: Person(string,string,Tsex,int,string); Person(); ~Person();

};类的聚合void

SetName(string);stringGetName(){returnName;}void

SetSex(Tsexsex){Sex=sex;}TsexGetSex(){returnSex;}void

SetId(stringid){IdPerson=id;}stringGetId(){returnIdPerson;}void

SetBirth(intbirthday){Birthday=birthday;}int

GetBirth(){returnBirthday;}void

SetHomeAdd(string);stringGetHomeAdd(){returnHomeAddress;}voidPrintPersonInfo();信息科学与工程学院第8章继承与多态18classStudent

:publicPerson{

//定义派生的学生类

stringNoStudent; //学号

coursecs[30]; //30门课程与成绩public: Student(stringid,stringname,Tsexsex,int birthday,stringhomeadd,stringnostud);

Student(); ~Student();

intSetCourse(string,int);

intGetCourse(string);

voidPrintStudentInfo();};

Student::Student(stringid,stringname,Tsexsex,intbirthday,

string homeadd,stringnostud):

Person(id,name,sex, birthday,homeadd){

NoStudent=nostud;

for(inti=0;i<30;i++){

cs[i].coursename="#";cs[i].grade=0; }}Student::Student(){//基类缺省无参数构造函数不必显式给出 NoStudent="";

for(inti=0;i<30;i++){ cs[i].coursename=""; cs[i].grade=0; }}信息科学与工程学院第8章继承与多态198.5派生类应用讨论

派生类与基类赋值兼容规则:在任何需要基类对象的地方都可以用公有派生类的对象来代替。包括以下情况赋值——派生类的对象可以赋值给基类的对象,这时是把派生类对象中从对应基类中继承来的成员赋值给基类对象(反过来不行,因为派生类的新成员无值可赋)信息科学与工程学院第8章继承与多态208.5派生类应用讨论

派生类与基类赋值兼容规则:在任何需要基类对象的地方都可以用公有派生类的对象来代替。包括以下情况指针——可以将一个派生类的对象的地址赋给其基类的指针变量,但只能通过这个指针访问派生类中由基类继承来的成员,不能访问派生类中的新成员(同样也不能反过来做)引用——派生类对象可以初始化基类的引用,但这个别名只能包含派生类对象中的由基类继承来的成员(同样也不能反过来做)信息科学与工程学院第8章继承与多态21【例8.5】赋值兼容规则与自定义的拷贝构造函数Person::Person(Person&ps){ IdPerson=ps.IdPerson; Name=ps.Name; Sex=ps.Sex; Birthday=ps.Birthday; HomeAddress=ps.HomeAddress;}Student::Student(Student&Std):Person(Std){ NoStudent=Std.NoStudent;

for(inti=0;i<30;i++){ cs[i].coursename=Std.cs[i].coursename; cs[i].grade=Std.cs[i].grade; }}信息科学与工程学院第8章继承与多态22Person&Person::operator=(Person&ps){ IdPerson=ps.IdPerson; Name=ps.Name; Sex=ps.Sex; Birthday=ps.Birthday; HomeAddress=ps.HomeAddress;

return

*this;}Student&Student::operator=(Student&Std){

this->Person::operator=(Std); NoStudent=Std.NoStudent;

for(inti=0;i<30;i++){ cs[i].coursename=Std.cs[i].coursename; cs[i].grade=Std.cs[i].grade; } return*this;}信息科学与工程学院第8章继承与多态238.5派生类应用讨论继承与聚合继承模式——派生类通过继承利用基类的成员聚合模式——把基类的对象作为一个新类的对象成员,也可以取得类似的效果体现了封装更深层次的含义是一个完备的小系统PointxyLinestartendCircleradiusCylinderheightLineX2y2信息科学与工程学院第8章继承与多态24PointxyLinestartendLine2X2y2Line3endclassLine{

Pointstart,end;

};classLine:publicPoint{

doublex2,y2;

};classLine:publicPoint{

Pointend;

};聚合派生派生+聚合信息科学与工程学院第8章继承与多态258.5派生类应用讨论派生类与模板派生类——编程效率代码的复用和程序的通用性由简到繁,逐步深入,程序编制过程中可以充分利用前面的工作,一步步完成一个复杂的任务模板——运行效率类模板是相互独立的,即,独立设计,没有使用继承的思想采用适配器(adapter)实现对类模板的扩展template<class_Ty, class_Container=deque<_Ty>>classstack信息科学与工程学院第8章继承与多态268.3多重继承与派生类成员标识

由多个基类共同派生出新的派生类,这样的继承结构被称为多重继承或多继承(multiple-inheritance)派生出来的新类同样可以作为基类再继续派生出更新的类,依此类推形成一个层次结构多层vs

多重信息科学与工程学院第8章继承与多态27在册人员学生(单继承)教职工(单继承)兼职教师(单继承)教师(单继承)行政人员(单继承)工人(单继承)研究生(单继承)行政人员兼教师(多重继承)在职研究生(多重继承)研究生助教(多重继承)8.3多重继承与派生类成员标识唯一标识问题:通常采用作用域分辨符“::”基类名::成员名;基类名::成员名(参数表);信息科学与工程学院第8章继承与多态28定义EGStudent类对象EGStud1并假定派生全部为公有派生而No全为公有成员classEGStudentintNo在职学号…classStudentintNo学生号…classPersonintNo身份证号…classEmployeeintNo工作证号…classPersonintNo身份证号…classGStudentintNo研究生号…EGStud1.NoEGStud1.GStudent::NoEGStud1.GStudent.Student::NoEGStud1.GStudent.Student.Person::NoEGStud1.Employee::NoEGStud1.Employee.Person::No一般数据成员总是私有成员,派生类对基类的访问只能间接进行。访问身份证号,应通过Person中的公有成员函数GetNo和SetNo进行:EGStud1.Employee.Person::SetNo(no);no=EGStud1.Employee.Person::GetNo();信息科学与工程学院第8章继承与多态29【例8.2】由圆和高多重继承派生出圆锥classCircle{protected:

float

x,y,r; //(x,y)为圆心,r为半径public: Circle(floata=0,floatb=0,floatR=0){x=a;y=b;r=R;}

voidSetcoordinate(floata,floatb){x=a;y=b;}

voidGetcoordinate(float&a,float&b){a=x;b=y;}

voidSetR(floatR){r=R;}

floatGetR(){returnr;}

floatGetAreaCircle(){return

float(r*r*3.14159);}

floatGetCircumference(){return

float(2*r*3.14159);}};classLine{protected:

float

High;public: Line(floata=0){High=a;}

voidSetHigh(floata){High=a;}

floatGetHigh(){returnHigh;}};classCone:publicCircle,public

Line{public: Cone(floata,floatb,floatR,floatd):Circle(a,b,R),Line(d){}

floatGetCV(){return

float(GetAreaCircle()*High/3);}

floatGetCA() {

return

float(GetAreaCircle()+ r*3.14159*sqrt(r*r+High*High)); } //共有派生类中能直接访问直接基类的保护成员};信息科学与工程学院第8章继承与多态30作业P306:习题8.1.1、8.1.2和8.1.3。抄写P307:习题8.1.4。抄写P307:习题8.2.1、8.2.2、8.2.3、8.2.5、8.2.6和8.2.7P307:习题8.3和8.4信息科学与工程学院第8章继承与多态312010年5月17日实验内容必作部分实验二十二:“继承与派生的基本概念”中的编程2实验二十三:“多重继承与虚基类”中的编程1信息科学与工程学院第8章继承与多态328.4虚基类

引入将共同基类设置为虚基类(

virtualbaseclass),这样派生类从不同派生路径继承来的同类同名数据成员在内存中就是同一个数据定义格式:

class

派生类名:virtual

访问限定符

基类类名{...};

class

派生类名:访问限定符

virtual

基类类名{...};

virtual关键字只对紧随其后的基类名起作用这种继承称之为虚拟继承信息科学与工程学院第8章继承与多态338.4虚基类虚拟继承的构造函数格式:

派生类名::派生类名(参数总表):基类名1(参数名表1)《,基类名2(参数名表2),……,基类名n(参数名表n)》,《成员对象名1(成员对象参数名表1),……,成员对象名m(成员对象参数名表m)》,底层虚基类名1(参数名表1)《,……,底层虚基类名r(参数名表r)》

{……};在多层虚拟继承构造函数中,基类名不仅要列出直接基类,而且要列出底层虚基类,否则编译器认为出错信息科学与工程学院第8章继承与多态34classPersonclassStudent:public

virtualPersonclassEmployee:public

virtualPersonclassGStudent:publicStudentclassEGStudent:publicEmployee,publicStudent信息科学与工程学院第8章继承与多态358.4虚基类派生类对象的创建中构造函数执行次序首先是虚基类的构造函数并按它们声明的顺序构造第二批是非虚基类的构造函数按它们声明的顺序调用第三批是成员对象的构造函数最后是派生类自己的构造函数被调用信息科学与工程学院第8章继承与多态36【例8.3】在采用虚基类的多重继承中,构造与析构的次序classObject{public: Object(){cout<<"constructorObject\n";} ~Object(){cout<<"deconstructorObject\n";}};class

Bclass1{public: Bclass1(){cout<<"constructorBclass1\n";} ~Bclass1(){cout<<"deconstructorBclass1\n";}};class

Bclass2{public: Bclass2(){cout<<"constructorBclass2\n";} ~Bclass2(){cout<<"deconstructorBclass2\n";}};class

Bclass3{public: Bclass3(){cout<<"constructorBclass3\n";} ~Bclass3(){cout<<"deconstructorBclass3\n";}};信息科学与工程学院第8章继承与多态37classDclass:public

Bclass1,

virtual

Bclass3,virtual

Bclass2{

Objectobject;public: Dclass():object(),Bclass2(),Bclass3(),Bclass1() {cout<<"派生类建立!\n";} ~Dclass() {cout<<"派生类析构!\n";}};intmain(){ Dclassdd; cout<<"主程序运行!\n";

return0;}constructorBclass3constructorBclass2constructorBclass1constructorObject派生类建立!主程序运行!deconstructorObjectdeconstructorBclass1deconstructorBclass2deconstructorBclass3信息科学与工程学院第8章继承与多态388.6多态性与虚函数

多态性(polymorphism)多态性是考虑在不同层次的类中,以及在同一类中,同名的成员函数之间关系的问题类型编译时的多态性函数重载运算符重载运行时的多态性——是面向对象程序设计的标志性特征以虚函数为基础的成员函数同名覆盖与一般的同名覆盖不同!信息科学与工程学院第8章继承与多态398.6多态性与虚函数内容8.6.1虚函数的定义8.6.2纯虚函数8.6.3继承与多态的应用8.6.4动态绑定信息科学与工程学院第8章继承与多态408.6.1虚函数的定义

虚函数概念:是一个类的成员函数定义格式:

virtual

返回类型

函数名(参数表){…};关键字virtual指明该成员函数为虚函数virtual仅用于类定义中,如虚函数在类外定义,不可再加virtual当一个类的某个成员函数被定义为虚函数,则由该类派生出来的所有派生类中,该函数始终保持虚函数的特征信息科学与工程学院第8章继承与多态418.6.1虚函数的定义虚函数定义要点当在派生类中重新定义虚函数(overridingavirtualfunction——超载)时,不必加关键字virtual但重新定义时不仅要同名,而且它的参数表和返回类型全部与基类中的虚函数一样,否则出错信息科学与工程学院第8章继承与多态42【例8.6】计算学分运行时的多态性可由本科生类派生出研究生类,但它们各自的从课程学时数折算为学分数的算法是不同的本科生是16个学时一学分研究生是20个学时一学分信息科学与工程学院第8章继承与多态43classStudent{ stringcoursename; //课程名

intclasshour; //学时

intcredit; //学分public: Student(){coursename="#";classhour=0;credit=0;}

virtual

void

Calculate(){credit=classhour/16;}

voidSetCourse(stringstr,inthour) { coursename=str;classhour=hour; }

intGetHour(){returnclasshour;}

voidSetCredit(intcred){credit=cred;}

voidPrint() { cout<<coursename<<'\t'<<classhour<<"学时" <<'\t'<<credit<<"学分"<<endl; }};classGradeStudent:publicStudent{public: GradeStudent(){};

voidCalculate() {

SetCredit(GetHour()/20); }};信息科学与工程学院第8章继承与多态44intmain(){

Students,*ps;

GradeStudentg; s.SetCourse("物理",80);

s.Calculate(); g.SetCourse("物理",80);

g.Calculate(); cout<<"本科生:"<<'\t'; s.Print(); cout<<"研究生:"<<'\t'; g.Print(); s.SetCourse("数学",160);

g.SetCourse("数学",160);

ps=&s; ps->Calculate(); cout<<"本科生:"<<'\t'; ps->Print();

ps=&g; ps->Calculate(); cout<<"研究生:"<<'\t'; ps->Print();

return0;}本科生: 物理 80学时 5学分研究生: 物理80学时 4学分本科生:数学160学时 10学分研究生:数学160学时 8学分voidCalfun(Student

&ps,stringstr,inthour){ ps.SetCourse(str,hour); ps.Calculate(); ps.Print();}intmain(){ Students; GradeStudentg; cout<<"本科生:\t";

Calfun(s,"物理",80);

cout<<"研究生:\t";

Calfun(g,"物理",80);

return0;}本科生: 物理 80学时 5学分研究生: 物理80学时 4学分信息科学与工程学院第8章继承与多态458.6.1虚函数的定义成员函数设置为虚函数的要点实现动态多态性时,必须使用基类类型的指针变量或引用,使该指针指向该基类的不同派生类的对象,并通过该指针指向虚函数,才能实现动态的多态性派生类中定义虚函数必须与基类中的虚函数同名外,还必须同参数表,同返回类型否则被认为是重载,而不是虚函数例外:如基类中返回基类指针,派生类中返回派生类指针是允许的信息科学与工程学院第8章继承与多态468.6.1虚函数的定义成员函数设置为虚函数的要点只有类的成员函数才能说明为虚函数,这是因为虚函数仅适用于有继承关系的类对象静态成员函数,是所有同一类对象共有,不受限于某个对象,不能作为虚函数内联函数因为每个对象有独立的一份函数代码,无映射关系,不能作为虚函数信息科学与工程学院第8章继承与多态478.6.1虚函数的定义成员函数设置为虚函数的要点析构函数可定义为虚函数,为实现撤消对象时的多态性,必须把析构函数定义为虚函数构造函数不能作为虚函数,因为在调用构造函数时对象还没有完成实例化信息科学与工程学院第8章继承与多态488.6.1虚函数的定义成员函数设置为虚函数的要点为了实现多态性,每一个派生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现,因此多态性总是要付出一定代价,执行速度要稍慢一些,但通用性是一个更高的目标如果定义放在类外,virtual只能加在函数声明前面,不能(再)加在函数定义前面信息科学与工程学院第8章继承与多态49【例8.5_1】根据赋值兼容规则可以用基类的指针指向派生类对象,如果由该指针撤销派生类对象,则必须将析构函数说明为虚函数,实现多态性,自动调用派生类析构函数classPerson{

…public: virtual~Person();

…};Studentstu1(…);…

Person*pper4;Student*pstu4=newStudent;

*pstu4=stu1;pper4=pstu4;deletepper4;

信息科学与工程学院第8章继承与多态508.6.2纯虚函数

纯虚函数(purevirtualfunction)是指被标明为不具体实现的虚拟成员函数它用于这样的情况:定义一个基类时,会遇到无法定义基类中虚函数的具体实现,其实现依赖于不同的派生类定义格式:

virtual

返回类型函数名(参数表)=0;含有纯虚函数的基类是不能用来定义对象的。由于纯虚函数没有实现部分,不能产生对象,所以含有纯虚函数的类是抽象类。信息科学与工程学院第8章继承与多态518.6.2纯虚函数定义纯虚函数的要点定义纯虚函数时,不能定义虚函数的实现部分,即使是函数体为空也不可以(函数体为空就可以执行,只是什么也不做就返回,而纯虚函数不能调用)=0表明程序员将不定义该函数,函数声明是为派生类保留一个位置本质上是将指向函数体的指针定为NULL在派生类中必须有重新定义的纯虚函数的函数体,这样的派生类才能用来定义对象信息科学与工程学院第8章继承与多态52【例8.8】学校对在册人员进行奖励,依据是业绩分但是业绩分的计算方法只能对具体人员进行,例如:学生,教师,行政人员,工人,算法都不同可以将在册人员类作为一个抽象类,业绩计算方法作为一个纯虚函数信息科学与工程学院第8章继承与多态53classPerson{

intMarkAchieve; stringName;public: Person(stringname){Name=name;MarkAchieve=0;}

voidSetMark(intmark){MarkAchieve=mark;}

virtual

void

CalMark()=0;//CalMark()为虚函数,Person为抽象类

voidPrint()

{cout<<Name<<"的业绩分为:"<<MarkAchieve<<endl;}};classStudent:public

Person{

intcredit,grade;

//学历和成绩public: Student(stringname,intcred,intgrad):Person(name) { credit=cred;grade=grad; }

void

CalMark(){SetMark

温馨提示

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

最新文档

评论

0/150

提交评论