C程序设计第8章1.ppt

上传人:sccc 文档编号:5336454 上传时间:2023-06-27 格式:PPT 页数:44 大小:206.01KB
返回 下载 相关 举报
C程序设计第8章1.ppt_第1页
第1页 / 共44页
C程序设计第8章1.ppt_第2页
第2页 / 共44页
C程序设计第8章1.ppt_第3页
第3页 / 共44页
C程序设计第8章1.ppt_第4页
第4页 / 共44页
C程序设计第8章1.ppt_第5页
第5页 / 共44页
点击查看更多>>
资源描述

《C程序设计第8章1.ppt》由会员分享,可在线阅读,更多相关《C程序设计第8章1.ppt(44页珍藏版)》请在三一办公上搜索。

1、本周习题:P307 习题8.6 实践教程:P89 实验二十四 2本周实验:习题上机调试 预习:第9章流类库与输入/输出 9.19.3(1)理解C+的基本流类体系;(2)提高标准输入/输出的健壮性;(3)掌握提取与插入运算符的重载(4)掌握文件的输入/输出:文件的打开与关闭,文本 文件与二进制文件。(5)掌握在构造函数中通过文件建立对象,在析构函数 中由文件保存对象的技术。,类似用实型变量描述整数,这条规则称赋值兼容规则。赋值兼容包括以下三种情况:,派生类与基类(赋值兼容),1.派生类对象赋值给基类的对象。反之不行。如:Person p;Student s;p=s;,2.派生类对象的地址赋给基类

2、指针。反之不行。如:Person*pointP;pointP=,3.派生类对象可以初始化基类的引用。反之不行。如:Person 常见用法是函数形参为基类引用,实参是派生类对象,注意:以上赋值后都只能间接使用到继承来的成员。,#include class A public:void show()coutA:show()endl;class B:public A public:void show()coutB:show()endl;class C:public Bpublic:void show()coutC:show()endl;,void main()A a;/声明A类对象B b;/声明B类对

3、象C c;/声明C类对象A*p;/声明A类指针p=,输出:A:display()A:display()A:display(),解决办法:虚函数,如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员。-二义性,解决办法:定义虚基类,使得在继承间接共同基类时只保留一份成员。,为保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类。否则仍然会出现对基类的多次继承。,声明虚基类格式:class 派生类名:virtual 继承方式 基类名,虚基类的初始化:class A public:A(int i);clas

4、s B:virtual public A public:B(int n):A(n);class C:virtual public A public:C(int n):A(n);class D:public B,public C public:D(int n):A(n),B(n),C(n);,注意:前介绍,在派生类的构造函数中只需对其直接基类初始化,再由其直接基类负责对间接基类初始化。,2.现在,由于虚基类在派生类中只有一份数据成员,所以这份数据成员的初始化必须由派生类直接给出。,派生类不仅要负责对其直接基类初始化还要负责对虚基类初始化。,在派生类对象的创建中:首先是虚基类的构造函数并按它们声明

5、的顺序构造。第二批是非虚基类的构造函数按它们声明的顺序调用。第三批是成员对象的构造函数。最后是派生类自己的构造函数被调用。,构造函数执行次序:,析构的次序与构造的次序相反。,例:通过构造函数的初始化表对虚基类进行初始化。#include class A/定义基类Apublic:int x;A(int a=0)x=a;/基类构造函数,有一个参数A()coutA析构endl;class B:virtual public A/A作为B的虚基类public:int y;B(int a=0,int b=0):A(b)y=a;/B类构造函数,在初始化表中对虚基类初始化B()coutB析构endl;void

6、 PB()coutx=xty=yendl;,class C:virtual public A/A作为C的虚基类public:int z;C(int a=0,int b=0):A(b)z=a;/C类构造函数,在初始化表中对虚基类初始化C()coutC析构endl;void PC()coutx=xtz=zendl;class D:public B,public Cpublic:int m;D(int a,int b,int d,int e,int f):B(a,b),C(d,e)m=f;D()coutD析构endl;void Print()PB();PC();coutm=mendl;,A(),如果

