C语言试题、学习、考试大全38.ppt

上传人:本田雅阁 文档编号:2037526 上传时间:2019-02-07 格式:PPT 页数:58 大小:315.51KB
返回 下载 相关 举报
C语言试题、学习、考试大全38.ppt_第1页
第1页 / 共58页
C语言试题、学习、考试大全38.ppt_第2页
第2页 / 共58页
C语言试题、学习、考试大全38.ppt_第3页
第3页 / 共58页
亲,该文档总共58页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《C语言试题、学习、考试大全38.ppt》由会员分享,可在线阅读,更多相关《C语言试题、学习、考试大全38.ppt(58页珍藏版)》请在三一文库上搜索。

1、目 录,第1章 C+概述 第2章 数据类型、运算符和表达式 第3章 简单的输入/输出 第4章 C+的流程控制 第5章 函数 第6章 编译预处理 第7章 数组 第8章 结构体、共同体和枚举类型 第9章 指针和引用 第10章 类和对象 第11章 类和对象的其他特性 第12章 继承和派生 第13章 多态性 第14章 输入/输出流 第15章 模板,第一部分 面向过程的程序设计,第二部分 面向对象的程序设计,第13章 多态性,13.1 函数重载 13.2 运算符重载 13.3 静态联编 13.4 动态联编和虚函数 13.5 纯虚函数和抽象类,第 13 章 多态性,多态性是实现 OOP 的关键技术之一。,

2、在C+中,多态性分为两种: 静态多态 动态多态,函数重载和运算符重载属于静态多态。 函数重载:相同函数名可以完成不同功能。 运算符重载:相同运算符完成不同功能。,动态多态是指:程序执行过程中确定的关系, 如动态确定函数的调用关系。,运行时的多态(动态多态) 是通过类的继承和虚函数来实现的。,13.1 函数重载 参见5.6节(略),13.2 重载运算符,C+中所有的运算符都已预先定义了用法及意义。 如:+ * / = 等,适用于已有的数据类型。,已定义的用法及意义是不允许用户改变的。,如果用户定义了新的数据类型,希望已定义的运算符 能适应新的数据类型,,如: 前述 Complex 类的加法运算,

3、Complex c1, c2, c3; c3 = add(c1, c2); /以前通过函数实现加法运算 c2.add(c3); / 或通过成员函数实现 c3 = c1 + c2; /能否直观地写成这样?,能 ! 运算符的重载可以达到此目的。,允许重载 和 不允许重载的运算符见13-1和13-2表。,13.2.1 运算符重载的几点说明,重载运算符的限制 (1)只能对已有运算符重载,不可臆造新的运算符。 (2)不允许改变运算符的优先级和结合性。 (3)不允许改变运算符的语法结构, 如二元运算符只能重载成二元运算符, 一元运算符只能重载成一元运算符。,1.重载为类的成员函数,在类内定义运算符重载函数

