版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
6.1靜態(tài)聯(lián)編和動(dòng)態(tài)聯(lián)編
(StaticBindingandDynamicBinding) 6.2虛函數(shù)(VitualFunctions)
6.3純虛函數(shù)和抽象類
(PureVirtualFunctionsandAbstractClasses)
6.4運(yùn)算符重載(OperatorOverloading)
6.5實(shí)例分析(CaseStudy) 6.6常見(jiàn)編程錯(cuò)誤
(CommonProgrammingErrors)本章小結(jié)(ChapterSummary)
習(xí)題6(Exercises6)
6.1.1靜態(tài)聯(lián)編(StaticBinding)
聯(lián)編是指一個(gè)計(jì)算機(jī)程序自身彼此關(guān)聯(lián)的過(guò)程。聯(lián)編在編譯和連接時(shí)進(jìn)行,稱為靜態(tài)聯(lián)編。
6.1靜態(tài)聯(lián)編和動(dòng)態(tài)聯(lián)編(StaticBindingandDynamicBinding)【例6-1】
分析程序輸出結(jié)果,理解靜態(tài)聯(lián)編的含義。
程序如下:
#include<iostream>
constdoublePI=3.14;
usingnamespacestd;
classFigure //定義基類
{
public:
?Figure(){}; doublearea()const{return0.0;}
};
classCircle:publicFigure//定義派生類,公有繼承方式
{
public:
Circle(doublemyr){R=myr;}
doublearea()const{returnPI*R*R;}
protected:
doubleR;
};classRectangle:publicFigure
//定義派生類,公有繼承方式
{
public:
Rectangle(doublemyl,doublemyw){L=myl;W=myw;}
doublearea()const{returnL*W;}
private:
doubleL,W;
};
intmain()
{?Figurefig; //基類Figure對(duì)象
doublearea;
area=Fig.area();
cout<<"Areaoffigureis"<<area<<endl;
Circlec(3.0); //派生類Circle對(duì)象
area=c.area();
cout<<"Areaofcircleis"<<area<<endl;
Rectanglerec(4.0,5.0); //派生類Rectangle對(duì)象
area=rec.area(); cout<<"Areaofrectangleis"<<area<<endl;
return0;
}
程序運(yùn)行結(jié)果為:
Areaoffigureis0
Areaofcircleis28.26
Areaofrectangleis20【例6-2】
靜態(tài)聯(lián)編的問(wèn)題。
程序如下:
#include<iostream>
constdoublePI=3.14;
usingnamespacestd;
classFigure //定義基類
{
public:
Figure(){};
doublearea()const{return0.0;}
};classCircle:publicFigure//定義派生類,公有繼承方式
{
public:
Circle(doublemyr){R=myr;}
doublearea()const{returnPI*R*R;}
protected:
doubleR;
};classRectangle:publicFigure//定義派生類,公有繼承方式
{
public:
Rectangle(doublemyl,doublemyw){L=myl;W=myw;}
doublearea()const{returnL*W;}
private:
doubleL,W;
};voidfunc(Figure&p) //形參為例基類的引用
{
cout<<p.area()<<endl;
}
intmain()
{
Figurefig; //基類Figure對(duì)象
cout<<"AreaofisFigureis";
func(fig); Circlec(3.0); //Circle派生類對(duì)象
cout<<"Areaofcircleis";
func(c);
Rectanglerec(4.0,5.0); //Rectangle派生類對(duì)象
cout<<"Areaofrectangleis";
func(rec);
?return0;
}程序運(yùn)行結(jié)果為:
Areaoffigureis0
Areaofcircleis0
Areaofrectangleis06.1.2動(dòng)態(tài)聯(lián)編(DynamicBinding)
聯(lián)編在程序運(yùn)行時(shí)進(jìn)行,稱為動(dòng)態(tài)聯(lián)編,或稱動(dòng)態(tài)綁定,又叫晚期聯(lián)編。在編譯、鏈接過(guò)程中無(wú)法解決的聯(lián)編問(wèn)題,要等到程序開(kāi)始運(yùn)行之后再來(lái)確定。動(dòng)態(tài)聯(lián)編的主要優(yōu)點(diǎn)是提供了更好的編程靈活性、問(wèn)題抽象性和程序易維護(hù)性,但是與靜態(tài)聯(lián)編相比,函數(shù)調(diào)用速度慢,因?yàn)閯?dòng)態(tài)聯(lián)編需要在程序運(yùn)行過(guò)程中搜索以確定函數(shù)調(diào)用(消息)與程序代碼(方法)之間的匹配關(guān)系。6.2.1虛函數(shù)的定義和使用
(DefinitionandUsageofVirtualFunctions)
1.定義虛函數(shù)
聲明虛函數(shù)的一般格式為:
class<類名>{
public:
6.2虛函數(shù)(VitualFunctions)virtual<返回類型><函數(shù)名>(<參數(shù)表>);
//虛函數(shù)的聲明
};
<返回類型><類名>::<函數(shù)名>(<參數(shù)表>) //虛函數(shù)的定義
{…}
2.使用虛函數(shù)
當(dāng)在類的層次結(jié)構(gòu)中聲明了虛函數(shù)以后,并不一定就能實(shí)現(xiàn)運(yùn)行時(shí)的多態(tài)性,必須合理調(diào)用虛函數(shù)才能實(shí)現(xiàn)動(dòng)態(tài)聯(lián)編。只有在程序中使用基類類型的指針或引用調(diào)用虛函數(shù)時(shí),系統(tǒng)才以動(dòng)態(tài)聯(lián)編方式實(shí)現(xiàn)對(duì)虛函數(shù)的調(diào)用。如果使用對(duì)象名調(diào)用虛函數(shù),系統(tǒng)仍然以靜態(tài)聯(lián)編方式完成對(duì)虛函數(shù)的調(diào)用,也就是說(shuō),用哪個(gè)類說(shuō)明的對(duì)象,就調(diào)用在哪個(gè)類中定義的虛函數(shù)?!纠?-3】
理解運(yùn)行時(shí)的多態(tài)性。
程序如下:
#include<iostream>
constdoublePI=3.14;
usingnamespacestd;
classA //定義基類
{
public:
A(){}; virtualdoublearea()const{return0.0;}
//定義為虛函數(shù)
};
classB:publicA
//定義派生類,公有繼承方式
{
public:
B(doublemyr){R=myr;}
virtualdoublearea()const{returnPI*R*R;}
//定義為虛函數(shù)
protected:
doubleR;
};
classC:publicA//定義派生類,公有繼承方式
{public:
C(doublemyl,doublemyw){L=myl;W=myw;}
virtualdoublearea()const{returnL*W;} //定義為虛函數(shù)
private:
doubleL,W;
};
voidfunc(A&p) //形參為基類的引用
{
cout<<p.area()<<endl;
}doublemain()
{
Afig; //基類A對(duì)象
cout<<"AreaofAis";
func(fig);
Bc(3.0); //B派生類對(duì)象
cout<<"AreaofBis";
func(c);Crec(4.0,5.0); //C派生類對(duì)象
cout<<"AreaofCis";
func(rec);
return0;
}
程序運(yùn)行結(jié)果為:
AreaofAis0
AreaofBis28.26
AreaofCis20
3.虛函數(shù)與函數(shù)重載的關(guān)系
在派生類中重新定義基類中的虛函數(shù),是函數(shù)重載的另一種形式。但虛函數(shù)與一般重載函數(shù)有區(qū)別,具體區(qū)別在于:
(1)重載函數(shù)的調(diào)用是以所傳遞參數(shù)序列的差別作為調(diào)用不同函數(shù)的依據(jù);而虛函數(shù)是根據(jù)對(duì)象的不同去調(diào)用不同類的虛函數(shù)。
(2)重載函數(shù)在編譯時(shí)表現(xiàn)出多態(tài)性,是靜態(tài)聯(lián)編;虛函數(shù)則在運(yùn)行時(shí)表現(xiàn)出多態(tài)性,是動(dòng)態(tài)聯(lián)編。
(3)構(gòu)造函數(shù)可以重載,析構(gòu)函數(shù)不能重載;正好相反,構(gòu)造函數(shù)不能定義為虛函數(shù),析構(gòu)函數(shù)能定義為虛函數(shù)。
(4)重載函數(shù)只要求函數(shù)有相同的函數(shù)名,并且重載函數(shù)是在相同作用域中定義的名字相同的不同函數(shù);而虛函數(shù)不僅要求函數(shù)名相同,而且要求函數(shù)的簽名、返回類型也相同。
(5)重載函數(shù)可以是成員函數(shù)或友元函數(shù);而虛函數(shù)只能是非靜態(tài)成員函數(shù)。6.2.2虛函數(shù)的特性(VirtualFunctionsFeature)
由虛函數(shù)實(shí)現(xiàn)的動(dòng)態(tài)多態(tài)性就是:同一類中不同的對(duì)象對(duì)同一函數(shù)調(diào)用作出不同的響應(yīng)。在派生類中重新定義函數(shù)時(shí),要求函數(shù)名、函數(shù)類型、函數(shù)參數(shù)個(gè)數(shù)和類型全部與基類的虛函數(shù)相同,并根據(jù)派生類的需要重新定義函數(shù)體。【例6-4】
虛函數(shù)的特性。
程序如下:
//繼承虛屬性
#include<iostream>
usingnamespacestd;
classBase
{
public:
virtualintfunc(intx) //虛函數(shù)
{ cout<<"ThisisBaseclass";
returnx;
}
};
classSubclass:publicBase
{
public:
intfunc(intx) //實(shí)為虛函數(shù)
{ cout<<"ThisisSubclass";
returnx;
}
};
voidtest(Base&x)
{
cout<<"x="<<x.func(5)<<endl;
}
voidmain()
{
Basebc;Subclasssc;
test(bc);
test(sc);
}
程序運(yùn)行結(jié)果為:
ThisisBaseclassx=5
ThisisSubclassx=5【例6-5】
演示虛函數(shù)使用不恰當(dāng)。
程序如下:
#include<iostream>
usingnamespacestd;
classBase
{
public:
virtualintfunc(intx)
//虛函數(shù)返回類型為int
{
cout<<"ThisisBaseclass";returnx;
}
};
classSubclass:publicBase
{
public:
virtualfloatfunc(intx)
//虛函數(shù)返回類型為float
{ cout<<"ThisisSubclass";
floaty=float(x);
returny;
}
};
voidtest(Base&x)
{
cout<<"x="<<x.func(5)<<endl;}
voidmain()
{
Basebc;
Subclasssc;
test(bc);
test(sc);
}【例6-6】
演示虛函數(shù)特性失效程序。
程序如下:
#include<iostream>
usingnamespacestd;
classBase
{
public: virtualintfunc(intx) //虛函數(shù),形參為int型
{
cout<<"ThisisBaseclass";
returnx;
}
};
classSubclass:publicBase
{
public: virtualintfunc(floatx) //虛函數(shù),形參為float型
{
cout<<"ThisisSubclass";
inty=float(x);
returny;
}
};
voidtest(Base&x)
{
cout<<"x="<<x.func(5)<<endl;}
voidmain()
{
Basebc;
Subclasssc;
test(bc);
test(sc);
}程序運(yùn)行結(jié)果為:
ThisisBaseclassx=5
ThisisBaseclassx=5
一個(gè)類中的虛函數(shù)說(shuō)明只對(duì)派生類中重定義的函數(shù)有影響,對(duì)它的基類中的函數(shù)并沒(méi)有影響?!纠?-7】
虛函數(shù)對(duì)它的基類中的函數(shù)沒(méi)有影響程序。
程序如下:
#include<iostream>
usingnamespacestd;
classA
{
public:
intfunc(intx) //不是虛函數(shù)
{
cout<<"ThisisAclass";
returnx;
}
};
classB:publicA
{
public:
virtualintfunc(intx) //虛函數(shù)
{
cout<<"ThisisBclass";
returnx;
}
}; classC:publicB
{
public:
intfunc(intx) //自動(dòng)成為虛函數(shù)
{
cout<<"ThisisCclass";
returnx;
}
}; voidmain()
{Bbb
;
B&sc3=bb;
Csc2;
A&bc=sc2;
cout<<"x="<<bc.func(5)<<endl;
cout<<"x="<<sc3.func(5)<<endl;
B&sc1=sc2;
cout<<"x="<<sc1.func(5)<<endl;
}程序運(yùn)行結(jié)果為:
ThisisAclassx=5
ThisisBclassx=5
ThisisCclassx=5
程序分析:基類成員函數(shù)func()不是虛函數(shù),對(duì)于C類對(duì)象sc2而言調(diào)用基類的func()時(shí)是按靜態(tài)聯(lián)編進(jìn)行的,調(diào)用B類的func()時(shí)就會(huì)采取動(dòng)態(tài)聯(lián)編。6.3.1純虛函數(shù)(PureVirtualFunctions)
純虛函數(shù)是一種特殊的虛函數(shù),它是被標(biāo)明為不具體實(shí)現(xiàn)的虛函數(shù),從語(yǔ)法上講,純虛函數(shù)是在虛函數(shù)的后面加上“=0”,表示該虛函數(shù)無(wú)函數(shù)體,這里的“=”并非賦值運(yùn)算。聲明純虛函數(shù)的一般格式如下:
virtual<返回類型><函數(shù)名>(<參數(shù)表>)=0;6.3純虛函數(shù)和抽象類(PureVirtualFunctionsandAbstractClasses)【例6-8】
使用純虛函數(shù)。
程序如下:
#include<iostream.h>
#include<math.h> //包含pow(x,y)數(shù)學(xué)函數(shù)(求x的y次方)的聲明
classBase //抽象類
{
protected:
intx,y;
public:
voidsetx(inti,intj=0){x=i;y=j;} virtualvoiddisp()=0; //聲明純虛函數(shù)
};
classSquare:publicBase
{
public:
voiddisp()
{ cout<<"x="<<x<<":";
cout<<"xsquare="<<x*x<<endl;
}
};
classCube:publicBase{
public:
voiddisp()
{ cout<<"x="<<x<<":";
cout<<"xcube="<<x*x*x<<endl;
}
};
classChpow:publicBase
{
public:
voiddisp()
{ cout<<"x="<<x<<"y="<<y<<":";
cout<<"pow(x,y)="<<pow(double(x),double(y))<<endl;
}
};
voidmain()
{ Base*ptr; //ptr為對(duì)象指針
SquareB; //定義對(duì)象B
CubeC; //定義對(duì)象C
ChpowD; //定義對(duì)象D
ptr=&B; //ptr指向?qū)ο驜
ptr->setx(5); //相當(dāng)于B.setx(5)
ptr->disp(); //相當(dāng)于B.disp()
ptr=&C; //ptr指向?qū)ο驝
ptr->setx(6); //相當(dāng)于C.setx(6)
ptr->disp(); //相當(dāng)于C.disp()
ptr=&D; //ptr指向?qū)ο驞
ptr->setx(3,4); //相當(dāng)于D.setx(5)
ptr->disp(); //相當(dāng)于D.disp()
}程序運(yùn)行結(jié)果為:
x=5:xsquare=25
x=6:xcube=261
x=3y=4:pow(x,y)=816.3.2抽象類(AbstractClasses)
在許多情況下,定義不實(shí)例化為任何對(duì)象的類是很有用處的,這種類稱為“抽象類”。因?yàn)槌橄箢愐鳛榛惐黄渌惱^承,所以通常也把它稱為“抽象基類”。抽象基類不能用來(lái)建立實(shí)例化的對(duì)象。抽象類的唯一用途是為其他類提供合適的基類,其他類可以從它這里繼承接口和(或)繼承實(shí)現(xiàn)。6.3.3抽象類的應(yīng)用
(ApplicationofAbstractClasses)
在類層次結(jié)構(gòu)中,盡可能地為類設(shè)計(jì)一個(gè)統(tǒng)一的公共接口(界面),即采用抽象基類設(shè)計(jì)方法。一個(gè)統(tǒng)一的公共接口必須要經(jīng)過(guò)精心的分析和設(shè)計(jì)?!纠?-9】
建立一個(gè)如圖6-1所示圖形類的繼承層次結(jié)構(gòu)?;怱hape是抽象類,通過(guò)它能夠訪問(wèn)派生類Point、Circle、Cylinder的類名、面積、體積。
程序如下:
//Shape.h
#ifndefSHAPE_H
#defineSHAPE_H
#include<iostream>
usingnamespacestd;
classShape
{
public:
virtualdoublearea()const{return0.0;}
virtualdoublevolume()const{return0.0;}
virtualvoidprintShapeName()const=0;
virtualvoidprint()const=0;
};
#endif//Point.h
#ifndefPOINT_H
#definePOINT_H
#include"shape.h"
classPoint:publicShape{
public:
Point(int=0,int=0);
voidsetPoint(int,int);
intgetX()const{returnx;}
intgetY()const{returny;}
virtualvoidprintShapeName()const
{cout<<"Point:";}
virtualvoidprint()const;
private:
intx,y;
};
#endif
//Point.cpp#include"point.h"
Point::Point(inta,intb){setPoint(a,b);}
voidPoint::setPoint(inta,intb)
{
x=a;
y=b;
}voidPoint::print()const
{cout<<'['<<x<<","<<y<<']';
}
//Circle.h
#ifndefCIRCLE_H
#defineCIRCLE_H
#include"point.h"
classCircle:publicPoint{
public:
Circle(doubler=0.0,intx=0,inty=0);
voidsetRadius(double);
doublegetRadius()const;
virtualdoublearea()const;
virtualvoidprintShapeName()const{cout<<"Circle:";}
virtualvoidprint()const;
private:
doubleradius; //radiusofCircle
};#endif
//Circle.cpp
#include"circle.h"
Circle::Circle(doubler,inta,intb):Point(a,b)
{setRadius(r);}
voidCircle::setRadius(doubler){radius=r>0?r:0;}
doubleCircle::getRadius()const{returnradius;}
doubleCircle::area()const
{return3.14159*radius*radius;}voidCircle::print()const
{Point::print();cout<<";Radius="<<radius;
}
//Cylinder.h
#ifndefCYLINDR_H
#defineCYLINDR_H
#include"circle.h"
classCylinder:publicCircle
{
public:
Cylinder(doubleh=0.0,doubler=0.0,
intx=0,inty=0);
voidsetHeight(double);
doublegetHeight();
virtualdoublearea()const;
virtualdoublevolume()const;
virtualvoidprintShapeName()const{cout<<"Cylinder:";}
virtualvoidprint()const;
private:
doubleheight;
};#endif
//Cylinder.cpp
#include"cylinder.h"
Cylinder::Cylinder(doubleh,doubler,intx,inty):Circle(r,x,y)
{setHeight(h);}
voidCylinder::setHeight(doubleh)
{height=h>0?h:0;}
doubleCylinder::getHeight(){returnheight;}
doubleCylinder::area()const
{return2*Circle::area()+2*3.14159*getRadius()*height;
}
doubleCylinder::volume()const
{returnCircle::area()*height;}
voidCylinder::print()const
{
Circle::print();
cout<<";Height="<<height;
}//main.cpp
#include<iostream>
usingnamespacestd;
#include<iomanip.h>
#include"shape.h"
#include"point.h"
#include"circle.h"
#include"cylinder.h"voidvirtualViaPointer(constShape*);
voidvirtualViaReference(constShape&);
voidvirtualViaPointer(constShape*baseClassPtr)
{
baseClassPtr->printShapeName();
baseClassPtr->print();
cout<<"\nArea="<<baseClassPtr->area()
<<"\nVolume="<<baseClassPtr->volume()<<"\n\n";
}voidvirtualViaReference(constShape&baseClassRef)
{
baseClassRef.printShapeName();
baseClassRef.print();
cout<<"\nArea="<<baseClassRef.area()
<<"\nVolume="<<baseClassRef.volume()<<"\n\n";
}
intmain()
{ cout<<setiosflags(ios::fixed|ios::showpoint)
<<setprecision(2);
Pointpoint(7,11);
Circlecircle(3.5,22,8);
Cylindercylinder(10,3.3,10,10);
point.printShapeName();
point.print();
cout<<'\n'; circle.printShapeName();
circle.print();
cout<<'\n';
cylinder.printShapeName();
cylinder.print();
cout<<"\n\n";
Shape*arrayOfShapes[3];
arrayOfShapes[0]=&point;
arrayOfShapes[1]=&circle;
arrayOfShapes[2]=&cylinder;
cout<<“Virtualfunctioncallsmadeoff”
<<“base-classpointers\n”;
for(inti=0;i<3;i++)
virtualViaPointer(arrayOfShapes[i]);
cout<<“Virtualfunctioncallsmadeoff”
<<“base-classreferences\n”;
for(intj=0;j<3;j++)
virtualViaReference(*arrayOfShapes[j]);
return0;
}圖6-1圖形類的繼承層次結(jié)構(gòu)程序運(yùn)行結(jié)果為:
Point:[7,11]
Circle:[22,8];Radius=3.50
Cylinder:[10,10];Radius=3.30;Height=10.00
Virtualfunctioncallsmadeoffbase-classpointers
Point:[7,11]
Area=0.00
Volume=0.00Circle:[22,8];Radius=3.50
Area=38.48
Volume=0.00
Cylinder:[10,10];Raduys=3.30;Height=10.00
Area=275.77
Volume=342.12
Virtualfunctioncallsmadeoffbase-classreferences
Point:[7,11]
Area=0.00
Volume=0.00Circle:[22,8];Radius=3.50
Area=38.48
Volume=0.00
Cylinder:[10,10];Raduys=3.30;Height=10.00
Area=275.77
Volume=342.12運(yùn)算符重載是指同樣的運(yùn)算符可以施加于不同類型的操作數(shù)上,使同樣的運(yùn)算符作用于不同類型的數(shù)據(jù)時(shí)可導(dǎo)致不同的行為。
C++語(yǔ)言中預(yù)定義的運(yùn)算符的操作對(duì)象只能是基本數(shù)據(jù)類型,例如:
inti=20,j=30;
floatx=35.6,y=47.8;
cout<<"i+j="<<i+j;
cout<<"x+y="<<x+y;
cout<<"i+x="<<i+x;
…6.4運(yùn)?算?符?重?載(OperatorOverloading)例如下面定義的一個(gè)簡(jiǎn)化的復(fù)數(shù)類,它向外界提供了加運(yùn)算:
classComplex
{
private:
floatReal;
floatImag;
public:
Complex(){Real=0;Imag=0;}
Complex(floatRe,floatIm)
{Real=Re;Imag=Im;}ComplexAdd(constComplex&c); //加運(yùn)算
};
inlineComplexComplex::Add(constComplex&c)
{
returnComplex(Real+c.Real,Imag+c.Imag);
}
voidmain()
{
Complexc1(5.0,10.0); //5+10i
Complexc2(3.0,-2.5); //3-2.5iComplexc;
c=c1.Add(c2); //8+7i
}
在函數(shù)main中,語(yǔ)句c=c1.Add(c2)的含義是:向復(fù)數(shù)類對(duì)象c1發(fā)送消息,請(qǐng)它完成把自己的復(fù)數(shù)值與對(duì)象c2的復(fù)數(shù)值相加的運(yùn)算,然后把求和后得出的復(fù)數(shù)值賦值給對(duì)象c。因此,當(dāng)像上面那樣定義了復(fù)數(shù)類之后,為完成復(fù)數(shù)c1和復(fù)數(shù)c2的相加操作,可以使用向?qū)ο蟀l(fā)送消息的函數(shù)調(diào)用方式:
c1.Add(c2)或c2.Add(c1)6.4.1運(yùn)算符重載的規(guī)則
(RulesofOperatorOverloading)
運(yùn)算符重載的規(guī)則如下:
(1)?C++語(yǔ)言中的運(yùn)算符除了少數(shù)幾個(gè)之外,全部可以重載,而且只能重載C++語(yǔ)言中已有的運(yùn)算符。
(2)重載之后的運(yùn)算符的優(yōu)先級(jí)和結(jié)合性都不會(huì)改變。
(3)不能改變?cè)\(yùn)算符操作數(shù)的個(gè)數(shù),如C++語(yǔ)言中的“~”是一個(gè)單目運(yùn)算符,只能有一個(gè)操作數(shù)。
(4)運(yùn)算符重載是針對(duì)新類型數(shù)據(jù)的實(shí)際需要,對(duì)原有運(yùn)算符進(jìn)行適當(dāng)?shù)母脑?,不能改變運(yùn)算符對(duì)預(yù)定義類型數(shù)據(jù)的操作方式。從這條規(guī)定可知,重載運(yùn)算符時(shí)必須至少有一個(gè)自定義類型的數(shù)據(jù)(即對(duì)象)作為操作數(shù)。
不能重載的運(yùn)算符只有5個(gè),它們是類屬關(guān)系運(yùn)算符“.”、成員指針運(yùn)算符“*”、作用域分辨符“::”、sizeof()運(yùn)算符和三目運(yùn)算符“?:”。運(yùn)算符重載的形式有兩種,重載為類的成員函數(shù)和重載為類的友元函數(shù)。運(yùn)算符重載為類的成員函數(shù)的一般語(yǔ)法為:
<函數(shù)類型>operator<運(yùn)算符>(<參數(shù)表>)
{
<函數(shù)體;>
}運(yùn)算符重載為類的友元函數(shù)的一般語(yǔ)法為:
<函數(shù)類型>operator<運(yùn)算符>(<參數(shù)表>)
{
<函數(shù)體;>
}6.4.2運(yùn)算符重載為成員函數(shù)
(OperatorOverloadedintoMemberFunction)
前面已經(jīng)講到,運(yùn)算符重載實(shí)質(zhì)上就是運(yùn)算符函數(shù)的重載。在實(shí)際使用時(shí),總是通過(guò)該類的某個(gè)對(duì)象訪問(wèn)重載的運(yùn)算符。
(1)如果是單目運(yùn)算符,函數(shù)的參數(shù)為空。
(2)如果是雙目運(yùn)算符,參數(shù)表中有一個(gè)參數(shù)。對(duì)于雙目運(yùn)算符D,如果要重載為類X的成員函數(shù),實(shí)現(xiàn)表達(dá)式xobj1Dxobj2,則函數(shù)只有一個(gè)形參,形參的類型是xobj2所屬的類型,經(jīng)過(guò)重載后,表達(dá)式xobj1Dxobj2相當(dāng)于函數(shù)調(diào)用xobj1.operatorD(xobj2)。
【例6-10】
用成員函數(shù)形式實(shí)現(xiàn)復(fù)數(shù)類加減法運(yùn)算符重載。程序如下:
#include<iostream>
usingnamespacestd;
classComplex
{
private:
floatReal;
floatImag;
public:
Complex(){Real=0;Imag=0;}Complex(floatRe,floatIm)
{Real=Re;Imag=Im;}
Complexoperator+(Complexc);
//運(yùn)算符“+”重載成員函數(shù)
Complexoperator-(Complexc); //運(yùn)算符“-”重載成員函數(shù)
voiddisplay();
};
ComplexComplex::operator+(Complexc)
{
returnComplex(Real+c.Real,Imag+c.Imag);
}ComplexComplex::operator-(Complexc)
{
returnComplex(Real-c.Real,Imag-c.Imag);
}
voidComplex::display()
{
cout<<"("<<Real<<","<<Imag<<")"<<endl;
}
voidmain()
{Complexc1(5.0,10.0),c2(3.0,-2.5),c3;//定義復(fù)數(shù)類對(duì)象
cout<<"c1=";c1.display();
cout<<"c2=";c2.display();
c3=c1+c2; //用重載運(yùn)算符實(shí)現(xiàn)復(fù)數(shù)加法
cout<<"c3=c1+c2";
c3.display();
c3=c1-c2; //用重載運(yùn)算符實(shí)現(xiàn)復(fù)數(shù)減法
cout<<"c1-c2=";
c3.display();
}程序的運(yùn)行結(jié)果為:
c1=(5,10)
c2=(3,-2,5)
c3=c1+c2(8,7.5)
c1-c2=(2,12.5)【例6-11】
成員函數(shù)形式實(shí)現(xiàn)單目運(yùn)算符“++”的重載。
程序如下:
#include<iostream>
usingnamespacestd;
classClock
{
private:intHour,Minute,Second;
public:
Clock(intH=0,intM=0,intS=0);
voidShowTime();
voidoperator++(); //前置單目運(yùn)算符重載成員函數(shù)
Clockoperator++(int);
//后置單目運(yùn)算符重載成員函數(shù)
};Clock::Clock(intH,intM,intS)
{
if(H>=0&&H<24&&M>=0&&M<60&&S>=0&&S,60)
{Hour=H;Minute=M;Second=S;}
else
cout<<"時(shí)間錯(cuò)誤!"<<endl;
}
voidClock::ShowTime()
{
cout<<Hour<<":"<<Minute<<":"<<Second<<endl;}
voidClock::operator++()
{
Second++;
if(Second>=60)
{Second-=60;
Minute++;
if(Minute>=60)
{Minute-=60;Hour++;Hour%=24;}
}
}ClockClock::operator++(int)
{
Clockh(Hour,Minute,Second);
Second++;
if(Second>=60)
{Second-=60;
Minute++;
if(Minute>=60)
{Minute-=60;Hour++;Hour%=24;}
} returnh;
}
voidmain()
{
Clockclock(23,59,59),c; //定義時(shí)鐘對(duì)象
cout<<"Firsttime:";clock.ShowTime();
++clock;
cout<<"++clock:";clock.ShowTime();
c=clock++;
cout<<"clock++:";c.ShowTime();
cout<<"colck:";clock.ShowTime();
}程序運(yùn)行結(jié)果:
Firsttime:23:59:59
++clock:0:0:0
clock++:0:0:0
colck:0:016.4.3運(yùn)算符重載為友元函數(shù)
(OperatorOverloadedintoFriendFunction)
運(yùn)算符也可以重載為類的友元函數(shù),這樣,它就可以訪問(wèn)該類中的任何數(shù)據(jù)成員。這時(shí),運(yùn)算符所需要的操作數(shù)都需要通過(guò)函數(shù)的參數(shù)表來(lái)傳遞,在參數(shù)表中形參從左到右的順序就是運(yùn)算符操作數(shù)的順序。
(1)對(duì)于雙目運(yùn)算符D,如果要重載為類X的友元函數(shù),實(shí)現(xiàn)表達(dá)式xobj1Dxobj2,則函數(shù)有兩個(gè)形參,其中xobj1和xobj2是類X的對(duì)象,經(jīng)過(guò)重載后,表達(dá)式xobj1Dxobj2相當(dāng)于函數(shù)調(diào)用operatorD(xobj1,xobj2)。
【例6-12】用友元函數(shù)實(shí)現(xiàn)分?jǐn)?shù)類的相加、相等運(yùn)算。
程序如下:
#include<iostream>
usingnamespacestd;
#include<stdlib.h>classFranc
{
private:
intnume;
intdeno;
public:
Franc(){}
Franc(intnu,intde)
{
if(de==0)
{
cerr<<"除數(shù)為零!"<<endl;
exit(1);//終止程序運(yùn)行,返回C++主操作窗口
}
nume=nu;deno=de;
}
friendFrancoperator+(Francf1,Francf2);
//運(yùn)算符“+”重載友元函數(shù)
friendbooloperator==(Francf1,Francf2);
//運(yùn)算符“==”重載友元函數(shù)
voidFranSimp();
voiddisplay();
};
voidFranc::display()
{
cout<<"("<<nume<<"/"<<deno<<")"<<endl; //輸出分?jǐn)?shù)
}voidFranc::FranSimp() //化簡(jiǎn)為最簡(jiǎn)分?jǐn)?shù)
{ //求x分?jǐn)?shù)的分子和分母的最大公約數(shù)
intm,n,r;
m=nume;n=deno;
r=m%n;
while(r!=0)
{
m=n;n=r;
r=m%n;
}
if(n!=0)
{ //化簡(jiǎn)為最簡(jiǎn)分式
nume/=n;
deno/=n;
}
if(deno<0)
{ //分母為負(fù)時(shí)處理
nume=-nume;
deno=-deno;
}
}Francoperator+(Francf1,Francf2)
{
Francf;
f.nume=f1.nume*f2.deno+f2.nume*f1.deno;
//計(jì)算結(jié)果分?jǐn)?shù)的分子
f.deno=f1.deno*f2.deno; //計(jì)算結(jié)果分?jǐn)?shù)的分母
f.FranSimp(); //對(duì)結(jié)果進(jìn)行簡(jiǎn)化處理
returnf; //返回結(jié)果分?jǐn)?shù)
}
booloperator==(Francf1,Francf2)
{
if(f1.nume*f2.deno==f2.nume*f1.deno)
returntrue;
else
returnfalse;
}
voidmain()
{Francf1(5,6),f2(1,-2),f3; //定義分?jǐn)?shù)類對(duì)象
cout<<"f1=";f1.display();
cout<<"f2=";f2.display();
f3=f1+f2; //用重載運(yùn)算符實(shí)現(xiàn)分?jǐn)?shù)加法
cout<<"f1+f2=";
if(f1==f2)cout<<"f1和f2相等"<<endl;
//判斷f1和f2是否相等
elsecout<<"f1和f2不相等"<<endl;
}程序運(yùn)行結(jié)果:
f1=(5/6)
f2=(1/-2)
f1+f2=f1和f2不相等6.5.1問(wèn)題提出(Questions)
【例6-13】
小型公司人員的信息管理系統(tǒng)。
某小型公司主要有4類人員:經(jīng)理、兼職技術(shù)人員、銷售經(jīng)理、兼職銷售員,這些人員具有以下屬性。6.5實(shí)例分析(CaseStudy)6.5.2類設(shè)計(jì)(ClassesDesigning)
根據(jù)題目要求,設(shè)計(jì)一個(gè)基類employee,然后派生出technician(兼職技術(shù)人員)類、manager(經(jīng)理)類和salesman(兼職銷售員)類。由于銷售經(jīng)理既是經(jīng)理又是銷售人員,擁有兩類人員的屬性,因此同時(shí)繼承manager類和salesman類。級(jí)別提升可以通過(guò)升級(jí)函數(shù)promote(int)實(shí)現(xiàn),其函數(shù)體是一樣的,只是不同類型的人員升級(jí)時(shí)使用的參數(shù)不同(指定提升的級(jí)數(shù)),可以將其在基類中定義,各派生類中可以繼承該函數(shù)。主函數(shù)中根據(jù)不同職員使用不同參數(shù)調(diào)用。
由于salesManager(銷售經(jīng)理)類的兩個(gè)基類又有公共基類employee,為了避免二義性,將employee類設(shè)計(jì)為虛基類。
類圖設(shè)計(jì)如圖6-2所示。圖6-2例6-13的類圖6.5.3程序代碼設(shè)計(jì)(ProgramCoding)
本程序分為3個(gè)獨(dú)立的文檔:employee.h是類頭文件,包括各個(gè)類的聲明部分;empfun.cpp是類的實(shí)現(xiàn)文件,包括類中各成員函數(shù)的定義;liti6_8.cpp是主函數(shù)文件,實(shí)現(xiàn)人員信息管理。
源程序:
//employee.h頭文件
classemployee
{ //定義職員類protected:
char*name; //定義姓名
intEmpNo; //個(gè)人編號(hào)
intgrade; //級(jí)別
doublesumPay; //月薪總額
staticintemployeeNo;
//本公司職員編號(hào)目前最大值public:
employee();
~employee();
virtualvoidpay()=0; //計(jì)算月薪函數(shù),解決:虛函數(shù)
voidpromote(int); //升級(jí)函數(shù)
virtualvoiddisplayStatus(); //顯示人員信息
};classtechnician:publicemployee
//兼職技術(shù)人員類(公有派生)
{protected:
floathourlyRate; //每小時(shí)酬金
intworkHours; //當(dāng)月工作時(shí)數(shù)
public:
technician();
voidpay();
//計(jì)算月薪函數(shù)
voiddisplayStatus();
//顯示人員信息
};classsalesman:virtualpublicemployee//兼職推銷員類
{protected:
doubleCommRate; //按銷售額提取酬金百分比
doublesales; //當(dāng)月銷售額
?public:
salesman();
voidpay(); //計(jì)算月薪函數(shù)
voiddisplayStatus(); //顯示人員信息};
classmanager:virtualpublicemployee
//經(jīng)理類
{ protected:
floatmonthlyPay;
//固定月薪數(shù)
??public:
manager();
voidpay();
//計(jì)算月薪函數(shù)
voiddisplayStatus();
//顯示人員信息
};classsalesManager:publicmanager,publicsalesman
//銷售經(jīng)理類
{ public:
salesManager();
voidpay(); //計(jì)算月薪函數(shù)
voiddisplayStatus(); //顯示人員信息
};
//empfun.cpp
#include<iostream.h>
#include<string.h>
#include"employee.h"
intemployee::employeeNo=1000; //員工編號(hào)基數(shù)employee::employee()
{
charstr[20];cout<<"\n輸入雇員姓名:";
cin>>str;
name=newchar[strlen(str)+1]; //動(dòng)態(tài)申請(qǐng)
strcpy(name,str);
EmpNo=employeeNo++; //新員工編號(hào)自動(dòng)生成
grade=1; //級(jí)別初始1
sumPay=0.0; //月薪總額初始0
}employee::~employee()
{
delete[]name; //釋放空間
}
voidemployee::displayStatus()
{
cout<<name<<":"<<"編號(hào):"<<EmpNo<<",級(jí)別:"<<grade<<",本月工資"<<sumPay<<endl;
}
voidemployee::promote(intincrement){
grade+=increment; //升級(jí)
}
technician::technician()
{
hourlyRate=100; //每小時(shí)酬金100元
}
voidtechnician::pay()
{
cout<<"輸入本月工作時(shí)數(shù):";cin>>workHours; //計(jì)算月薪 sumPay=hourlyRate*workHours;}
voidtechnician::displayStatus()
{
cout<<"兼職技術(shù)人員:";
employee::displayStatus();
}
salesman::salesman()
{
CommRate=0.04; //提成比例
}
voidsalesman::pay()
{
cout<<"輸入本月銷售額:";cin>>sales; sumPay=sales*CommRate; //月薪=銷售提成
}
voidsalesman::displayStatus()
{
cout<<"推銷員:";
employee::displayStatus();
}
manager::manager()
{
monthlyPay=8000;
}voidmanager::pay()
{
sumPay=monthlyPay; //月薪總額=固定月薪數(shù)
}
voidmanager::displayStatus()
{
cout<<"經(jīng)理:";
employee::displayStatus();
}
salesManager::salesManager()
{ monthlyPay=5000;
CommRate=0.0005;
}
voidsalesManager::pay()
{
cout<<"輸入"<<employee::name<<"部門(mén)本月銷售總額:";cin>>sales;
sumPay=monthlyPay+CommRate*sales;
//月薪=固定月薪+銷售提成
}
voidsalesManager::displayStatus()
{ cout<<"銷售經(jīng)理:";
employee::displayStatus();
}
//ch6_8.cpp
#include<iostream.h>
#include<string.h>
#include"employee.h"
voidmain(){//經(jīng)理:";
managerm1;
mote(3);
m1.pay();
m1.displayStatus();
//兼職技術(shù)人員:";
techniciant1;
mote(2);
t1.pay();t1.displayStatus();
//銷售經(jīng)理:";
salesManagersm1;
mote(2);
sm1.pay();
sm1.displayStatus();
//兼職推銷員:";
salesmans1;
mote(3);s1.pay();
s1.displayStatus();
cout<<"\n使基類指針指向子類對(duì)象"<<endl;
employee*ptr[4]={&m1,&t1,&sm1,&s1};
for(inti=0;i<4;i++)
ptr[i]->displayStatus();
}程序運(yùn)行結(jié)果為:
輸入雇員姓名:wangping
經(jīng)理:wangping:編號(hào):1000,級(jí)別:4,本月工資8000
輸入雇員姓名:wujing
輸入本月工作時(shí)數(shù):100
兼職技術(shù)人員:wujing:編號(hào):1001,級(jí)別:3,本月工資10000
輸入雇員姓名:zhaoguanglin
輸入部門(mén)本月銷售總額:5000
銷售經(jīng)理:zhaoguanglin:編號(hào):1002,級(jí)別:3,本月工資5002.5輸入雇員姓名:lifang
輸入本月銷售額:10000
推銷員:lifang:編號(hào):1003,級(jí)別:4,本月工資400
使基類指針指向子類對(duì)象
經(jīng)理:wangping:編號(hào):1000,級(jí)別:4,本月工資8000
兼職技術(shù)人員:wujing:編號(hào):1001,級(jí)別:3,本月工資10000
銷售經(jīng)理:zhaoguanglin:編號(hào):1002,級(jí)別:3,本月工資5002.5
推銷員:lifang:編號(hào):1003,級(jí)別:4,本月工資400
1.只有成員函數(shù)才可以聲明為虛函數(shù),聲明一個(gè)頂層函數(shù)為虛函數(shù)是錯(cuò)誤的。
virtualboolf();//***error:fisnotamethod
2.不能聲明一個(gè)靜態(tài)成員函數(shù)為虛函數(shù)。
classbase
{public:
virtualvoidm(); //ok.objectmethod
virtualstaticvoids(); //*****ERROR:staticmethod
};6.6常見(jiàn)編程錯(cuò)誤(CommonProgrammingErrors)
3.虛函數(shù)采用類內(nèi)聲明類外定義時(shí),只需在聲明處使用關(guān)鍵字virtual,定義處不需要使用virtual。
classbase
{public:
virtualvoidm1(){…}//ok
virtualvoidm2(){…}//ok
};
//****ERROR:virtualshouldnotoccurinadefinitionoutsidetheclassdeclaration
virtualvoidC::m2(){//…}
4.構(gòu)造函數(shù)不能聲明為虛函數(shù),但析構(gòu)函數(shù)可以是虛函數(shù)。
classbase{
public:
virtualbase();//*****ERROR:constructor
virtualbase(int);//*****ERROR:constructor
virtual~base();//ok.destructor
};
5.用new創(chuàng)建對(duì)象,在對(duì)象失效后,一定要用delete釋放該對(duì)象。
classbase
{
//…};
voidfun()
{base*p=newbase;//dynamicallycreateabaseaobject
//…useit
deletep;
}//deleteit
6.如果一個(gè)成員函數(shù)隱藏了繼承而來(lái)的成員函數(shù),不指定其全名來(lái)調(diào)用繼承的成員函數(shù)會(huì)導(dǎo)致錯(cuò)誤。
classbase{
public:
voidm(int){//…};
classdebase:publicbase{
public:
voidm(){//…};
};
intmain(){
debasea1;
a1.m(-58); //*****ERROR
:debase
::mhidesbase
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年某房地產(chǎn)公司與某家居企業(yè)關(guān)于智能家居系統(tǒng)的合同
- 雨水收集利用項(xiàng)目施工合同
- 垃圾焚燒發(fā)電廠大包工程施工合同
- 親子教育機(jī)構(gòu)店長(zhǎng)招聘合同樣本
- 研發(fā)服務(wù)租賃承包合同
- 劇院內(nèi)部裝修項(xiàng)目合同
- 林業(yè)作業(yè)拖拉機(jī)租賃合約
- 生態(tài)治理施工員聘用協(xié)議
- 河北省承德市2023-2024學(xué)年高一上學(xué)期期末考試數(shù)學(xué)試題(解析版)
- 設(shè)備維修進(jìn)度協(xié)議
- 中醫(yī)病歷書(shū)寫(xiě)基本規(guī)范
- 作物育種方法與實(shí)踐智慧樹(shù)知到期末考試答案2024年
- 個(gè)人建筑工程技術(shù)職業(yè)生涯發(fā)展規(guī)劃報(bào)告
- 排球《正面上手發(fā)球》教案
- 浣溪沙細(xì)雨斜風(fēng)作曉寒
- 2024-2030年中國(guó)pcba板行業(yè)市場(chǎng)現(xiàn)狀分析及競(jìng)爭(zhēng)格局與投資發(fā)展研究報(bào)告
- 2023年檢測(cè)站站長(zhǎng)工作總結(jié)報(bào)告
- 排球競(jìng)賽規(guī)則
- 2024版藥品管理法
- 中印戰(zhàn)爭(zhēng)完整版本
- 公路工程資料整理-課件
評(píng)論
0/150
提交評(píng)論