来自:玉诗 > 馆藏分类
配色: 字号:
第8章 多态性
2014-12-01 | 阅:  转:  |  分享 
  
case''w'': //取出现金 cin>>index>>amount; getline(cin,desc)
; accounts[index]->withdraw(date,amount,desc); break;
case''s'': //查询各账户信息 for(inti=0;i<<"["<show(); cout< } break; case''c'': //改变日期 cin>>day; if(day.getDay()) cout<<"Youcannotspecifyapreviousday"; el
seif(day>date.getMaxDay()) cout<<"Invalidday"; else
date=Date(date.getYear(),date.getMonth(),day); break;
8.6综合实例——对个人银行账户管理程序的改进例8-8(续) case''n'': //进入下个月 i
f(date.getMonth()==12) date=Date(date.getYear()+1,1,
1); else date=Date(date.getYear(),date.getMonth()+1,
1); for(inti=0;isettle(date);
break; } }while(cmd!=''e''); return0;}8.6综合实例——对个
人银行账户管理程序的改进例8-8(续)2008-11-1#S3755217created2008-11-1
#02342342created2008-11-1#C5392394created(d)depo
sit(w)withdraw(s)show(c)changeday(n)nextmonth(e)exit2008-
11-1Total:0command>c52008-11-5Total:0
command>d05000salary2008-11-5#S37552175
0005000salary2008-11-5Total:5000command>c
152008-11-15Total:5000command>w22000buyacell
2008-11-15#C5392394-2000-2000buyacell2008-
11-15Total:3000command>c252008-11-25Total:3
000command>d110000sellstock03232008-11-25#02342
3421000010000sellstock03232008-11-25Total:
13000command>n2008-12-1#C5392394-16-201
6interest2008-12-1Total:12984command>d22016re
paythecredit2008-12-1#C539239420160r
epaythecredit2008-12-1Total:15000command>c52008
-12-5Total:15000command>d05500salary2008-12-5
#S3755217550010500salary2008-12-5Total
:20500command>n2009-1-1#S375521717.77105
17.8interest2009-1-1#0234234215.1610015.2int
erest2009-1-1#C5392394-50-50annualfee
2009-1-1Total:20482.9command>s[0]S3755217Balanc
e:10517.8[1]02342342Balance:10015.2[2]C5392394Balan
ce:-50Availablecredit:99502009-1-1Total:20482.9
command>e8.6综合实例——对个人银行账户管理程序的改进例8-8(续)运行结果8.7.1多态类型与非
多态类型多态类型与非多态类型有虚函数的类类型称为多态类型其它类型皆为非多态类型二者的差异语言层面的差异多态类型支持运行
时类型识别多态类型对象占用额外的空间设计原则上的差异8.7深度探索设计原则多态类型多态类型的析构函数一般应为虚函
数非多态类型非多态类型不宜作为公共基类由于没有利用动态多态性,一般可以用组合,而无需用共有继承;如果继承,则由于析构函数不
是虚函数,删除对象时所执行操作与指针类型有关,易引起混乱。把不需被继承的类型设定为非多态类型由于成员函数都是静态绑定,调用速度
较快;对象占用空间较小。8.7深度探索——8.7.1多态类型与非多态类型8.7.2运行时类型识别运行时类型识
别允许在运行时通过基类指针(或引用)辨别对象所属的具体派生类;只对多态类型适用;比虚函数动态绑定的开销更大,因此应仅对虚函数
无法解决的问题使用。运行时类型识别的方式用dynamic_cast做类型转换的尝试;用typeid直接获取类型信息。8
.7深度探索使用dynamic_cast语法形式dynamic_cast<目的类型>(表达式)功能将基类指针转换为派生
类指针,将基类引用转换为派生类引用;转换是有条件的如果指针(或引用)所指对象的实际类型与转换的目的类型兼容,则转换成功进行;
否则如执行的是指针类型的转换,则得到空指针;如执行的是引用类型的转换,则抛出异常。8.7深度探索——8.7.2运行时
类型识别例8-9dynamic_cast用法示例#includeusingnamespacest
d;classBase{public: virtualvoidfun1(){cout<<"Base::fun
1()"<se{public: virtualvoidfun1(){cout<<"Derived1::fun1()"<<
endl;} virtualvoidfun2(){cout<<"Derived1::fun2()"<l;}};classDerived2:publicDerived1{public: virtualvoidf
un1(){cout<<"Derived2::fun1()"<){cout<<"Derived2::fun2()"<运行时类型识别voidfun(Baseb){ b->fun1(); //尝试将b转换为Derived1指针 De
rived1d=dynamic_cast(b); //判断转换是否成功 if(d!=0)
d->fun2();}intmain(){ Baseb; fun(&b); Derived1d1; fun(
&d1); Derived2d2; fun(&d2); return0;}8.7深度探索——8.7.2运
行时类型识别例8-9(续)运行结果:Base::fun1()Derived1::fun1()Derived1::fun2
()Derived2::fun1()Derived2::fun2()用typeid获取运行时类型信息语法形式typeid
(表达式)typeid(类型说明符)功能获得表达式或类型说明符的类型信息表达式有多态类型时,会被求值,并得到动
态类型信息;否则,表达式不被求值,只能得到静态的类型信息。类型信息用type_info对象表示type_info是typei
nfo头文件中声明的类;typeid的结果是type_info类型的常引用;可以用type_info的重载的“==”、“!=”
操作符比较两类型的异同;type_info的name成员函数返回类型名称,类型为constchar。8.7深度探索
——8.7.2运行时类型识别例8-10typeid用法示例//8_10.cpp#include
#includeusingnamespacestd;?classBase{public:
virtual~Base(){}};classDerived:publicBase{};?voidfu
n(Baseb){ //得到表示b和b类型信息的对象 consttype_info&info1=typeid(
b); consttype_info&info2=typeid(b); 8.7深度探索——8.7.2运
行时类型识别cout<<"typeid(b):"<peid(b):"<typeid(Base)) cout<<"Abaseclass!"<(){Baseb;fun(&b);Derivedd;fun(&d);ret
urn0;}结果:typeid(b):classBasetypeid(b):classBaseAba
seclass!typeid(b):classBasetypeid(b):classDerived8.7.3
虚函数动态绑定的实现原理动态选择被执行的函数函数的调用,需要通过函数代码的入口地址把函数入口地址作为变量,在不同情况下赋予
不同的值,通过该变量调用函数,就可动态选择被执行的函数回顾:第6章介绍的函数指针、指向成员函数的指针虚表每个多态类有一个虚表
(virtualtable)虚表中有当前类的各个虚函数的入口地址每个对象有一个指向当前类的虚表的指针(虚指针vptr)动态
绑定的实现构造函数中为对象的虚指针赋值通过多态类型的指针或引用调用成员函数时,通过虚指针找到虚表,进而找到所调用的虚函数的入口
地址通过该入口地址调用虚函数8.7深度探索指向f()的指针指向g()的指针Base的虚表指向f()的指针指
向g()的指针指向h()的指针Derived的虚表(Base::f的代码)push%ebpmov%esp,%ebp
……(Base::g的代码)push%ebpmov%esp,%ebp……(Derived::f的代码)push
%ebpmov%esp,%ebp……(Derived::h的代码)push%ebpmov%esp,%ebp…
…ivptrivptrjBase类型对象Derived类型对象classBase{public: vir
tualvoidf(); virtualvoidg();private: inti;};classDeriv
ed:publicBase{public: virtualvoidf();//覆盖Base::f virtual
voidh();//新增的虚函数private: intj;};8.7深度探索——8.7.3虚函数动态绑定
的实现原理8.8小结主要内容多态性的概念、运算符重载、虚函数、纯虚函数、抽象类达到的目标理解多态的概念,学会运用多态机
制。2013/12/38.2.3运算符重载为非成员函数函数的
形参代表依自左至右次序排列的各操作数。后置单目运算符++和--的重载函数,形参列表中要增加一个int,但不必写形参名。如果在
运算符的重载函数中需要操作某类对象的私有成员,可以将此函数声明为该类的友元。8.2运算符重载8.2.3运算符重载为非成员
函数(续)双目运算符B重载后,表达式oprd1Boprd2等同于operatorB(oprd1,oprd2)前
置单目运算符B重载后,表达式Boprd等同于operatorB(oprd)后置单目运算符++和--重载后,表
达式oprdB等同于operatorB(oprd,0)8.2运算符重载例8-3以非成员函数形式重载Compl
ex的加减法运算和“<<”运算符将+、-(双目)重载为非成员函数,并将其声明为复数类的友元,两个操作数都是复数类的常引用。将<
<(双目)重载为非成员函数,并将其声明为复数类的友元,它的左操作数是std::ostream引用,右操作数为复数类的常引用,返回s
td::ostream引用,用以支持下面形式的输出:cout<operator<<(cout,a),b);8.2运算符重载——8.2.3运算符重载为非成员函数//8_3
.cpp#includeusingnamespacestd;?classComplex{ /
/复数类定义public: //外部接口 Complex(doubler=0.0,doublei=0.0):
real(r),imag(i){} //构造函数 friendComplexoperator+(constCom
plex&c1,constComplex&c2); //运算符+重载 friendComplexoperator-
(constComplex&c1,constComplex&c2); //运算符-重载 friendostream
&operator<<(ostream&out,constComplex&c);//运算符<<重载privat
e: //私有数据成员 doublereal; //复数实部 doubleimag; //复数虚部};?Comple
xoperator+(constComplex&c1,constComplex&c2){ //重载运算符函数实现
returnComplex(c1.real+c2.real,c1.imag+c2.imag);}8.2
运算符重载——8.2.3运算符重载为非成员函数例8-3(续)Complexoperator-(constCom
plex&c1,constComplex&c2){ //重载运算符函数实现 returnComplex(c1.rea
l-c2.real,c1.imag-c2.imag);}?ostream&operator<<(ostr
eam&out,constComplex&c){ //重载运算符函数实现 out<<"("<<","<plexc1(5,4),c2(2,10),c3; //定义复数类的对象 cout<<"c1="<使用重载运算符完成复数加法 cout<<"c3=c1+c2="<;}8.2运算符重载——8.2.3运算符重载为非成员函数例8-3(续)8.3虚函数用virtual关键字说
明的函数虚函数是实现运行时多态性基础C++中的虚函数是动态绑定的函数虚函数必须是非静态的成员函数,虚函数经过派生之后,就可以
实现运行过程中的多态。8.3.1虚函数成员C++中引入了虚函数的机制在派生类中可以对基类中的成员函数进行覆盖(重定义)。
虚函数的声明 Virtual函数类型函数名(形参表) { 函数体 }8.3虚函数例8-4通
过虚函数实现运行时多态#includeusingnamespacestd;classBase1
{//基类Base1定义public: virtualvoiddisplay()const; //虚函数};vo
idBase1::display()const{ cout<<"Base1::display()"<}classBase2::publicBase1{//公有派生类Base2定义public: voiddispl
ay()const; //覆盖基类的虚函数};voidBase2::display()const{ cout<<
"Base2::display()"<ived:publicBase2{//公有派生类public: voiddisplay()const; //覆盖
基类的虚函数};voidDerived::display()const{ cout<<"Derived::disp
lay()"<d
isplay(); //"对象指针->成员名"}?intmain(){ //主函数 Base1base1; //定义
Base1类对象 Base2base2; //定义Base2类对象 Derivedderived; //定义Derived
类对象 fun(&base1);//用Base1对象的指针调用fun函数 fun(&base2);//用Base2对象的指针调
用fun函数 fun(&derived);//用Derived对象的指针调用fun函数 return0;}例8-4(
续)8.3虚函数——8.3.1虚函数成员运行结果:Base1::display()Base2::display(
)Derived::display()8.3.2虚析构函数为什么需要虚析构函数?可能通过基类指针删除派生类对象;如果你
打算允许其他人通过基类指针调用对象的析构函数(通过delete这样做是正常的),就需要让基类的析构函数成为虚函数,否则执行dele
te的结果是不确定的。8.3虚函数例8-5虚析构函数举例#includeusingname
spacestd;?classBase{public: ~Base();};Base::~Base(){ c
out<<"Basedestructor"<public: Derived(); ~Derived();8.3虚函数——8.3.2虚析构函数priva
te: intp;};Derived::Derived(){ p=newint(0);}Derived::
~Derived(){cout<<"Deriveddestructor"<;}?voidfun(Baseb){ deleteb;}?intmain(){ Baseb=
newDerived(); fun(b); return0;}例8-5(续)运行时结果:Basedestruct
or避免上述错误的有效方法就是将析构函数声明为虚函数,运行结果变为:DeriveddestructorBasedestr
uctor8.3虚函数——8.3.2虚析构函数8.4.1纯虚函数纯虚函数是一个在基类中声明的虚函数,它在该基
类中没有定义具体的操作内容,要求各派生类根据实际需要定义自己的版本,纯虚函数的声明格式为:virtual函数类型函数名(参数
表)=0;带有纯虚函数的类称为抽象类:class类名{virtual类型函数名(参数表)=0;/
/纯虚函数...}8.4抽象类8.4.2抽象类作用抽象类为抽象和设计的目的而声明,将有关的数据和行为
组织在一个继承层次结构中,保证派生类具有要求的行为。对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。注意抽象类
只能作为基类来使用。不能声明抽象类的对象。构造函数不能是虚函数,析构函数可以是虚函数。8.4抽象类例8-6抽象类举
例//8_6.cpp#includeusingnamespacestd;?classBase
1{//基类Base1定义public: virtualvoiddisplay()const=0; //纯虚函数
};?classBase2:publicBase1{//公有派生类Base2定义public: voiddi
splay()const; //覆盖基类的虚函数};voidBase2::display()const{ cout
<<"Base2::display()"<ived:publicBase2{//公有派生类Derived定义public: voiddisplay()con
st; //覆盖基类的虚函数};voidDerived::display()const{ cout<<"Deriv
ed::display()"< ptr->display(); //"对象指针->成员名"}?intmain(){ //主函数 Base2bas
e2; //定义Base2类对象 Derivedderived; //定义Derived类对象 fun(&base2); /
/用Base2对象的指针调用fun函数 fun(&derived); //用Derived对象的指针调用fun函数 retur
n0;}8.4抽象类——8.4.2抽象类例8-6(续)运行结果:Base2::display()Deri
ved::display()8.5程序实例—变步长梯形积分算法求解函数的定积分——算法基本原理我们只考虑最简单的情况,设被积
函数是一个一元函数,定积分表达式为:积分表示的意义是一元函数f(x)在区间a到b之间与x轴所夹的面积yxab
xkxk-1hIkf(x)8.5.1算法基本原理(续)在每个小区间上都用小的梯形面积来近似原函数的积分,当小区间足
够小时,我们就可以得到原来积分的近似值。每个小区间的面积值公式:实际计算中步长h逐次减半,反复利用上述求积公式进行计算,直到所
求得的积分结果满足要求的精度为止。并得到递推公式:8.5程序实例—变步长梯形积分算法求解函数的定积分8.5.2程序设计分
析8.5程序实例—变步长梯形积分算法求解函数的定积分MyFunction<>+operator()(
x:double):doubleIntegration<>+operator()
(a:double,b:double,eps:double):doubleFunction<abstract>>+operator()(x:double):doubleTrapz+Trapz(pf:
constF&)<>+operator()(a:double,b:double,eps:d
ouble):double-f8.5.3源程序及说明——例8-7变步长梯形积分法求解函数的定积分
我们求一个测试函数在某给定区间的积分值,对整个程序进行了测试,误差为eps为10-7。测试函数:整个程序分为三个独立的文档,
Trapzint.h文件包括类的定义,Trapzint.cpp文件包括类的成员函数实现。文件intmain.cpp是程序的主函数,
主函数中定义了函数类Fun和梯形积分类Trapz的对象8.5程序实例—变步长梯形积分算法求解函数的定积分//Trapzi
nt.h文件一,类定义classFunction{ //抽象类Function的定义public: virtual
doubleoperator()(doublex)const=0; //纯虚函数重载运算符() virtual~
Function(){}};?classMyFunction:publicFunction{ //公有派生类My
Function定义public: virtualdoubleoperator()(doublex)const; //
覆盖虚函数};?classIntegration{ //抽象类Integration定义public: virtua
ldoubleoperator()(doublea,doubleb,doubleeps)const=0;
virtual~Integration(){}};?例8-7(续)8.5程序实例—变步长梯形积分算法求解函数
的定积分classTrapz:publicIntegration { //公有派生类Trapz定义public: Tr
apz(constFunction&f):f(f){} //构造函数 virtualdoubleoperator
()(doublea,doubleb,doubleeps)const;private: constFunction
&f; //私有成员,Function类对象的指针};//Trapzint.cpp文件二,类实现#include"
Trapzint.h" //包含类的定义头文件#includedoubleMyFunction::opera
tor()(doublex)const{//被积函数 returnlog(1.0+x)/(1.0+x
x);}doubleTrapz::operator()(doublea,doubleb,doubleeps)c
onst{//积分运算过程,重载为运算符() booldone=false; //是Trapz类的虚函数成员 int
n=1; doubleh=b-a; doubletn=h(f(a)+f(b))/2; //计
算n=1时的积分值 doublet2n; 例8-7(续)8.5程序实例—变步长梯形积分算法求解函数的定积分d
o{ doublesum=0; for(intk=0;k=a+(k+0.5)h; sum+=f(x); } t2n=(tn+hsum)/
2.0; //变步长梯形法计算 if(fabs(t2n-tn)积分误差 else{ //进行下一步计算 tn=t2n; n=2; h/=2; } }
while(!done); returnt2n;}例8-7(续)8.5程序实例—变步长梯形积分算法求解函数的定
积分//8_7.cpp文件三,主函数#include"Trapzint.h" //类定义头文件#includestream>#includeusingnamespacestd;?intmain(){ //
主函数 MyFunctionf; //定义MyFunction类的对象 Trapztrapz(f); //定义Trapz类
的对象 //计算并输出积分结果 cout<<"TRAPZInt:"<pz(0,2,1e-7)<算法求解函数的定积分运行结果:TRAPZInt:0.55489528.6综合实例——对个人银行账户管理程序的改进本例
在第七章例7-10的基础上,对Account类做了如下改进:(1)将show函数声明为虚函数,因此通过指向CreditAcco
unt类实例的Accout类型的指针来调用show函数时,被实际调用的将是为CreditAccount类定义的show函数,这样,
如果创建一个Account指针类型的数组,使各个元素分别指向各个账户对象,就可以通过一个循环来调用它们的show函数;(2)在
Account类中添加deposit、withdraw、settle这3个函数的声明,且将它们都声明为纯虚函数,这使得通过基类的指
针可以调用派生类的相应函数,而且无需给出它们在基类中的实现。经过这一改动之后,Account类就变成了抽象类。- acc
:Accumulator- rate:doubleSavingsAccount+ SavingsAccount(da
te:Date,id:int,rate:double)<>+ getRate():doubl
e+ deposit(date:Date,amount:double,desc:string)+ withdr
aw(date:Date,amount:double,desc:string)+ settle(date:D
ate)11- acc:Accumulator- credit:double- rate:double-
fee:doubleCreditAccount<>- getDebt():double+ Credi
tAccount(date:Date,id:int,credit:double,rate:double,f
ee:double)<>+ getCredit():double<>+ getRate
():double<>+ getFee():double<>+ getAvailabl
eCredit():double+ deposit(date:Date,amount:double,desc:
string)+ withdraw(date:Date,amount:double,desc:string)
+ settle(date:Date)<>+ show()- year:int- month:
int- day:int- totalDays:int+ Date(year:int,month:int,
day:int)<>+ getYear():int<>+ getMonth():
int<>+ getDay():int<>+getMaxDay():int<nst>>+isLeapYear():bool<>+show()<>+operat
or-(date:Date):intDate- lastDate:Date- value:double
- sum:double+ Accumulator(date:Date,value:double)<>>+ getSum(date:Date):double+ change(date:Date,value:d
ouble)+ reset(date:Date,value:double)Accumulator# Account
(date:Date,id:int)# record(date:Date,amount:double,des
c:string)<>#error(msg:string) <>+ getId()
:int<>+ getBalance():double<>+deposit(d
ate:Date,amount:double,desc:string)<>+withdr
aw(date:Date,amount:double,desc:string)<>+se
ttle(date:Date)<><>+show()<>+ get
Total():doubleAccount- id:string- balance:double- total
:double1//date.h#ifndef__DATE_H__#define__DATE_H__class
Date{ //日期类private: intyear; //年 intmonth; //月 intday;
//日 inttotalDays; //该日期是从公元元年1月1日开始的第几天public: Date(intyear
,intmonth,intday); //用年、月、日构造日期 intgetYear()const{return
year;} intgetMonth()const{returnmonth;} intgetDay()co
nst{returnday;} intgetMaxDay()const; //获得当月有多少天 boolisL
eapYear()const{ //判断当年是否为闰年 returnyear%4==0&&year%10
0!=0||year%400==0; } voidshow()const; //输出当前日期 int
operator-(constDate&date)const{ //计算两个日期之间差多少天 returnto
talDays-date.totalDays; }};#endif//__DATE_H__8.6综合实例——对
个人银行账户管理程序的改进例8-8(续)//accumulator.h#ifndef__ACCUMULATOR_H__#
define__ACCUMULATOR_H__#include"date.h"classAccumulator{ //
将某个数值按日累加private: DatelastDate; //上次变更数值的时期 doublevalue; //数
值的当前值 doublesum; //数值按日累加之和public: doublegetSum(constDate
&date)const{ returnsum+value(date-lastDate); } //该类
其它成员函数的原型和实现与例7-10完全相同,不再重复给出};#endif//__ACCUMULATOR_H__8
.6综合实例——对个人银行账户管理程序的改进例8-8(续)//account.h#ifndef__ACCOUNT_H__
#define__ACCOUNT_H__#include"date.h"#include"accumulator.h"
#include?classAccount{//账户类private: std::string
id; //帐号 doublebalance; //余额 staticdoubletotal;//所有账户的总金额p
rotected: //供派生类调用的构造函数,id为账户 Account(constDate&date,consts
td::string&id); //记录一笔帐,date为日期,amount为金额,desc为说明 voidrecord(
constDate&date,doubleamount,conststd::string&desc); //报告错
误信息 voiderror(conststd::string&msg)const;8.6综合实例——对个人银行账
户管理程序的改进例8-8(续)public: conststd::string&getId()const{retu
rnid;} doublegetBalance()const{returnbalance;} staticd
oublegetTotal(){returntotal;} //存入现金,date为日期,amount为金额,desc
为款项说明 virtualvoiddeposit(constDate&date,doubleamount,cons
tstd::string&desc)=0; //取出现金,date为日期,amount为金额,desc为款项说明 vi
rtualvoidwithdraw(constDate&date,doubleamount,conststd::s
tring&desc)=0; //结算(计算利息、年费等),每月结算一次,date为结算日期 virtualvoid
settle(constDate&date)=0; //显示账户信息 virtualvoidshow()cons
t;};//SavingsAccount和CreditAccount两个类的定义与例7-10完全相同,不再重复给出#end
if//__ACCOUNT_H__8.6综合实例——对个人银行账户管理程序的改进例8-8(续)//account.
cpp//仅下面的函数定义与例7-10不同,其它皆相同,不再重复给出voidSavingsAccount::settle(c
onstDate&date){ if(date.getMonth()==1){ //每年的一月计算一次利息 d
oubleinterest=acc.getSum(date)rate /(date-Date(date.g
etYear()-1,1,1)); if(interest!=0) record(date,intere
st,"interest"); acc.reset(date,getBalance()); }}?//8_8.cp
p#include"account.h"#includeusingnamespacestd;i
ntmain(){ Datedate(2008,11,1); //起始日期 //建立几个账户 SavingsAcc
ountsa1(date,"S3755217",0.015); SavingsAccountsa2(date,"023
42342",0.015);8.6综合实例——对个人银行账户管理程序的改进例8-8(续)CreditAccount
ca(date,"C5392394",10000,0.0005,50); Accountaccounts[]={
&sa1,&sa2,&ca}; constintn=sizeof(accounts)/sizeof(Acco
unt);//账户总数 cout<<"(d)deposit(w)withdraw(s)show(c)changed
ay(n)nextmonth(e)exit"< date.show(); cout<<"\tTotal:"<tcommand>"; intindex,day; doubleamount; stringdesc;
cin>>cmd; switch(cmd){ case''d'': //存入现金 cin>>index>
>amount; getline(cin,desc); accounts[index]->deposit(date
,amount,desc); break; 8.6综合实例——对个人银行账户管理程序的改进例8-8(续)
C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版)C++语言程序设计(第4版),郑莉,清华大学C++语
言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学
C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清
华大学C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),
郑莉,清华大学第八章多态性清华大学郑莉目录8.1多态性概述8.2运算符重载8.3虚函数8.4
抽象类8.5程序实例—变步长梯形积分算法求解函数的定积分8.6综合实例——对个人银行账户管理程序的改进8.7
深度探索8.8小结8.1多态性概述多态是指操作接口具有表现多种形态的能力,即能根据操作环境的不同采用不同的处理方式
。多态性是面向对象系统的主要特性之一,在这样的系统中,一组具有相同基本语义的方法能在同一接口下为不同的对象服务。C++语言支持
的多态性可以按其实现的时机分为编译时多态和运行时多态两类。8.1多态性概述8.1.1多态的类型多态的类型重载多
态强制多态包含多态参数多态根据多态性作用的时机可以分为编译时的多态运行时的多态8.1多态性概述8.1.2
多态的实现绑定是指将一个标识符名和一个存储地址联系在一起的过程编译时的多态绑定工作在编译连接阶段完成的情况称为静态绑定。运
行时的多态绑定工作在程序运行阶段完成的情况称为动态绑定8.1多态性概述8.2运算符重载思考:用“+”、“-”能够
实现复数的加减运算吗?实现复数加减运算的方法——重载“+”、“-”运算符运算符重载是对已有的运算符赋予多
重含义,使同一个运算符作用于不同类型的数据时导致不同的行为。8.2.1运算符重载的规则C++几乎可以重载全部的运算符,
而且只能够重载C++中已经有的。不能重载的运算符举例:“.”、“.”、“::”、“?:”重载之后运算符的优先级和结合性都不会
改变。运算符重载是针对新类型数据的实际需要,对原有运算符进行适当的改造。两种重载方式:重载为类的非静态成员函数和重载为非成员函
数。8.2运算符重载8.2.2运算符重载为成员函数声明形式函数类型operator运算符(形参){
......}重载为类成员函数时参数个数=原操作数个数-1 (后置++、--除外)重载为非成员函数时参数个
数=原操作数个数,且至少应该有一个自定义类型的形参。8.2运算符重载8.2.2运算符重载为成员函数(续)双目运算符
B如果要重载B为类成员函数,使之能够实现表达式oprd1Boprd2,其中oprd1为A类对象,则B应被重载
为A类的成员函数,形参类型应该是oprd2所属的类型。经重载后,表达式oprd1Boprd2相当于oprd1.
operatorB(oprd2)如:Oprd1+oprd2相当于oprd1.operator+(oprd2)8.2运
算符重载例8-1复数类加减法运算重载—成员函数形式将“+”、“-”运算重载为复数类的成员函数。规则:实部和虚部
分别相加减。操作数:两个操作数都是复数类的对象。8.2运算符重载——8.2.2运算符重载为成员函数例8-
1(续)#includeusingnamespacestd;classComplex{ //复
数类定义public: //外部接口 Complex(doubler=0.0,doublei=0.0):re
al(r),imag(i){} //构造函数 Complexoperator+(constComplex&c2)
const; //运算符+重载成员函数 Complexoperator-(constComplex&c2)cons
t; //运算符-重载成员函数 voiddisplay()const; //输出复数private: //私有数据成员
doublereal; //复数实部 doubleimag; //复数虚部};8.2运算符重载——8.2.2
运算符重载为成员函数例8-1(续)ComplexComplex::operator+(constComplex&c2
)const{ //重载运算符函数实现 returnComplex(real+c2.real,imag+c2.i
mag);//创建一个临时无名对象作为返回值}ComplexComplex::operator-(constCom
plex&c2)const{ //重载运算符函数实现 returnComplex(real-c2.real,ima
g-c2.imag);//创建一个临时无名对象作为返回值}voidComplex::display()const{
cout<<"("<重载——8.2.2运算符重载为成员函数例8-1(续)intmain(){ //主函数 Complexc1(5,
4),c2(2,10),c3; //定义复数类的对象 cout<<"c1=";c1.display(); co
ut<<"c2=";c2.display(); c3=c1-c2; //使用重载运算符完成复数减法 cout
<<"c3=c1-c2=";c3.display(); c3=c1+c2; //使用重载运算符完成复数
加法 cout<<"c3=c1+c2=";c3.display(); return0;}8.2运
算符重载——8.2.2运算符重载为成员函数例8-1(续)程序输出的结果为:c1=(5,4)c2=(2,
10)c3=c1-c2=(3,-6)c3=c1+c2=(7,14)8.2运算符重载——
8.2.2运算符重载为成员函数运算符成员函数的设计前置单目运算符U如果要重载U为类成员函数,使之能够实现表达式U
oprd,其中oprd为A类对象,则U应被重载为A类的成员函数,无形参。经重载后,表达式Uoprd相当于o
prd.operatorU()8.2运算符重载——8.2.2运算符重载为成员函数运算符成员函数的设计(续)后
置单目运算符++和--如果要重载++或--为类成员函数,使之能够实现表达式oprd++或oprd--,其中opr
d为A类对象,则++或--应被重载为A类的成员函数,且具有一个int类型形参。经重载后,表达式oprd++
相当于oprd.operator++(0)8.2运算符重载——8.2.2运算符重载为成员函数例8-2运算
符前置++和后置++重载为时钟类的成员函数。前置单目运算符,重载函数没有形参,对于后置单目运算符,重载函数需要有一个整型形参。
操作数是时钟类的对象。实现时间增加1秒钟。8.2运算符重载——8.2.2运算符重载为成员函数#include<
iostream>usingnamespacestd;classClock { //时钟类定义public: //外部
接口 Clock(inthour=0,intminute=0,intsecond=0); voidshowTime()const; Clock&operator++(); //前置单目运算符重载 Clockoperator++(int); //后置单目运算符重载private: //私有数据成员 inthour,minute,second;};?Clock::Clock(inthour/=0/,intminute/=0/,intsecond/=0/){ if(0<=hour&&hour<24&&0<=minute&&minute<60 &&0<=second&&second<60){ this->hour=hour; this->minute=minute; this->second=second; }else cout<<"Timeerror!"<=60){ second-=60; minute++; if(minute>=60){ minute-=60; hour=(hour+1)%24; } } returnthis;}?ClockClock::operator++(int){ //后置单目运算符重载 //注意形参表中的整型参数 Clockold=this; ++(this); //调用前置“++”运算符 returnold;}8.2运算符重载——8.2.2运算符重载为成员函数例8-2(续)intmain(){ ClockmyClock(23,59,59); cout<<"Firsttimeoutput:"; myClock.showTime(); cout<<"ShowmyClock++:"; (myClock++).showTime(); cout<<"Show++myClock:"; (++myClock).showTime(); return0;}8.2运算符重载——8.2.2运算符重载为成员函数例8-2(续)运行结果:Firsttimeoutput:23:59:59ShowmyClock++:23:59:59Show++myClock:0:0:1C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版)C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学C++语言程序设计(第4版),郑莉,清华大学2013/12/3
献花(0)
+1
(本文系玉诗首藏)