4、的格式为: operator ( ) ,以 operator 为关键字,编译器可以很容易将 运算符重载函数与其他成员函数区别开来。,例13.2 实现复数类的“+”,“-”等重载运算 关键部分见下页,13.2.2 运算符重载的两种方式,在类外定义运算符重载函数的格式为: :operator ( ) ,class Complex float Real, Image; public: . Complex operator +(const Complex ,void main( ) Complex c1(2, 3), c2(4, -2), c3; c3 = c1+c2 ; c3 = c1+5 ; c3

5、 = - c1; ,编译器将 c1+c2 解释为:c1.operator+(c2),将 c1+5 解释为:c1.operator+(5),第1个运算量是对象,第2个运算量是参数。,阅读教材上程序全文(讲解略),将 - c1 解释为:c1.operator( ),当用成员函数实现运算符的重载时,重载函数的 参数个数只能是 0 个或 1 个。分别实现:一元、二元 运算符的重载。,2.重载为友元函数,例13.3 用友元函数实现复数类的“+”和“” 重载运算 关键部分见下页,class Complex float Real, Image; public: . friend Complex operat

6、or +(const Complex ,在 main( )函数中, 若有 Complex c1,c2; 则编译器将 c1+c2 解释为: operator+(c1, c2),将 c1 解释为:operator(c1),阅读教材上程序全文(讲解略),当用友元函数实现运算符的重载时,重载函数的 参数个数只能是1 个或 2 个。 分别实现:一元运算符重载、二元运算符重载,3.两种重载方式的比较,因此: 对一般的二元运算符重载为友元函数比重载为成员函数更优越。 但是对于赋值运算符,将其重载为成员函数较好,因为赋值运算符是一个二元运算符, 其语法格式为 =, 第一个运算量必须是对象(变量也是对象),通过

7、对象调用成员函数比较自然。 若重载为友元,则可能会出现5.6=c这样的表达式,与赋值表达式的语义不一致。,定义友元的目的是在友元函数中直接访问类的私有成员, 实际上,也可以通过公有函数接口访问类的私有成员, 所以实现运算符重载,可以即不用成员函数, 也不用友元函数。,class Complex float Real, Image; public: Complex(double r=0, double i=0) Real=r; Image=i; void SetReal(double Real) Complex:Real = Real; void SetImage(double Image) C

8、omplex:Image = Image; double GetReal( ) return(Real); double GetImage( ) return(Image); ;,4.使用非成员、非友元实现运算符的重载,例13.4:,Complex operator +(Complex ,编译器将 c1+c2 解释为: operator +(c1, c2),对于Complex类,如有Complex c1(2, 3), c2; 则自动将 c2=c1; 处理成: c2.Real = c1.Real; c2.Image = c1. Image; 一般不会出现问题。,5. 何时必须重载 = 和 +=

9、运算符?,相同类型的对象之间是可以直接赋值的,C+将赋值处理成两个对象的各个成员直接赋值。两个对象的对应数据成员逐一赋值。,例13.5 在类中,用字符数组实现字符串。 见 “第13章 多态性(例子).doc”,程序中A行使用赋值运算符进行对象整体赋值, C+将其处理成各个成员逐一赋值,如下图所示: :,C+默认的处理是:strcpy(stud2.Num, stud1.Num); strcpy(stud2.Name, stud1.Name); stud2.Score = stud1.Score; 。,但是如果对象的成员中有成员指向动态分配的 数据空间就会出现问题。,例13.6 在类中,用指针实现

10、字符串, 即字符串的空间是动态分配的。 class Student char *Nump; /学号,注意:用指针实现 char *Namep; /姓名,注意:用指针实现 int Score; /成绩 public: Student(char *nump=NULL, char *namep=NULL, int score=0) if(nump) /构造函数 Nump = new charstrlen(nump)+1; strcpy(Nump, nump); /动态分配存储空间 else Nump=NULL; if(namep) Score=score; ,Student( ) / 析构函数,释放

11、指针指向的空间 if(Nump)delete Nump; if(Namep)delete Namep; void Show( ) if(Nump ,void main( ) Student stud1(“01201“, “Mary“, 88), stud2; stud2=stud1; /A stud1.Show( ); stud2.Show( ); cout.flush( ); /B ,首先撤消对象stud2,然后撤消对象stud1 ,出问题! 同一对象被撤销两次。,编译器将A行处理成:,解决办法,在类中增加赋值 = 重载函数:,Student / *this是对象自身 ,在赋值时,为目的对象

12、的指针重新分配指向的字符串空间。,增加赋值 = 重载函数后,对象赋值后的存储空间如下:,这样,程序结束时,分别撤销两个对象,程序正确运行!,6对于+=(或=)运算符,重载函数的返回值为void类型或本类类型对象的区别,例13.7 见 “第13章 多态性(例子).doc”,若重载为返回void类型,则本类对象不可连续赋值。 若重载为返回本类类型,则本类对象可以连续赋值。,7对于+=(或=)运算符,返回本类对象与返回本类对象的引用的区别,比较下面两例,先看第一个例子: Complex Complex :operator+=(const Complex 此函数的返回值为本类对象,C+的处理是:用返回

13、的对象值*this初始化内存临时对象(调用拷贝构造函数),从本函数返回后,用内存临时对象作为调用函数的结果。,再看第二个例子: Complex Complex :operator+=(const Complex C+的处理是:调用拷贝构造函数,用返回的对象值temp初始化内存临时对象,内存临时对象作为调用函数的结果。,从上面两个小例子可以看出,若重载函数返回对象值,则返回*this和返回temp均可。 但要注意,因为返回对象值时需要调用拷贝构造函数初始化内存临时对象,因此若对象有动态分配的存储空间,就必须定义拷贝构造函数。,第三个例子: Complex 本例的返回值为本类对象的引用,不需初始化

14、内存临时对象,返回值是对象自身,即*this。因为不需要初始化内存临时对象,效率较高,所以像 +=、= 等改变对象值的重载函数最好返回对象的引用。,第四个例子: Complex 此时,出现问题。因为返回的是对象的引用(即对象自身,不借助于内存临时对象),系统要求在执行流程返回到调用处时,返回值是存在的。但是本例返回值是函数内部的局部对象temp,而局部对象在函数返回前,其空间是被撤消的。,结论: 返回本类对象时,可以用对象自身和局部对象做为返回值(有时需要定义拷贝构造函数)。 返回对象的引用时,不能用局部对象做为返回值。,例13.8对于字符串类,重载 = 运算符,返回对象自身的引用。就本例而言

15、,可以不定义拷贝构造函数,程序能正确运行。 见 “第13章 多态性(例子).doc”,8.调用拷贝构造函数 和 调用赋值运算符重载函数的时机,class Complex double Real, Image; public: Complex(double r=0, double i=0) / 构造函数 Real=r; Image=i; Complex(Complex ,程序的运行结果是?,调用了拷贝构造函数 未调用拷贝构造函数,Call copy 2 3,只有在产生新对象时,调用构造函数。 用已有对象初始化新产生的对象时, 调用拷贝构造函数。 赋值运算 = , 不调用拷贝构造函数。,13.2.

16、3 类型转换函数将本类对象转换成其他类对象,例: Complex c(3, 2); double x=6.2; 如果有: c=x; /类型不一致 或: x=c; /类型不一致 则系统自动处理为: c = Complex(x); /需作类型转换 x = double(c); /需作类型转换,构造函数: Complex:Complex(double r) real = r ; image = 0; ,定义类型转换函数:,功能:将 类对象自动转换成 类型对象,类型转换函数只能用成员函数实现, 不能用友元函数实现。,例13.10 类型转换函数的定义和使用 #include class Complex

17、double Real, Image; public: Complex(double r=0, double i=0) Real=r; Image=i; operator double ( ) / A 类型转换函数, / 将 Complex 类转换成 double 类 return Real; ; void main( ) Complex c(3,2); double x; x = c; / B cout “x=“ x endl; ,/ 即 x = double (c) ;,例13.11 成员函数和类型转换函数的比较 见 “第13章 多态性(例子).doc”,其中主函数为: void main

18、(void) RMB r(23, 8, 6); int r1, r2, r3; r1= r ; /处理成 r1 = r.operator int( ); r2= int(r); /处理成 r2 = r.operator int( ) ; r3= r.GetFen( ); cout“r1=“r1endl; cout“r2=“r2endl; cout“r3=“r3endl; ,13.2.4 其他运算符的重载 1重载+、-运算符,重载前置 + 运算符的成员函数的一般格式为:, :operator+ ( ) . ,重载后置 + 运算符的成员函数的一般格式为:, :operator+ (int) . ,

19、重载前置 + 运算符的友元函数的一般格式为:,friend operator+ ( &obj ) . ,重载后置+运算符的友元函数的一般格式为:,friend operator+ ( &obj, int) . ,例13.12 实现+和 - 的前置和后置运算符重载, 程序见 “第13章 多态性(例子).doc”,13.2.5 字符串类,C+ 提供的字符串处理能力较弱,如不能用运算符直接对字符串对象进行加、减等操作, 而必须通过字符串处理函数来实现拷贝、连接等操作。 如:char s110=“abc“, s210=“123“; strcpy(s1, s2); /不能写成 s1= s2; strca

20、t(s1, s2); /不能写成 s1= s1+s2; 能否定义一个字符串类:String 实现: String s1(“abc“), s2(“123“), s3; s1 = s2; s3 = s1 + s2; 能! 可以利用C+提供的运算符重载实现。,例13.18 定义字符串类String,并测试重载的运算符以及成员函数 程序见 “第13章 多态性(例子).doc”,或阅读教材上的程序。,重点讲解: (1) 说明:函数名后的 const (2) (拷贝)构造函数,在主函数中如何使用? (3) 重载赋值 = 运算符 (4) 重载 + 运算符 (5) 类型转换函数 operator const

21、char * (6) 删除子串图示见下页,删除子串: String operator - (const String &s1, const char *s2 ),13.3 静态联编,联编是指一个计算机程序彼此关联的过程。,在本章中指函数间调用关系的确定。,按照联编所确定的时刻不同,可分为两种: 静态联编和动态联编。,静态联编是指联编出现在编译连接阶段, 即函数调用关系的确定是在程序执行之前。,这种联编又称早期联编, 通过这种联编可实现静态多态。,例13.20 普通函数的静态联编 #include int add(int a, int b) /重载函数1 return(a+b); double

22、add(double a, double b) /重载函数2 return(a+b); void main( ) coutadd(1, 2)t; /编译时确定调用重载函数1 coutadd(1.1, 2.2)n; /编译时确定调用重载函数2 在编译连接阶段,就能根据参数的个数和类型确定调用的是哪一个函数。,例13.21 读书上程序,重点讲解: double CalcArea( Point ,能否找到一种机制, 让CalcArea( )函数变成一个 通用的求面积的函数。 这就是C+提供的动态联编和 虚函数应完成的工作。,13.4 动态联编和虚函数,运行阶段才能确定函数的调用关系,这就是动态联编,

23、动态联编又称滞后联编、晚期联编,,动态联编技术能实现动态多态。,必须将类的成员函数定义成虚函数, 才可以实现动态联编。,将成员函数定义成虚函数的格式为: virtual ( ) ,13.4.1 虚函数,重点讲解: double CalcArea(Point ,例13.22 阅读书上程序,Area( )是虚函数 , C+ 规定, 在 A 行保留相关的 三个虚函数入口地址 。 在程序的运行阶段, 根据实参的类型来确定 调用哪一个虚函数。,通用的求面积函数,(1)派生类的虚函数必须与基类虚函数同名, 且参数的类型、个数、顺序必须一致, 否则,属于函数重载,而不是虚函数。,有关虚函数的说明:,(2)基

24、类中虚函数前的关键字virtual不能缺省。,(3)必须通过基类对象的指针或引用调用虚函数, 才能实现动态多态。,(4)虚函数必须是类的成员函数,不能是友元函数, 也不能是静态成员函数。,(5)不能将构造函数定义为虚函数, 但可将析构函数定义为虚函数。,(6)调用虚函数速度较慢。因为,要实现动态多态, 在函数调用处必须保留多个虚函数的入口地址。,例13.23 在成员函数中调用虚函数 程序见 “第13章 多态性(例子).doc”,或阅读教材上的程序。,B b; b.fun1( ); / 调用A类的fun1( )和fun2( ),在A类的fun2( )函数中, / 在E行, this 是指向b的指

25、针, 所以调用B的fun3( )函数,class A public: A( ) fun( ); virtual void fun( ) cout “A:fun“ t; ; class B: public A public: B( ) fun( ); void fun( ) cout “B:fun“ t; void g( ) fun( ); / 在成员函数中调用虚函数 ;,例13.24 在构造函数中调用虚函数,class C: public B public: C( ) fun( ); void fun( ) cout “C:fun“ n; ; void main( ) C c; /依次调用A、

26、B、C三类的缺省构造函数 c.g( ); ,运行结果: A:fun B:fun C:fun C:fun,构造函数调用虚函数,调用的是类本身的虚函数。 成员函数调用虚函数,遵循动态多态性原则。,13.4.2 虚析构函数,如果类的构造函数中有动态申请的存储空间, 在析构函数中应释放该空间。 此时,建议将析构函数定义为虚函数, 以便实现通过基类的指针或引用 撤消派生类对象时的多态性。,#include class A char *Aptr; public: A( ) Aptr = new char100; A( ) /析构函数不是虚函数 delete Aptr; cout“Delete Aptr“e

27、ndl; ;,例: 析构函数不是虚函数的情况,class B : public A char *Bptr; public: B( ) Bptr=new char100; B( ) delete Bptr; cout“Delete Bptr“endl; ; void main(void) B b; ,输出结果: Delete Bptr Delete Aptr,系统自动撤销派生类对象, 不需要将析构函数定义为虚函数。,void main(void) A *p=new B; delete p; A ,输出结果: Delete Aptr Delete Aptr,如果通过基类的指针或引用 指向或引用派生

28、类对象。 则输出是?,结论: 对于虚函数, 若用派生类的指针或对象 初始化基类的指针或引用, 则通过该指针或引用调用 虚函数,遵循派生类规则。, 例13.26 虚析构函数 程序见 “第13章 多态性(例子).doc”, 或阅读教材上的程序。,13.5 纯虚函数和抽象类,在定义基类时,会遇到这样的情况:无法定义基类中 虚函数的具体实现,其实现依赖于其不同的派生类。,考虑例13.22有关“形状”的类,从日常抽象思维的角度考虑,能否定义一个“形状”类Shape,对形状的通用操作可能有:求这个形状的面积、绘制这个形状的图形等。当然它是一个抽象的类,我们无法真正定义对这个形状操作的具体实现。可以由Sha

29、pe类派生出具体的“点”类Point、“长方形”类Rectangle、“圆”类Circle等,在派生类中实现具体的求不同形状的面积和绘制不同形状的图形的操作。,可以把基类中的虚函数定义为纯虚函数。,把至少包含一个纯虚函数的类称为抽象类。,在抽象类的派生类中,应写出纯虚函数的实现, 否则,派生类依然是抽象类。,抽象类只能作基类,不能定义抽象类的对象。,定义纯虚函数的一般格式为: virtual ( ) = 0 ;,没有函数体。函数参数列表圆括号后面的“= 0”,表示将函数名的值赋予0。,例13.27 定义抽象类,派生出若干类, 在派生类中实现纯虚函数。 程序见 “第13章 多态性(例子).doc”, 或阅读教材上的程序。,重点讲解,见下页:,double CalcArea(Shape ,运行结果是?,

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

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1