7、在虚基类中定义了带参数的构造函数,则在其所有派生类中(包括直接派生或间接派生的派生类)中,通过构造函数的初始化表对虚基类进行初始化。,void main()D d1(100,200,300,400,500);d1.Print();d1.x=400;d1.Print();,输出:x=0 y=100/基类构造函数使用了默认值做参数x=0 z=300/基类构造函数使用了默认值做参数m=500 x=400 y=100/主函数改变了基类参数值x=400 z=300/主函数改变了基类参数值m=500D析构C析构B析构A析构,D(int a,int b,int d,int e,int f):A(),B(a,

8、b),C(d,e)m=f;,8.7 多态性与虚函数,多态性是面向对象程序设计的关键技术之一。指的是调用同样的函数实现不同的功能。,在C+中有两种多态,编译时的多态性(静态多态性),运行时的多态性,在程序执行前无法确定调用哪一个函数,必须在程序执行过程中,根据执行的具体情况来动态地确定。通过类继承关系和虚函数来实现的。目的:建立一种通用的程序。,通过函数的重载和运算符的重载实现。,多态性是“一个接口,多种方法”:通过继承产生了相关的不同的派生类,和基类成员同名的成员在不同的派生类中有不同的含义。,虚函数的作用:允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中

9、的同名函数。,虚函数是一个类的成员函数,定义虚函数的格式如下:virtual 返回类型 函数名(参数表);,注:virtual仅用于类定义中,若虚函数在类外定义,不能 加virtual(类中函数说明要加virtual)。,当某一个类的一个类成员函数被定义为虚函数,则由该类派生出来的所有派生类中,该函数始终保持虚函数的特征。,8.7.1 虚函数的定义,在派生类中重新定义虚函数(称超载或覆盖)时,不必再加关键字virtual,但函数头一定要完全相同。,虚函数的使用:(1)在基类用virtual声明成员函数为虚函数。,(2)在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类

10、的虚函数相同,并根据派生类的需要重新定义函数体。,(3)定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用该函数的对象。,(4)通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数。,例:#includeclass Apublic:virtual void show();void A:show()coutA:show()endl;class B:public Apublic:void show()coutB:show()endl;class C:public Bpublic:void show()coutC:show()endl;,void main()A a1,*

11、p;B b1;C c1;p=/动态调用,运行结果:A:show()B:show()C:show(),void main()C c;c.A:show();/静态调用c.B:show();/静态调用 c.show();/静态调用,【例8.6】计算学分。由本科生类派生出研究生类GradeStudent,但它们各自的从课程学时数折算为学分数的算法是不同的,本科生是16个学时一学分,而研究生是20个学时一学分。class Studentchar coursename20;/课程名int classhour;/学时int credit;/学分,未考虑0.5学分public:Student();/构造函数,

12、将类成员赋值为0void Calculate();/计算学分(考虑该函数的设置)void SetCourse(char*str,int hour);/设置课程和学时int GetHour();/获取学时数void SetCredit(int cred);/获取学分void Print();/显示学时和学分;,class Student char coursename20;/课程名 int classhour;/学时 int credit;/学分public:Student()coursename0=0;classhour=0;credit=0;virtual void Calculate()c

13、redit=classhour/16;void SetCourse(char*str,int hour)/设置课程和学时 strcpy(coursename,str);classhour=hour;,int GetHour()return classhour;/获取学时数 void SetCredit(int cred)credit=cred;/获取学分 voidPrint()/显示学时和学分 coutcoursenametclasshour学时 tcredit学分endl;,class GradeStudent:public Studentpublic:GradeStudent();/基类缺

14、省构造函数调用可省 void Calculate()SetCredit(GetHour()/20);,void main()Student s,*ps;GradeStudent g;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=,输出结果为:本科生:物理 80 学时 5学分研究生:物理 80 学时 4学分本科生:数学 160学时 10学分研

