【文档说明】C++程序设计第10章-继承和派生类-课件.ppt,共(63)页,212.012 KB,由小橙橙上传
转载请保留链接:https://www.ichengzhen.cn/view-44704.html
以下为本文档部分文字说明:
第10章继承和派生类目的与要求10.1继承与派生10.2冲突、支配规则和赋值兼容性10.3虚基类10.4静态数据成员本章小结目的与要求通过本章学习,应理解继承与派生的概念,掌握派生类的定义语句格式及使用方法。知道基类成员经
公有或私有派生后在派生类中的访问权限。初步掌握冲突、支配规则、赋值兼容性的概念。了解虚基类的概念、虚基类的语句定义格式及使用方法。了解静态成员的定义格式、初始化方式及作用域。10.1继承与派生继承:从已有类出发建立新的类,使新类部分或全部地
继承已有类的成员。派生:通过继承已有的一个或多个类产生一个新类称为派生。10.1.1继承与派生的基本概念1.继承与派生的定义在定义类B时,若使用类A的部分或全部成员,则称类B继承了类A,并称类A为基类或父类,称类B
为派生类或子类。基类与派生类或父类与子类的关系可以用图表示。基类(父类)A派生类(子类)B继承与派生2.单一继承若派生类是由一个基类派生出来的,称为单一继承,如派生类Score是由一个基类Student派生出的,所以为单一继承。如图所示。基
类(父类)Student派生类(子类)Score单一继承3.多重继承若派生类由多个基类派生出,称为多重继承。如图所示。多重继承基类(父类)Student基类(父类)Score派生类(子类)Information10.1.2派生类的定义1.定义派生类的格式c
lass<派生类名>:<access><基类名1>〔,…,<access><基类名n>〕{…};说明:(1)派生类由n个基类多重派生,当n=1时为单一继承。(2)访问权限access={public、private
与protected}2.公有派生若在定义派生类时,access为public,则定义公有派生。公有派生时,基类中所有成员在派生类中保持各个成员的访问权限。具体访问权限如下:(1)基类中public成员在派生类仍保持为
public成员,所以在派生类内、外都可直接使用这些成员。(2)基类中private成员属于基类私有成员,所以在派生类内、外都不能直接使用这些成员。只能通过该基类公有或保护成员函数间接使用基类中的私有成员。公有派生(3)基类中p
rotected成员可在派生类中直接使用,但在派生类外不可直接访问这类成员,必须通过派生类的公有或保护成员函数或基类的成员函数才能访问。【例10.1】用学生档案类Student派生出学生成绩类Score。讨论基类中公有
、私有与保护数据成员在派生类中访问权限的变化。例程3.私有派生若在定义派生类时,access为private则定义了私有派生。经过私有派生后:(1)基类中公有成员在派生类变为私有成员,在派生类内可以使用,而在派生类外不能直接使用。(2
)基类中保护成员在派生类变为私有成员,在派生类内可以使用,而在派生类外不能直接使用。(3)基类中私有成员在派生类内、外都不能直接使用,必须通过基类公有函数使用。10.1.3派生类的构造函数与基类成员的初始化1.派生类构造函数格式派生类的构造函数由初始化基类数据
成员构造函数与初始化派生类新增的数据成员构造函数组成。派生类构造函数的格式为:<派生类名>::<派生类名>(形参表):<基类构造函数名1>(实参表1)〔,…,<基类构造函数名n>(实参表n)〕{派生类构造函数体}派生
类构造函数格式说明:(1)基类构造函数实参可以是表达式或派生类构造函数的形参。(2)实参只与形参名有关,而与参数顺序无关。(3)冒号后基类构造函数列表称为初始化成员列表。派生类构造函数举例【例10.2】多重派生实例。定义描述圆的类Circle,定义描述高的类
High,用描述圆的类与描述高的类作为基类,多重派生出圆柱体类Cylinder。讨论多重继承中基类成员的初始化问题。例程2.建立对象时,构造函数的调用顺序先调用基类构造函数,再调用派生类构造函数。注意:(1)在派生类中并不能直接对基类的私有数据成赋初始值,必须通过公有的构造函数进行初始
化工作。(2)基类构造函数的调用顺序取决于它们在派生类中说明顺序,而与它们在派生类构造函数中的顺序无关。3.撤消对象时,析构函数的调用顺序在撤消派生类的对象时,析构函数的调用顺序正好与构造函数的顺序相反。即:先调用派生类的析构函数,再调用基类析构函数。【例10
.3】定义两个基类Base1与Base2,并由Base1与Base2派生出派生类Derive。编写程序,输出派生类中构造函数与析构函数的调用关系。例程4.派生类中包含对象成员的构造函数若派生类中包含对象成员,则在派生类构造函数的初始化成员列表中不仅要列举要调用的基类构造函数,而且要列举调用的对象成
员构造函数。【例10.4】将[例10.3]的派生类改为包含对象成员的派生类。例程首先要调用基类的构造函数,再调用对象成员的构造函数,最后执行派生类的构造函数。在有多个对象成员的情况下,调用这些对象成员的构造函数的顺序取决于它们在派生类中说明顺
序。10.2冲突、支配规则和赋值兼容性10.2.1冲突引例:【例10.5】在[例10.2]中将描述高的类改为描述矩形的类Rectangle,在Rectangle类中用矩形中心坐标(x,y)、高(High)与宽(Width)作为类的数据成员。例程冲突:
派生类使用基类中同名成员时出现不惟一称为冲突。解决方法:使用作用域运算符“::”指明同名成员属于哪一个基类,即:<基类名>::<成员名>冲突结论:若一个公有派生类是由两个或多个基类派生,当基类中成员的访问权限为publi
c或protected且不同基类中的成员同名时,派生类使用到基类中的同名成员时出现不惟一性称为冲突。解决方法是使用作用域运算符指明发生冲突的成员属于哪个基类。10.2.2支配规则所谓支配规则是指,当派生类中成员与基类中成员同名时
,在不用作用域运算符时,派生类成员名优于基类成员名。【例10.6】在【例10.5】的派生类Cylinder中,新增数据成员(x,y,z)表示圆柱体中心坐标。并从基类Rectangle中删除(x,y)。例程注意:当派生的成员名与基
类的成员名相同时,在派生类中或派生类外要使用基类中的这种成员时,仍要使用作用域运算符。10.2.3赋值兼容规则公有派生类的对象可赋给其基类对象,基类对象不能赋给派生类对象的规则,称为赋值兼容规则。10.2.4基类和对象成员的
几点说明(1)任一基类在派生类中只能继承一次。(2)基类成员与对象成员在使用上的差别①派生类中可直接使用基类的成员(访问权限允许的话);②使用对象成员时,必须在对象名后加上成员运算符“.”和成员名。10.3虚基类10.3.1多重派生的基类拷贝若类B与类C由类A公有派生,而类D由
类B与类C公有派生,则类D中将包含类A的两个拷贝(如图所示)。这种同一个基类在派生类中产生多个拷贝不仅多占用了存储空间,而且可能会造成多个拷贝数据的不一致。基类A基类A基类B基类C派生类D派生类中包含同一基类的两
个拷贝多重派生的基类拷贝【例10.7】一个公共基类在派生类中产生两个拷贝。例程如果在多条继承路径上有一个公共的基类,则该基类会在这些路径中的某几条路径的汇合处产生几个拷贝。为使这样的公共基类只产生一个拷贝,须将该基类说明为虚基类。10.
3.2虚基类在多重派生的过程中,欲使公共的基类在派生中只有一个拷贝,可将此基类说明成虚基类。虚基类的定义格式为:class<派生类名>:virtual<access><虚基类名>{…};或class<派生类名>:<acc
ess>virtual<虚基类名>{…};【例10.8】定义虚基类,使派生类中只有基类的一个拷贝。例程10.4静态数据成员将类的某一个数据成员的存储类型定义为静态类型时,则由该类所产生的所有对象均共享为静态成员所分配的一个存储空
间。(1)静态数据成员的定义与引用方法①在类中对静态数据成员作引用性说明static<类型><静态数据成员名>;②在类外文件作用域对静态数据成员作定义性说明必须在类外的文件作用域中,且只能作一次定义性说明,并分配内存空间。<类型><类名>::<静态数据成员名>〔=初值〕;定义性说
明时,静态数据成员缺省初值为0。③静态数据成员引用格式<类名>::<静态数据成员名>静态数据成员(2)同类不同对象的静态数据成员占用相同的存储空间。(3)静态数据成员置初值不受访问权限的限制。(4)为了保持静态数据成员取值的一致性,通常在构造函数中不给静态数据置初值,而
是在静态数据成员的定义性说明时指定初值。本章小结1.继承继承:从已有类出发建立新的类,使新类部分或全部地继承已有类的成员称为继承。派生:通过继承已有的一个或多个类产生一个新类称为派生。派生类的定义格式如下:cl
ass<派生类名>:<access><基类名1>〔,…,<access><基类名n>〕{派生类体};基类成员在派生类中访问权限基类成员访问权限公有派生后的访问权限私有派生后的访问权限publicpublicprivatepr
ivate不可直接访问不可直接访问protectedprivateprivate公有或私有派生后基类成员在派生类中访问权限派生类构造函数和析构函数因为派生类成员由基类成员与派生类中新增加的成员组成,所以初始化工作应分为对派生类中新增成员的初始化与基类成员的初始化。由于初始化工作是由构造
函数完成的,所以初始化基类成员的工作是由派生类的构造函数来完成的。派生类构造函数的格式如下:<派生类名>::<派生类构造函数名>(形参表):<基类构造函数名1>(实参表1)…<基类构造函数名n>(实参表n){…};构造函数的调用顺序是先基类后派生类,析构函数的调用
顺序是先派生类后基类。2.冲突、支配和赋值兼容性冲突:派生类使用基类中同名成员时出现不唯一称为冲突。冲突的解决方法:<基类名>::<成员名>支配规则:使用派生类中与基类中同名成员时,派生类成员优于基类同名成员的规则称为支配
规则。赋值的兼容性:派生类对象可赋值给基类对象,基类对象不能赋给派生类对象称为赋值的兼容性。3.虚基类在多重派生的过程中,欲使公共的基类在派生类中只有一个拷贝,可将此基类说明成虚基类。虚基类的定义格式为:class<派生类名>:virtual<access><虚基
类名>{…};或class<派生类名>:<access>virtual<虚基类名>{…};4.静态数据成员静态数据成员的定义必须分两步完成,在类内作引用性说明,在类外作定义性说明。说明与引用格式为:类内
作引用性说明的格式:static<类型><数据成员>;类外作定义性说明的格式:<类型><类名>::<静态数据成员>〔=初值〕;引用格式:<类名>::<静态数据成员>例10.1(1)#include<iostream.h>classStudent{private:intNo;
//定义No为私有数据成员protected:intAge;//定义Age为保护的数据成员public:charSex;//定义Sex为公有数据成员Student(intno,intage,charsex)//定义类Student的构造函数{No=no;Age=age
;Sex=sex;}intGetNo(){returnNo;}//返回No的公有成员函数intGetAge(){returnAge;}//返回Age的公有成员函数voidShowS()//显示No、Age、Sex的公有成员函数{cout<<"No="<<No<<'\t'<<"Age="
<<Age<<'\t'<<"Sex="<<Sex<<endl;}};例10.1(2)classScore:publicStudent//由基类Student公有派生出子类Score{private:intPhi,Math;//定义
类Score的私有数据成员public:Score(intn,inta,chars,intp,intm):Student(n,a,s)//类Score的构造函数{Phi=p;Math=m;}voidShow(void)//显示类Score与其父类Student的数据成员值{co
ut<<"No="<<GetNo()<<'\t'<<"Age="<<Age<<'\t'<<"Sex="<<Sex<<'\t'<<"Phi="<<Phi<<'\t'<<"Math="<<Math<<endl;}};例10.1(3)voidmain(void){Sc
ores(101,20,’M’,90,80);//用类Score定义一个对象ss.ShowS();//类Score的对象s调用基类公有函数ShowS()s.Show();//类Score的对象调用公有函数Show()cout<<"No="
<<s.GetNo()<<'\t'<<"Age="<<s.GetAge()<<'\t'<<"Sex="<<s.Sex<<endl;}程序执行后输出:No=101Age=20Sex=MNo=101Age=20Sex=MPhi=90Math=
80No=101Age=20Sex=M返回例10.2(1)#include<iostream.h>classCircle//定义描述圆的类,其中(x,y)为圆心,r为半径{protected:floatx,y,r;
public:Circle(floata,floatb,floatc){x=a;y=b;r=c;}};classHigh//定义描述高的类{private:floath;public:High(floata){h
=a;}floatGeth(){returnh;}};例10.2(2)classCylinder:publicCircle,privateHigh//由圆与高派生出圆柱体类{private:floatVolume;public:Cylinder(floata,floa
tb,floatc,floatd):Circle(a,b,c),High(d)//D{Volume=r*r*3.1415*Geth();}//EvoidShow(){cout<<"x="<<x<<'\t'<<"y=
"<<y<<'\t'<<"r="<<r<<'\t'<<"h="<<Geth()<<'\t'<<"V="<<Volume<<endl;}};例10.2(3)voidmain(void){Cylindercy(3,3,2,
10);cy.Show();}程序执行后输出:x=3y=3r=2h=10V=125.664返回例10.3(1)#include<iostream.h>classBase1//定义基类Base1{private:intx;//定义基类Ba
se1的私有数据成员xpublic:Base1(inta)//基类Base1的构造函数{x=a;cout<<"调用基类1的构造函数!"<<endl;}~Base1()//基类Base1的析构函数{cout<
<"调用基类1的析构函数!"<<endl;}};例10.3(2)classBase2//定义基类Base2{private:inty;//定义基类Base2的私有数据成员ypublic:Base2(i
nta)//基类Base2的构造函数{y=a;cout<<"调用基类2的构造函数!"<<endl;}~Base2()//基类Base2的析构函数{cout<<"调用基类2的析构函数!"<<endl;}};例10.3(3)classDeriv
e:publicBase1,publicBase2//派生类Derive{private:intz;//派生类Derive新增的私有数据成员public:Derive(inta,intb):Base1(a),Base2(20)//派生类Derive构造函数{z=b;
cout<<"调用派生类构造函数!"<<endl;}~Derive()//派生类Derive的析构函数{cout<<"调用派生类的析构函数!"<<endl;}};voidmain(void){Derivec(100,
200);}例10.3(4)程序执行后输出:调用基类1的构造函数!调用基类2的构造函数!调用派生类的构造函数!调用派生类析构函数!调用基类2的析构函数!调用基类1的析构函数!返回例10.4(1)classDerive:p
ublicBase1,publicBase2{private:intz;Base1b1,b2;//在派生类中定义基类对象成员b1,b2public:Derive(inta,intb):Base1(a),Base2(20),b1
(200),b2(a+b)//定义构造函数{z=b;cout<<"调用派生类的构造函数!"<<endl;}~Derive(){cout<<"调用派生类的析构函数!"<<endl;}};voidmain(void){Derived(100,2
00);}例10.4(2)执行程序后输出:调用基类1的构造函数!调用基类2的构造函数!调用基类1的构造函数!调用基类1的构造函数!调用派生类的构造函数!调用派生类的析构函数!调用基类1的析构函数!调用基类1的析构函数!调用基类2的析构函数!调用基类1的析构函数!返回例10.5(1)#inclu
de<iostream.h>classCircle//定义描述圆的基类{protected:floatx,y,r;//(x,y)为圆心,r为半径public:Circle(floata,floatb,floatc){x=a;y
=b;r=c;}floatArea(){return(r*r*3.14159);}//计算圆的面积};例10.5(2)classRectangle//定义描述矩形的基类{protected:floatx,y,h,w;public
:Rectangle(floata,floatb,floatc,floatd){x=a;y=b;h=c;w=d;}floatArea(void)//计算矩形面积{returnh*w;}};例10.5(3)classCylinder:publicCircle,publicRectangle//描述一
个圆柱体的派生类{private:floatVolume;//圆柱体的体积public:Cylinder(floata,floatb,floatc):Circle(a,b,c),Rectangle(10,10,c,c)//A{Volume=Area()*h
;}//BfloatGetV(){returnVolume;}voidShow(void){cout<<"x="<<x<<'\t'<<"y="<<y<<endl;}//C};例10.5(4)voidmain(v
oid){Cylindercy(3,3,2);cy.Show();cout<<"Volume="<<cy.GetV()<<endl;}程序执行后输出:x=3y=3Volume=25.1327返回例10.5(5)#include<iostream.h>classCircle//定
义描述圆的基类{protected:floatx,y,r;//(x,y)为圆心,r为半径public:Circle(floata,floatb,floatc){x=a;y=b;r=c;}floatArea(){return(r*r*3.14159);}//计算圆的面积};例1
0.5(6)classRectangle//定义描述矩形的基类{protected:floatx,y,h,w;public:Rectangle(floata,floatb,floatc,floatd){x=a;y=b;h=c;w=d;}floatArea(
void)//计算矩形面积{returnh*w;}};例10.5(7)classCylinder:publicCircle,publicRectangle//描述一个圆柱体的派生类{private:floatV
olume;//圆柱体的体积public:Cylinder(floata,floatb,floatc):Circle(a,b,c),Rectangle(10,10,c,c)//D{Volume=Circle::Area()*h;}//Efloa
tGetV(){returnVolume;}voidShow(void){cout<<"x="<<Circle::x<<'\t'<<"y="<<Circle::y<<endl;}//F};例10.5(8)voidmain(void){Cylindercy(3,3,2);cy.Show
();cout<<"Volume="<<cy.GetV()<<endl;}程序执行后输出:x=3y=3Volume=25.1327返回例10.6(1)#include<iostream.h>classCircle//定义描述圆的基类,其中(x,y)为圆心,r为半径{protect
ed:floatx,y,r;public:Circle(floata,floatb,floatc){x=a;y=b;r=c;}floatArea(){return(r*r*3.14159);}//计算圆的面积};classRectangle//定义描述
矩形的基类{protected:floath,w;public:例10.6(2)Rectangle(floatc,floatd){h=c;w=d;}floatArea(void)//计算矩形面积{returnh*w;}};classCylinder:publicCi
rcle,publicRectangle//描述一个圆柱体的派生类{private:floatx,y,z,Volume;//圆柱体的中心坐标与体积public:Cylinder(floata,floatb,fl
oatc,floatd):Circle(a,b,d),Rectangle(d,d){x=a;y=b;z=c;Volume=Circle::Area()*h;}例10.6(3)floatGetV(){returnVolume;}voidShow(v
oid){cout<<"x="<<x<<'\t'<<"y="<<y<<'\t'<<"z="<<z<<endl;}};voidmain(void){Cylindercy(3,3,3,2);cy.Show();cout<<"Volume="<<cy.GetV()<<endl;}程序执行后
输出:x=3y=3z=3Volume=25.1327返回例10.7(1)#include<iostream.h>classA{public:intx;A(inta){x=a;}};classB:publicA//由公共基类A派生出类B{public:inty;B(inta,intb):A(b
){y=a;}};例10.7(2)classC:publicA//由公共基类A派生出类C{public:intz;C(inta,intb):A(b){z=a;}};classD:publicB,publicC//由基类B
、C派生出类D{public:intm;D(inta,intb,intd,inte,intf):B(a,b),C(d,e){m=f;}voidPrint(){cout<<"x="<<B::x<<'\t'<<"y="<
<y<<endl;cout<<"x="<<C::x<<'\t'<<"z="<<z<<e例10.7(3)cout<<”m=”<<m<<endl;}};voidmain(void){Dd1(100,200,300,400,500);d1.Print();}程序执行后输出:x=
200y=100x=400z=300m=500返回例10.8(1)#include<iostream.h>classA{public:intx;A(inta=0){x=a;}};classB:virtualpublicA//由公共基类A派生出类B{public:inty;B
(inta,intb):A(b){y=a;}};例10.8(2)classC:publicvirtualA//由公共基类A派生出类C{public:intz;C(inta,intb):A(b){z=a;}
};classD:publicB,publicC//由基类B、C派生出类D{public:intm;D(inta,intb,intd,inte,intf):B(a,b),C(d,e){m=f;}例10.8(3)VoidPrint()
{cout<<"x="<<x<<'\t'<<"y="<<y<<endl;cout<<"x="<<x<<'\t'<<"z="<<z<<endl;cout<<"m="<<m<<endl;}};voidmain(void){Dd1(100,200,300,400,500);d1.Print();
d1.x=400;d1.Print();}例10.8(4)执行程序后输出:x=0y=100x=0z=300m=500x=400y=100x=400z=300m=500返回