编译时多态教学.ppt
《编译时多态教学.ppt》由会员分享,可在线阅读,更多相关《编译时多态教学.ppt(121页珍藏版)》请在三一办公上搜索。
1、第四章 编译时多态性,封装性是面向对象程序设计的基础,可以说没有封装性就没有面向对象的程序设计。封装性从根本上解决了数据的安全性,也为实现不同数据的操作同一性奠定了基础,这种操作的同一性反映了客观世界中规范不同对象行为的一致性需要,它构成了面向对象程序设计多态性的一部分。不过这种多态性必须预先确定操作所施加的数据类型,因此不是完备意义上的多态性,而只是基于对象的多态性。这种不同数据的操作同一性必须在程序编译时静态确定,故称为编译时多态性,或称为静态多态性。实现这种静态多态性的途径是:函数重载和运算符重载。,本章要点1 函数重载2 运算符重载3 使用成员函数重载运算符4 使用友元函数重载运算符5
2、 自增+和自减-运算符的重载6 调用运算符()和下标运算符 的重载7 动态内存管理运算符的重载8 赋值运算符重载9 输入输出运算符重载10 类型转换(函数),4.1 函数重载 函数重载为实现不同数据的操作同一性提供了根本编程机制。函数重载的基本规则:函数名必须相同:体现了操作同一性。函数的参数必须不同:体现了同一操作的实现差异 和所施加的数据差异。在面向对象的程序中函数重载表现在两个方面:类外全局函数重载 类成员函数重载,4.1.1 类外全局函数重载 在面向对象程序设计中,全局函数常常用来作为类的友元函数,因此全局函数重载可以用来实现不同类对象的同一类外操作。关于全局函数重载的方法已经在第二章
3、中讲述,本章不再赘述。为了进一步理解函数重载是如何在编译时确定同一操作施加于不同类型的数据,我们模拟分析 C+编译器如何采用“名子压延”的方法实现重载函数的调用。所谓“名子压延”是指编译器编译重载函数调用时,先将原重载函数名和参数类型结合起来,以创建新重载函数名。然后用新重载函数名替代原重载函数名。例如,一个程序中有两个重载函数,原型声明为:,int myAns(float x,int j);int myAns(int I,char c);编译时,编译器首先压延修改这两个重载函数名。修改后的新函数名或许会变成如下形式:int myAnsFLTINT(float x,int j);int myA
4、nsINTCHAR(int i,char c);这样,就把原来无法区分开来的相同的函数名变成可以区分的不同的函数名。例如:调用 myAns(float x,int j)时,则调用 myAnsFLTINT(float x,int j);而调用 myAns(int I,char c)时,则调用 myAnsINTCHAR(int i,char c)。,假如调用这两个函数的语句为:exam1=myAns(15.3,15);exam2=myAns(45,a);用新函数名替代原函数名后,使得调用这两个函数的语句会发生如下相应的改变:exam1=myAnsFLTINT(15.3,15);exam2=myAn
5、sINTCHAR(45,a);,注意,上述对函数名的具体压延过程和结果只是为了便于叙述假定的,并非 C+编译器对这两个函数名的真实压延结果。,4.1.2 类成员函数重载 类成员函数重载分为两类:1 构造函数重载 为类对象的创建和类对象的数据成员赋初值提供不 同的方法。重载构造函数的参数必须不同。2 公有成员函数重载 提供响应相同消息的不同接口方法。这类重载有两 种情况:同一类中的重载成员函数的参数必须有差别。派生类中分属于基类和派生类的重载成员函数的 参数可以相同,但基类的重载成员函数被覆盖。例如:,class point int x,y;public:point(int a,int b)x=
6、a;y=b;float area()return 0.0;class circle:public point int radius;public:circle(int x,int y,int rad):point(x,y)radius=rad;float area()/重载成员函数 return float(3.1416)*float(radius)*float(radius);,main()point p(20,20);circle c(8,8,30);cout p.area()endl;cout c.area()endl;cout c.point:area()endl;return 0;其
7、中 area 是参数相同的重载成员函数。编译过程 中区别这两个不同版本的 area 的情况有两种:,使用对象名只能分别调用基类和派生类的参数 相同的重载成员函数。例如:p.area()将调用 point:area()方法;c.area()将调用 circle:area 方法。在对象名后添加“基类名:”可以使用派生类对 象名可以调用基类的参数相同的重载成员函 数。例如:c.point:area()将调用 point:area()方法。返回,4.2 运算符重载运算符可以视为是一种特殊的函数,它们可以用一种简洁的,接近自然语言的表达式方式被调用。C+拥有一系列的预定义运算符,这些运算符能对所有的预定
8、义类型的数据进行形式一致的操作。也就是说,可以使用预定义类型的数据作参数调用这些运算符函数。例如,加运算符+:int x,y;float e,f;y=x+y;f=e+f;,这种对于不同类型的数据使用相同的表达式调用相同的运算符正是通过重载机制实现的。但这些运算符却不能自动施加在自定义类型的对象。例如:自定义的复数类 complex 的定义如下:class complex double real,imag;public:complex(double r=0,double i=0)real=r;imag=i;,main()complex com1(1.1,2.2),com2(3.3,4.4),to
9、tal;total=com1+com2;/“+”运算符无法对复数进行操作 return 0;产生上述错误的原因很显然,因为运算符“+”允许的操作数据类型中不包括 complex 类型,所以编译器就不知道调用该运算符的哪个重载版本来完成相应的操作。,解决的办法就是扩展“+”运算符允许操作的数据类型,即为复数类 complex 定义自己的“+”运算符操作。C+的运算符重载方法为设计不同类对象的同一行为提供了非常有效和方便的手段。运算符重载是通过对运算符函数的重载实现的,运算符函数重载的原型和定义的一般形式如下:函数类型 operator(参数表列);/是运算符名的通配符号函数类型 operator
10、(参数表列)重载操作代码,例如,对复数类 complex 的“+”运算符重载:class complex double real,imag;public:complex(double r=0,double i=0)real=r;imag=i;complex operator+(complex);complex complex:operator+(complex,4.2.1 重载运算符的规则 C+不允许定义新的运算符,只能对系统预定义运 算符进行重载。C+的预定义运算符中允许重载的包括:,不允许重载预定义运算符的包括:重载不能改变预定义运算符函数的参数(操作数)个数。双目运算符重载后仍为双目运算
11、符,单目运 算符重载后仍为单目运算符。重载不能改变预定义运算符的原有优先级。重载不能改变预定义运算符的原有结合律。重载运算符函数的参数不允许有缺省值。否则编译 器会认为是改变了运算符函数的参数个数。,重载的运算符必须与用户自定义类型的对象一起使 用,因此重载运算符函数的参数中至少应该有一个 是自定义类对象或类对象的引用。换句话说,重载 运算符函数的参数不能全部是预定义类型对象,防 止用户修改预定义运算符的性质。例如:int operator+(int a,int b)return a b;显然,这是绝对不允许的。对于双目运算符,允许两个参数都是自定义类对 象,例如两个复数的加运算;也允许一个参
12、数是自 定义类对象,另一个参数是预定义类型对象,例如 一个复数与一个实数的加运算。,complex operator+(double d,complex 系统会为每个自定义类缺省重载了赋值运算符“=”和取地址运算符“&”,其他运算符都需要根据需要进行重载定义。其中:赋值运算符“=”用于同类对象之间的数据成员赋值操作。一般情况下,缺省重载的赋值运算符“=”可以满足要求,但遇到类的数据成员中包含了动态数据指针,则使用缺省重载赋值运算符就可能发生危险,因此必须重载新的赋值运算符。取地址运算符“&”用于计算返回对象在内存中的起始地址。,虽然可以任意定义重载运算符的操作功能,但应该 使重载运算符的功能类
13、似于被重载运算符作用于预 定义类型数据时的操作功能。运算符重载函数可以是运算所施加类对象的成员函 数,也可以是该类的友元函数,如果不需要访问类 的私有数据成员,还可以是既非类成员函数也非友 元函数的普通函数。,在 Java 中虽然也不乏运算符重载的例子,例如:String str=hello+there;该表达式相当于在 C+中的表达式:string str=hello+there;上述表达式都是因为对 Java 的 String 类型和 C+的 string 类型的进行了“+”运算符重载的结果。但要注意的是在 Java 中不允许用户进行运算符重载。返回,4.3 使用成员函数重载运算符1 使用
14、成员函数重载运算符的语法形式 在类定义体中声明要重载的运算符成员函数 type 运算符函数类型;operator 运算符函数名关键字;要重载的运算符名;参数表 被重载运算符所需的右操作数。参数个数=运算符所需操作数个数-1,缺省 的左操作数必须是运算符所属类对象。例如:,type operator(参数表);,class complex double real,imag;public:complex operator+(complex,type 类名:operator(参数列表),重载运算符的使用 运算符的左操作数必须是该运算符成员函数所属 类的对象。双目运算符 例如:complex com1
15、,com2,com3;com3=com1+com2;或 com3=com1.operator+(com2);,表达式形式:对象名 参数对象名;函数形式:对象名.operator(参数对象名);,单目运算符 例如:complex com;+com;com+;或 com.operator+();com.operator+(0);,表达式形式:对象名;对象名;函数形式:对象名.operator();对象名.operator(0);,2 用成员函数重载运算符的使用实例例4-1 定义了一个表达三维空间位置的简单类 three_d,在此类中含有三维空间位置的坐标。通过运算符重载来实现对此类对象的+、和=运
16、算。注意:定义中+和 运算符函数的返回值不应是被加对象和被减对象,而=运算符函数的返回值必须是被赋值的对象。这都是运算符的操作含义所决定的。返回,4.4 使用友元函数重载运算符1 使用友元函数重载运算符的语法形式 在类定义体中声明重载运算符友元函数 friend 友元函数关键字 type 运算符函数类型;operator 运算符函数名关键字;要重载的运算符名;参数表 被重载运算符所需的操作数。参数个数=运算符所需操作数个数。例如:,friend type operator(参数表);,class point int x,y;public:friend point operator+(point
17、,type operator(参数表),重载运算符的使用 双目运算符 例如:point pt1,pt2,pt3;pt3=pt1+pt2;或 pt3=operator+(pt1,pt2);,表达式调用:参数对象名1 参数对象名2;函数调用:operator(参数对象名1,参数对象名2);,单目运算符 例如:point pt;+pt;pt+或 operator+(pt);operator+(pt,0);,表达式调用:参数对象名;参数对象名;函数调用:operator(参数对象名);operator(参数对象名,0);,2 使用友元函数重载运算符的应用实例例4-2 使用友元函数重载运算符的方法实现复
18、数的四则运算,复数运算规则如下:(a+bi)+(c+di)=(a+c)+(b+d)i;(a+bi)-(c+di)=(a-c)+(b-d)i;(a+bi)*(c+di)=(ac-bd)+(bc+ad)i;(a+bi)/(c+di)=(ac+bd)+(bc-ad)i)/(c2+d2);定义一个复数类 complex,并声明和定义相应的友元函数,用于重载运算符+、-、*、/。,例4-3 使用友元函数重载运算符的方法实现集合运算。集合可以用数组或链表表示,在该数组或链表中不允许包含重复元素。定义整型数集合 set,元素个数用整型变量 card 表示。集合的操作包括向集合中追加元素、显示集合的全部元素以
19、及使用友元函数重载运算符的方法实现的集合的主要运算,这些运算包含:判定某一个元素属于集合的运算符&;判定两个集合相等的运算符=;,判定两个集合的不等于运算符!=;两个集合的交运算符*;两个集合的并运算符+;判定某集合是另一集合的子集运算符=;判定某集合是另一集合的纯子集运算符。,4.4.1 两种运算符重载方法的比较1 参数数目:双目运算符 重载运算符的成员函数只有一个参数,用于 表示表达式的右操作数。重载运算符的友元函数有两个参数,分别用 于表达式的左、右操作数 单目运算符 重载运算符的成员函数无参数。重载运算符的友元函数带有一个参数,用于 表示表达式的唯一操作数。,2 无论是使用成员函数还是
20、友元函数重载的运算符,调用它们的表达式形式是一致的,而调用它们的函 数形式是有差别的。,3 大部分运算符重载函数既可以是成员函数,又可以 是友元函数。究竟选择哪一种好呢?要视实际情况 和程序员的习惯。但是应注意以下情况:对于双目运算符,如果使用成员函数重载,则在 有些情况下会产生操作数类型错误。例如:class complex double real,imag;public:complex operator+(double x);complex complex:operator+(double x)return complex(real+x,imag);,对 complex 的对象 com 进
21、行如下的+运算:com=com+100.0;由于对象 com 是+运算符的左操作数,所以它调 用了 complex 类重载的+运算符,把实数 100.0 加 到 com 的实部数据成员 real 上。如果按照+运算 符操作数的交换律,将上面的表达式写成:com=100.0+com;则会引起操作数类型错误,无法完成表达式所要 求的操作。这是因为 complex 类+运算符的左操 作数是 complex 类对象,而表达式的左操作数是 实数,无法调用 complex 类+运算符函数。,如果使用两个友元函数来替换上述 complex 类的+运算符重载:friend operator+(complex
22、com,double x);friend operator+(double x,complex com);complex operator+(complex com,double x)return complex(com.real+x,imag);complex operator+(double x,complex com)return complex(x+com.real,imag);则可避免上述问题,因为友元运算符函数的两个 操作数都是显式地传递给运算符函数的,能满足 加运算操作数的交换律。,所以一般建议使用友元函数重载双目运算符,特 别是期望双目运算符的左操作数的类型能够隐式 转换的情况
23、,则重载双目运算符必须使用友元函 数,而不能使用成员函数。若一个运算符需要修改其操作所施加对象(特别 是该对象为该运算符的第一操作数)的状态,则 选择使用成员函数重载运算符不破坏类对象的封 装性,更符合面向对象程序设计的原则。返回,4.5 自增+和自减-运算符的重载 从运算操作的正确性考虑,使用成员函数还是使用友元函数重载自增运算符+和自减运算符-的效果是一致的。但从面向对象设计原则出发,类对象的自增和自减运算实际上是对被封装的类数据成员依次进行的。因此,建议使用成员函数实现自增运算符+和自减运算符-的重载。使用+和-的表达式形式分为前缀和后缀两种形式,这两种形式对操作数的最终修改虽然相同,但
24、运算符函数的返回值不同,前缀形式返回修改后的操作数,而后缀形式返回修改前的操作数。例如:,int i=0,j=0;i+;+j;cout i,j endl;/显示1,1 cout i+,+j endl;/显示1,2 在 C+2.1 及以后的版本中,编译器可以通过判别在自增或自减运算符函数的参数列表中是否增加了一个附加的整型 int 形式参数来区分所重载的自增或自减运算符可以用于前缀表达式还是用于后缀表达式。,1 自增运算符“+”使用成员函数重载 原型 前缀形式:类名 operator+();后缀形式:类名 operator+(int);例如:class point int x,y;public:
25、point operator+();point operator+(int);,定义 前缀形式:类名 类名:operator+()后缀形式:类名 类名:operator+(int)例如:point point:operatot+()+x,+y;return*this;point point:operatot+(int)point temp(x,y);+x,+y;return temp;,调用 前缀表达式形式:+类对象名;后缀表达式形式:类对象名+;例如:point pt;+pt;/前缀表达式 pt+;/后缀表达式 前缀函数形式:对象名.operator+();后缀函数形式:对象名.operat
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 编译 时多态 教学
![提示](https://www.31ppt.com/images/bang_tan.gif)
链接地址:https://www.31ppt.com/p-6486259.html