15、究生:数学 160学时 8学分,使用基类引用去指向不同对象,同样可实现运行时多态性。,void Calfun(Student,【例8.7】计算学分,基类与派生类定义同【例8.6】(基类派生类函数不变),增加函数:,几点提示:,5.虚函数重构不同于重载。派生类中定义虚函数必须与基类中的虚函数同名外,还必须同参数表,同返回类型。否则被认为是重载,而不是虚函数。,1.实现动态多态性需要三个条件:派生类体系、虚函数、指针或引用。,2.析构函数可定义为虚函数,构造函数不能定义虚函数。在基类及其派生类都有动态分配的内存空间时,应当考虑把析构函数定义为虚函数,以便能够用基类指针实现撤消对象的多态性。,3.虚

16、函数执行速度稍慢。为了实现多态性,每一个派生类中均要保存相应虚函数的入口地址表,函数的调用机制也与一般函数不同。这是多态性为实现通用性付出的代价。,4.静态成员函数不能作为虚函数,因为它不属于某个对象,而是为所有同类对象共有。内联函数也不能作为虚函数。,声明虚函数的成员函数主要考虑以下几点:,首先看成员函数所在的类是否会作为基类。然后看成员函数在类的继承后有无可能被更改功能,如果希望更改其功能的,一般应该将它声明为虚函数。,(2)如果成员函数在类被继承后功能不需修改,或派生类用不到该函数,则不要把它声明为虚函数。,(3)应考虑对成员函数的调用是通过对象名还是通过基类指针或引用去访问,如果是通过

17、基类指针或引用去访问的,则应当声明为虚函数。,虚析构函数,析构函数的作用:在对象撤销之前做必要的“清理现场”的工作。当派生类的对象从内存中撤销时一般先调用派生类的析构函数,然后再调用基类的析构函数。,如果用new运算符建立了临时对象,若基类中有析构函数,并且定义了一个指向该基类的指针变量。在程序用带指针参数的delete运算符撤销对象时,会出现:系统会只执行基类的析构函数,而不执行派生类的析构函数。,解决办法:把析构函数定义为虚函数,实现撤消对象时的多态性。,虚析构函数声明:virtual 类名();,#includeclass A public:A()coutA析构n;class B:pub

18、lic A public:B();B();private:int*p;,B:B()p=new int();B:B()coutB析构n;delete p;void fun(A*a)delete a;void main()A*a=new B;/a是指向基类的指针变量指向new开辟的动态存储空间 fun(a);,输出:A析构,virtual,输出:B析构 A析构,把基类的析构函数声明为虚函数。可使所有派生类的析构函数自动成为虚函数(即使函数名不同)。若程序中显式地用了delete运算符准备删除一个对象,而delete运算符的操作对象用了指向派生类对象的基类指针,系统会调用相应类的析构函数。,1.虚函

19、数是动态关联的基础。,2.虚函数是非静态的成员函数。,3.virtual 只用来说明类声明中的原型,不能用在函数实 现时。,4.具有继承性,基类中声明了虚函数,派生类中无论是 否说明,同原型函数都自动为虚函数。,5.不是重载声明而是覆盖。,6.通过基类指针或引用,执行时会根据指针指向的对象的类,决定调用哪个函数。,【例8.5_1】把【例8.1】析构函数改造为虚函数。,一般为了将类设计成通用的,必须把析构函数定义为虚函数。在动态分配内存时所有C+的标准库函数都采用这种格式。,class Person/数据成员略public:virtual Person();/其他成员函数略;,在主函数中添加以下

20、内容:Person*per4;Student*stu4=new Student;*stu4=stu1;/把stu1的数据拷入*stu4 stu4-PrintStudentInfo();per4=stu4;delete per4;/基类指针撤销派生类对象,必须显式撤销,例:,class Pointpublic:.double Area()return 0;.;,class Circle:public Pointpublic:double Area()return PI*radius*radius;,基类函数为空或可以不要,在实现部分仍然要写出函数体,纯虚函数可以解决接口,8.7.2 纯虚函数,1

21、.纯虚函数不同于空函数,纯虚函数不能调用。2.派生类中必须重新定义纯虚函数的函数体。,纯虚函数(pure virtual function)当基类中某虚函数无法具体实现,可定义为纯虚函数,具体实现依赖于派生类。定义格式为:,定义纯虚函数要注意:,virtual 返回类型 函数名(参数表)=0;,含有纯虚函数的基类不能用来定义对象,称为抽象类。抽象类的意义在于定义框架。,注:纯虚函数没有函数体;最后面的“=0”并不表示函数返回值为0,它只起形式 上的作用,告诉编译系统“这是纯虚函数”;这是一个声明语句,最后应有分号。,含有纯虚函数的基类是不能用来定义对象的。纯虚函数没有实现部分,不能产生对象,含

22、有纯虚函数的类是抽象类。,抽象类,带有纯虚函数的类称为抽象类:class 类名 virtual 类型 函数名(参数表)=0;/纯虚函数.,作用:抽象类为抽象和设计的目的而建立,将有关的数据和 行为组织在一个继承层次结构中,保证派生类具有要 求的行为。,对于暂时无法实现的函数,可以声明为纯虚函数,留 给派生类去实现。,注意,抽象类只能作为基类来使用。不能声明抽象类的对象。构造函数不能是虚函数,析构函数可以是虚函数。,#include class B0/抽象基类B0声明public:/外部接口virtual void display()=0;/纯虚函数成员;class B1:public B0/公

23、有派生public:void display()/虚成员函数 coutB1:display()endl;,class D1:public B1/公有派生public:void display()/虚成员函数 coutdisplay();,void main()B0*p;/声明抽象基类指针B1 b1;/声明派生类对象D1 d1;/声明派生类对象p=/调用派生类D1函数成员,程序的运行结果为:B1:display()D1:display(),结论:一个基类如果包含一个或一个以上纯虚函数,就是抽 象基类。,(2)派生类如果没有实现基类中的全部纯虚函数,则该派 生类仍然是抽象类;,(3)在类的层次结构

24、中,顶层或最上面的几层可以是抽象 基类。抽象基类体现了本类族中各类的共性,把各类 中共有的成员函数集中在抽象基类中声明。,(4)抽象基类是本类族的公共接口。,(5)区别静态关联和动态关联。通过对象调用虚函数静态关联 通过基类指针调用虚函数动态关联,(6)如果在基类声明了虚函数,则在派生类中凡是与该函 数有相同的函数名、函数类型、参数个数和类型的函 数,均为虚函数(不论在派生类中是否用virtual声明)。,把类的声明与类的使用分离。对于设计类库的软件开发商来说尤为重要。开发商设计了各种各样的类,但不向用户提供源代码,用户可以不知道类是怎样声明的,但是可以使用这些类来派生出自己的类。,(7)使用

25、虚函数提高了程序的可扩充性。,class Person int MarkAchieve;/业绩分 char Name20;public:Person(char*name)strcpy(Name,name);MarkAchieve=0;void SetMark(int mark)MarkAchieve=mark;virtual void CalMark()=0;void Print()coutName的业绩分为:MarkAchieveendl;,【例8.8】学校对在册人员进行奖励,依据是业绩分。业绩分的计算方法只能对具体人员进行,各类人员算法不同,所以将在册人员类定义为抽象类,业绩计算方法为纯虚

26、函数。,class Student:public Person int credit,grade;/学历和成绩public:Student(char*name,int cred,int grad):Person(name)credit=cred;grade=grad;void CalMark()SetMark(credit*grade);,class Teacher:public Person int classhour,studnum;/授课学时和学生人数public:Teacher(char*name,int ch,int sn):Person(name)classhour=ch;stud

27、num=sn;void CalMark()int K=(studnum+15)/30;/工作量系数,30人一班,15人以下不开课 switch(K)case 1:SetMark(classhour*studnum);break;case 2:SetMark(classhour*(30+(studnum-30)*8/10);break;case 3:SetMark(classhour*(30+24+(studnum-60)*6/10);break;case 4:SetMark(classhour*(30+24+18+(studnum-90)*4/10);break;case 5:SetMark(

28、classhour*(30+24+12+(studnum-120)*2/10);break;default:SetMark(classhour*(30+24+12+6+(studnum-150)*1/10);,int main()Person*pp;Student s1(张成,20,80);Teacher t1(范英明,64,125),t2(李凯,80,85);pp=,class Simpson double Intevalue,a,b;/Intevalue积分值,a积分下限,b积分上限public:virtual double fun(double x)=0;/被积函数声明为纯虚函数 Sim

29、pson(double ra=0,double rb=0)a=ra;b=rb;Intevalue=0;void Integrate()double dx;int i;dx=(b-a)/2000;Intevalue=fun(a)+fun(b);for(i=1;i2000;i+=2)Intevalue+=4*fun(a+dx*i);for(i=2;i2000;i+=2)Intevalue+=2*fun(a+dx*i);Intevalue*=dx/3;,【例8.9】用虚函数来实现辛普生法求函数的定积分。(P210 例6.11),void Print()cout积分值=Intevalueendl;在派生类中加被积函数:class A:public Simpsonpublic:A(double ra,double rb):Simpson(ra,rb);double fun(double x)return sin(x);class B:public Simpson/B也可以说明为由A派生,更利于说明动态多态性public:B(double ra,double rb):Simpson(ra,rb);double fun(double x)return exp(x);,int main()A a1(0.0,3.1415926535/2.0);Simpson*s=,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 建筑/施工/环境 > 农业报告


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号