《C++面向?qū)ο蟪绦蛟O(shè)計(jì)》課件第6章_第1頁(yè)
《C++面向?qū)ο蟪绦蛟O(shè)計(jì)》課件第6章_第2頁(yè)
《C++面向?qū)ο蟪绦蛟O(shè)計(jì)》課件第6章_第3頁(yè)
《C++面向?qū)ο蟪绦蛟O(shè)計(jì)》課件第6章_第4頁(yè)
《C++面向?qū)ο蟪绦蛟O(shè)計(jì)》課件第6章_第5頁(yè)
已閱讀5頁(yè),還剩166頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論