貴州大學計算機學院Visual C++maocpp5_第1頁
貴州大學計算機學院Visual C++maocpp5_第2頁
貴州大學計算機學院Visual C++maocpp5_第3頁
貴州大學計算機學院Visual C++maocpp5_第4頁
貴州大學計算機學院Visual C++maocpp5_第5頁
已閱讀5頁,還剩128頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第五章多態性

1

5.1編譯時的多態性與運行時的多態性

多態性是指用同一個名字定義不同的函數,這些函數執行不同但

又類似的操作。

聯編的概念:

一個源程序經過編譯、連接、成為可執行文件的過程是把可執行

代碼聯編(或稱裝配)在一起的過程。

靜態聯編(前期聯編)

’靜態聯編要求在程序編譯時就知道調用哪個函數,就決定如何

實現某一動作。

動態聯編(后期聯編、滯后聯編)

一直要到程序運行時才能確定調用哪個函數。系統在運行時才動

態完成的聯編。

靜態聯編支持的多態性稱為編譯時多態性,也稱靜態多態性。在

C++中,編譯時多態性是通過函數重載和運算符重載實現的。

動態聯編支持的多態性稱為運行時多態性,也稱動態多態性。在

C++中,運行

時多態性是通過繼承和虛函數來實現的。2

5.2函數重載I<

質譯時多本件可以通過函數重載來實現。函數重載的意義在于它能

Voidmain()

{pointp(20,20);

circlec(8,8,30);

cout?p.area()?endl;〃執彳亍基類point中的area()的函數

cout?c.area()?endl;〃執彳亍派生類circle中的areaO函數

coutvvc.point::area()vvendl;〃執彳亍基類point中的areaO的函數

}

程序運行結果為:

0

2827.439941

0

classcircle:publicpoint(

intradius;

public:

circle(intx,inty,intrad):point(x,y){radius=rad;}

floatarea(){return34416*radius*radius派生類中Marea函數

0?3

說明:<

在基類和派生類中的函數重載有兩種情況:

1.參數有所差別的重載,這種重載函數的定義和調用方法在前面章

節已進行了介紹;在編譯時根據參數不同來區分函數重載。

2.函數所帶的參數完全相同,只是它們屬于不同的類。這時要人工

干預,以下兩種方法可以使編譯時區別這類函數:

(1)使用對象名加以區分。例如:p.area()和c.erea()分別

調用類point的area()函數和類circle的area()函數。

(2)使用“類名::''加以區分。例如:point::area()和

circle::area()分別調用類point和類circle的area()函數。

4

5.3運算符重載<

在C++中,除了可以對函數重載外,還可以對大多數運算

符實施重載。

運算符重載通過創建運算符函數operator()來實現。

運算符函數定義了重載的運算符將要進行的新的操作,

這種操作通常作用在一個類上。

函數operatorO可以

(1)外部函數

(2)類的友元函數

(3)是類的成員函數

5

5.3.1類以外的運算符重載<

對基本的數據類型,C++提供了許多預定義的運算符,如“+”、”-

工“二,,等,若有一個復數類complex:

classcomplex{

public:

doublereal,imag;

complex(doubler=0,doublei=0){real=r;imag=i;}

};

若要把類complex的兩個對象com1和com2加在一起,下面的語句

是不能實現的:

voidmain()

(

complexcom1(1.1,2.2),com2(3.3,4.4),total;

total=com1+com2;〃錯誤

〃???

}

錯誤原因是類complex的類型不是基本數據類型,而是用戶自定義

的數據類型。C++還是無法直接將兩個complex類對象相加。

類外部的運算符函數外

為了表達上的方便,人們希望預定義的內部運算符(如“+”、

“*”、等)在特定類的對象上以新的含義進行解釋,如希

望能夠實現tota仁com1+com2,這就需要用重載運算符來解決。

C++為運算符重載提供了一種方法,即在進行運算符重載時,

必須寫一個運算符函數,其名字為operator后隨一個要重載的運

算符。例如,要重載號,應該寫一個名字為operator+的

函數。

---表5.1

函數功△*匕

operator+()法

operator-()

operator*()

operator/()

operator<()

7

運算符函數operator+()

在編譯時遇到名為operator@的運算符函數(@表示所要重

算符),就檢查傳遞給函數的參數的類型。如果編譯器在一個運

算符的兩邊看到自定義的數據類型,就執行用戶自己的函數,而

不是常規運算符。

若要將上述類complex的兩個對象相加,只要寫一個運算符函數

operator+O

complexoperator+(complexom1,complexom2)

(

complextemp;

temp.real=om1.real+om2.real;

temp.imag=om1.imag+om2.imag;

returntemp;

}

我們就能方便的使用語句total=com1+com2;

將類complex的兩個對象com1和com2相力口。

也可以使用以下的調用語句,將兩個complex類對象相加:

total=operator+(com1,com2);

這兩個調用語句是等價的,但顯然后者不如前者簡明和方便。

例5.2運算符函數operator+()將兩個compIex類對象相加程序

#include<iostream.h>

classcomplex{

public:

doublereal;

doubleimaa:

Voidmain()

{complexcom1(1.1,2.2),com2(3.3,4.4),total1,total2;

total1=operator+(com1,com2);〃調用運算符函數operator+O

的第一種方式

cout?"real1="?total1.reaK<5

,,?"imag1="?total1.imag?endl;

total2=com1+com2;〃調用運算符函數operator+()的第二種方

coutvv"real2="vvtotal2,realvv”

,,?"imag2="?total2.imag?endl;

)

程序運行結果為:

real1=4.4imag1=6.6

reaI2=4.4imag2=6.6

綜合說明:<

(1)運算符重載函數operator@O可以返回任何類型,甚至可以是void類型

但通常返回類型與它所操作的類的類型相同,這樣可以使重載運算符用在復

雜的表達式中。

(2)在重載運算符時,運算符函數所作的操作不一定要保持C++中該運算符原

有的含義。例如,可以把加運算符重載成減操作,但這樣容易造成混亂。所

以保持原含義,容易被接受,也符合人們的習慣。

(3)在C++中,用戶不能定義新的運算符,只能從C++已有的運算符中選擇一

個恰當的運算符重載。

(4)C++編譯器根據參數的個數和類型來決定調用哪個重載函數。因此,可以

為同一個運算符定義幾個運算符重載函數來進行不同的操作

(5)重載運算符與預定義運算符的使用方法完全相同,它不能改變原有運算符

的參數個數(單目或雙目),也不能改變原有的優先級和結合性。

(6)在C++中,大多數系統預定義的運算符都能被重載,例如

*

+J.,*1/0%/A&?I1

■1=<>+=?*=

/=%=A=&=1=???=

?===1■=<=>=&&II++

■■[]0newdelete

也有一些運算符是不能被重載的,如:

*..Q>

前處如符號癡##也不露重載。

5.3.2友元運算符函數I<

把運算符函數定義成某個類的友元函數,稱為友元運算符

函數。

1.友元運算符函數定義的語法形式

友元運算符函數在類的內部聲明格式如下:

classX{

//.....

friend返回類型operator運算符(參數表);

//.....

);

11

在類外,友元運算符函數與定義一般的友元函數相似,其

格式如下:

返回類型operator運算符(參數表)

〃函數體

}

由于友元函數不是類的成員函數,所以不需要綴上類名。

與成員運算符函數不同,友元運算符函數是不屬于任何

類對象的,它沒有this指針。若重載的是雙目運算符,

則參數表中有兩個操作數;若重載的是單目運算符,則

參數表中只有一個操作數。

12

2.雙目運算符重載

當用友元函數重載雙目運算符時,兩個操作數都要傳給運

算符函數。

例5.3友元函數重載雙目運算符

數a+bi和c+di進行加、減、乘、除的方法如下:

(a+bi)+(c+di)=(a+c)+(b+d)i

法(a+bi)-(c+di)=(a-c)+(b-d)i

除(a+bi)*(c+di)=(ac-bd)+(ad+bc)i

(a+bi)/(c+di)=((a+bi)*(c-di))/(c*c+d*d)

13

#include<iostream.h>

classcomplex{

private:

doublereal;

doubleimag;

public:

complex(doubler=0.0,double1=0.0){real=r;imag=i;}

voidprint();

friendcomplexoperator+(complexa,complexb);

//相友元運算符函數重載復數

friendcomplexoperator-(complexa,complexb);

〃用友元運算符函數重載魚數

friendcomplexoperator*(complexa,complexb);

〃用友元運算符函數重載復數

friendcomplexoperator/(complexa,complexb);

〃用友元是算符函藪重載復數

);

14

接1例5.3

complexoperator+(complexa,complexb)〃重載“十”定義

{complextemp;

temp.real=a.real+b.real;temp.imag=a.imag+b.imag;

returntemp;

}

complexoperator-(complexa,complexb)〃重載定義

{complextemp;

temp.real=a.real-b.real;temp.imag=a.imag-b.imag;

returntemp;

)

complexoperator(complexa,complexb)〃重載定義

{complextemp;

temp.real=a.rearb.real-a.imag*b.imag;

temp.imag=a.rearb.imag+a.imag*b.real;

returntemp;

)15

接2例5.5

complexoperator/(complexa,complexb)〃重載定義

{complextemp;

doublet;

t=1/(b.rearb.real+b.imag*b.imag);

temp.real=(a.rearb.real+a.imag*b.imag)*t;

temp.imag=(b.reara.imag-a.real*b.imag)*t;

returntemp;

}

voidcomplex::print()〃輸出顯示復數

(

cout?real;

if(imag>0)cout?u+,5;

if(imag!=0)cout?imag?^^i\n^^

}16

voidmain()

{complexAl(2,3,4.6),A2(3,6,2.8),

A3,A4,A5,A6;〃定義6個復數對象

A3=A1+A2;〃復數相加

A4=A1-A2;〃復數相減

A5=A1*a2;〃復數相乘

A6=A1/A2;〃復數相除

A1.print();〃輸出復數A1

A2.print();〃輸出復數A2

A3.print();〃輸出復數相加結果A3

A4.print();〃輸出復數相減結果A4

A5.print();〃輸出復數相乘結果A5

A6.print();〃輸出復數相除結果A6

17

程序運行結果如下:

2.3+4.6i

3.6+2.8i

5.9+7.4i

13+18

-4.6+23i

1.017308+0.486538i

調用時:

aa@bb;//隱式調用

opreator@(aa,bb)〃顯式調用

18

3.單目運算符重載<

用友元函數重載單目運算符時,需要一個顯式的操作數。

例5.4用友元函數重載單目運算符“-”例.子。,

#include<iostream.h>voidmian()

classAB{{ABob1(50,60),ob2;

inta,b;ob1.show();

public:ob2="ob1;

AB(intx=0,inty=0){a=x;b=y;}ob2.show();

friendABoperator-(ABobj);〃聲}、一、_

voidshow();

);

ABoperator-(ABob|)〃定義重載單目運算符

{obj.a=-obj.a;obj.b=-obj.b;returnobj;}

voidAB::show()

{cout?ua,,,,?au<,,=,,?b?end}

程序運行結果如下:

a=50b=60

3=-50b=-6019

仗m漢/L四姒里秋丁丁.,-------心q十口^心舁1可,HJ日匕云可以心口IT

喻5.5

ttinclude<iostream.h>

classcoord{

intx,y;

public:

coord(inti=0,intj=0){x=i;y=j;}

voidprint(){cout<<〃x:〃〈〈x〈〈〃,y:〃〈〈y〈〈endl;}

friendcoordoperator++(coordop);

);

coordoperator++(coordop)

{++op.X;

++op.y;

returnop;

20

voidmain()

{coordob(10,20);

ob.print();

operator++(ob);//顯式調用

ob.print();

++ob;//隱式調用

ob.print();

)

運行結果:

x:10,y:20

x:10,y:20

x:10,y:20

這個運行結果沒有達“++”的目的,由于友元函數沒有this指針,

所以不能引用this指針所指的對象。這個函數是通過傳值的方法

傳遞參數的,函數體內對。P的所以修改都無法傳到函數體外。因

止匕在operator++函數中,任何內容的改變不會影響產生調用的

操作數,也就是說。實際上對象x和y并未增加。21

為了解決以上問題,使用友元函數重載單目運算符“++”匚力

“一”時,應采用引用參數傳遞操作數(&),這樣函數參數的任何改

變都影響產生調用的操作數,從而保持了兩個運算符的原意。

例5.6使用友元運算符函數重寫例5.5的程序。

#include<iostream.h>

classcoord{

intx,y;

public:

coord(inti=O,intj=0){x=i;y=j;}

voidprint(){cout?ux:"?x?u,y:^?y?endl;}

friendcoordoperator++(corrd&op);

〃聲明友元運算符函數,形參為引用

};

coordoperator++(coord&op)〃定義友元運算符函數

{++op.x;

++op.y;

returnop;

}22

voidmain()

{coordob(10,20);

ob.print();

++ob;〃隱式調用友元運算符函數

ob.print();

operator++(ob);〃顯式調用友元運算符函數

ob.print();

)

程序運行的結果如下:

x:10,y:20

x:11,y:21

x:12,y:22

23

當用友元函數重載單目運算符時,參數表中有一個操作數

一般而言,采用友元函數重載單目運算符@后,可以采用以下

兩種方法來使用:

@aa;〃隱式調用

operator@(aa);〃顯式調用

綜合說明:

(1)不能用友元函數重載的運算符是:=,(),[],?>其余的

運算符都可以使用友元函數來實現重載。

(2)由于單目運算符“?”可不改變操作數自身的值,所以在例5.4

載單目運算符的友元運算符函數的原型可寫成:

friendABoperator-(ABobj);

通過傳值的方式傳遞參數。

24

5.3.3成員運算符函數<

運算符函數可以定義為類的成員(稱為成員運算符函數)

1.成員運算符函數定義的語法形式

成員運算符函數的原型在類的內部聲明格式為:

classX{

//...

返回類型operator運算符(形參表);

//...

};

類外成員運算符函數定義的更一般形式為:

返回類型X:operator運算符(形參表)

//函數體

}

在成員運算符函數的參數表中,若運算符是單目的,則參

數表為空;若運算符是雙目的,則參數表中有一個操作

數。

2.雙目運算符重載<

對雙目運算符而言,成員運算符函數的參數表中僅有一個

參數,它們為運算符的右操作數,此時當前對象作為運

算符的左操作數,它們通過this指針隱含地傳遞給函數

的。例如:

classX{

〃…

intoperator+(Xa);

);

在類X中聲明了重載“十”的成員運算符函數,返回類型為

int,它具有兩個操作數,一個是當前對象,另一個是

對象a。

例5.7用雙目運算符函數進行復數運算的例子

26

#include<iostream.h>

classcomplex{

private:

doublereal;〃復數的實數部分

doubleimag;〃復數的虛數部分

public:

complex(doubler=0.0,doublei=0.0);〃構造函數

voidprint();〃顯示輸出復數

complexoperator+(complexc);〃重載復數運算

complexoperator-(complexc);〃重載復數“一”運算

complexoperator*(complexc);〃重載復數“釬運算

complexoperator/(complexc);〃重載復數運算

);27

complex::complex(doubler,doublei)〃定義構造函數

接1例5.7

complexcomplex::operator+(complexc)〃重載定義

complextemp;

temp.real=real+c.real;

temp.imag=imag+c.imag;

returntemp;

)

complexcomplex::operator-(complexc)〃重載定義

(

complextemp;

temp.real=real-c.real;

temp.imag=imag-c.imag;

returntemp;

)

28

接2例5.7

complexcomplex::operator*(complexc)〃重載“*F

(

complextemp;

temp.real=real*c.real-imag*c.imag;

temp.imag=imag*c.real+rearc.imag;

returntemp;

)

complexcomplex::operator/(complexc)〃重載定義

(

complextemp;

doublet;

t=1/(c.rearc.real+c.imag*c.imag);

temp.real=(rearc.real+imag*c.imag)*t;

temp.imag=(c.rearimag-rearc.imag)*t;

returntemp;

)29

接3例5.7

voidcomplex::print()〃顯示復數的實數部分和虛g

{cout?real;

if(imag>0)cout?,,+";

if(imag!=0)cout?imag?"i\n";

)

voidmain()

{complexA1(2.3,4.6),A2(3.6,2.8),A3,A4,A5,A6;//定義六個復

數類對象

A3=A1+A2;〃復數相加

A4=A1-A2;〃復數相減程序運行結果如下:

A5=A1*A2;〃復數相乘2.3+4.6i

A6=A1/A2;〃復數相除3.6+2.8i

A1.print();〃輸出復數A15.9+7.4i

A2.print();〃輸出復數A2-1.3+1.8i

A3.print();〃輸出復數相加的結身

-4.6+23i

A4.print();〃輸出復數相減的結身1017308+0486?8i

A5.print();〃輸出復數相乘的結果!-017308+0.4865381

A6.print();〃輸出復數相除的結果A6

30

}

在主函數main()中的語句|.

A3=A1+A2;一—

A4=A1-A2;

A5=A1*A2;

A6=A1/A2;

所用的運算符“+”、"-”、“*"、“/”是重載后的運算符。程序

執行到這四條語句時C++將其解釋為:

A3=A1.operator+(A2);

A4=A1.operator-(A2);

A5=A1.operator*(A2);

A6=A1.operator/(A2);

由此我們可以看出,成員運算符函數operator@實際上是由雙目

運算符左邊的對象A1調用的,盡管雙目運算符函數的參數表只

有一個操作數A2,但另一個操作數是由對象A1通過this指針隱

含地傳遞的。以上兩組語句的執行結果是完全相同的。一般而言,

采用成員函數重載雙目運算符@后,可以用以下兩種方法來使用:

aa@bb;〃隱式調用

aa.operator@(bb);〃顯式調用

成員運算符函數operator@所需的一個操作數由對象aa通過this

指針隱含地傳遞。因此,它的參數表中只有一個操作數bbB

3.單目運算符重載<

對單目運算符而言,成員運算符函數的參數表中沒有參數,此時當

前對象作為運算符的一個操作數。

例5.8重載單目運算符“++”的例子

#include<iostream.h>

classcoord{

intx,y;

public:

coord(inti=O,intj=0);

voidprint();

coordoperator++();〃聲明成員運算符函數operator++()

);

coord::coord(inti,intj){x=i;y=j;}

voidcoord::print(){cout?"x:"<<x?",y:"?y?endl;}

coordcoord::operator++()〃定義成員運算符函數。perator++()

{++x;

++y;

return*this;

)

32

接1例5.8

voidmain()

{coordob(10,20);

ob.print();

++ob;〃隱式調用成員運算符函數operator++()

ob.print();

ob.operator++();〃顯式調用成員運算符函數operator++()

ob.print();

)

程序運行結果如下:

x:10,y:20

x:11,y:21

x;12,y=22

不例主函數main()中調用成員運算符函數operator++()采用

了兩種方法:++ob;與ob.operator++()

兩者是等價的,其執行效果是完全相同的。

從本例還可以看出,當成員函數重載單目運算符時,沒有參數被顯

式地傳遞給成員運算符函數。參數是通過this指針隱含地傳遞給

函數的。33

采用成員函數重載單目運算符時,可以用以下兩種方法來使用:

@aa;〃隱式調用

aa.operator@();〃顯式調用

成員運算符函數operator@所需的一個操作數由對象aa通過this

指針隱含地傳遞。因此,在它的參數表中沒有參數。

34

5.3.4成員運算符函數與友元運算符函數的比較

我們對成員運算符函數與友元運算符函數作一比較。

(1)對雙目運算符,成員運算符函數帶有一個參數,友元運算符函數

帶有兩個參數對單目運算符,成員運算符函數不帶參數,友元運算

符函數帶有一個參數。

(2)雙目運算符一般可以被重載為友元運算符函數或成員運算符函

數,但有一種情況,必須使用友元函數。

例如,在例5.4的類AB中,用成員運算符函數重載運算

ABAB::operator+(intx)

ABtemp;

temp.a=a+x;

temp.b=b+x;

returntemp;

35

若類AB的對象ob要做賦值運算和加法運算,以下是一條正確的語句

ob=ob+100;

由于對象ob是運算符的左操作數,所以它調用了運算符重

載函數,把一個整數加到了對象ob的某些元素上去。然而,下一條

語句就不能工作了:

ob=100+ob;

不能工作的原因是運算符的左操作數是一個整數,而整數是一個內

部數據類型,100不能產生對成員運算符函數的調用。

36

用兩種形式的友元函數來重載運算符函數“+”

就能消除由于運算符的左操作數是內部數據類型而帶來的問題,

下述程序說明了如何實現。

例5.9用兩種形式的友元函數來重載運算符函數

#include<iostream.h>

classAB{

inta,b;

public:

AB(intx=O,inty=0){a=x;b=y}

friendABopterator+(ABob,intx);

〃聲明一種友元運算符函數

friendABopterator+(intx,ABob);

〃聲明另一種友元運算符函數

voidshow();

);

37

接1例5.9

ABoperator+(ABob,intx)

〃定義常規類型在右邊的友元運算符函數

{ABtemp;

temp.a=a+x;

temp.b=b+x;

returntemp;

)

ABoperator+(intx,ABob)

〃定義常規類型在左邊的友元運算符函數

ABtemp;voidmain()

temp.a=x+ob.a;{ABob1(30,40),ob2;

temp.b=x+ob.b;

ob2=ob1+30;

returntemp;ob2.show();

)ob2=50+ob1;

voidAB::show()

,,,ob2.show();

{coutvv"a=''vvavv''u?b=<<b<<k}

(3)成員運算符函數和友元運算符函數都可以用習慣方式調—

也可以用它們專用的方式調用,表5.2列出了一般情況下運算符

函數的調用形式。

習慣形式友元運算符函數調用形式成員運算符函數調用形式

a+boperator+(a,b)a.operator+(b)

-aoperator-(a)a.operator-()

a++operator++(a,0)a.operator++(0)

(4)C++的大部分運算符既可說明為成員運算符函數,又可說明

為友元運算符函數。究竟選擇哪一種運算符函數好一些,沒有定

論,這主要取決于實際情況和程序員的習慣。

一般而言,對于雙目運算符,將它重載為一個友元運算符函數

比重載為一個成員運算符函數便于使用。若一個運算符的操作需

要修改類對象的狀態,則選擇成員運算符函數較好。如果運算符

所需的操作數(尤其是第一個操作數)希望有隱式類型轉換,則

運算符重載必須用友元函數,而不能用成員函數。39

有以下的經驗:

?對于單目運算符,建議選擇成員函數;

?對于運算符J、()、[]、-〉”只能作為成員函數;

?對于運算符“+二、一二、/二、*二、&=>!=>?二、%二、>>=>

〈〈二”,建議重載為成員函數;

?對于其他運算符,建議重載為友元函數。

40

5.3.5“++”和“--”的重載<

運算符“++”和“--”放置在變量的前面與后面,其作用是有區別

的。但是C++2.1之前的版本在重載“++”(或“--”)時,不能

顯式地區分是前綴還是后綴方式。也就是說,在例5.8的main()

函數中,以下兩條語句是完全相同的:

ob++;++ob;

在C++2.1及以后的版本中,編輯器可以通過在運算符函數參數表中

是否插入關鍵字int來區分這兩種方式。

(1)前綴方式++ob,可以用運算符函數重載為:

ob.operator++();//成員函數重載

operator++(X&ob);//友元函數重載,其中ob為X類對象

⑵后綴方式ob++,可以用運算符函數重載為:

ob.operator++(int);〃成員函數重載

operator++(X&ob,int);〃友元函數重載

調用時,參數int一般用0值。

重載運算符“--”也可用類似的方法。

41

例5.10包含“++”和“一”兩種重載運算符

#include<iostream.h>

#include<iomanip.h>

classover{

inti1,i2,i3;

public:

voidinit(int11,intI2,intI3){i1=l1;i2=l2;i3=l3;}

〃給數據成員賦初值

voidprint();{cout?ui1:"vvilvv“i2:"vvi2vv”

i3:?i3?endl;〃顯示輸出數據成員

overoperator++();〃成員函數重載“++”(前綴方式)聲

overoperator++(int);//成員函數重載“++”(后綴方式)聲

friendoveroperator--(over&);

〃友元函數重載(前綴方式)聲

42

friendoveroperator--(over&,int);

接1例5.10<

overover::operator++()〃定義成員函數重載(前綴方式)

{++i1;++i2;++i3;

return*this:

)

overover::operator++(int)〃定義成員函數重載++”(后綴方式)

{i1++;i2++;i3++;

return*this;

)

overoperator--(over&op)〃定義友元函數重載(前綴方

式)

{--op.il;--op.i2;--op.i3;

returnop;

}

overoperator--(over&op,int)//定義友元函數重載(后綴方

式)

{op.i1--;op.i2-op.i3--;43

returnop;

接2例5.10

Voidmain()

{overobj1,obj2,obj3,obj4;

nhi125V

程序運行結果如下:

i1:5i2:3i3:6

i1:3i2:6i3:10

i1:7i2:2i3:7

i1:2i2:5i3:6

i2:4i3:7

i2:7i3:11

i2:1i3:6

i2:4i3:5

以上例子中,使用成員函數分別以前綴方式和后綴方式重載了運算

符“十+”,使用友元函數分別以前綴方式和后綴方式重載了運算符

ODJi.pnnqj;oojz.pnnq);oojo.pnnq);ooj4.prinq);44

}

5.3.6賦值運算符“二”的重載<

對任一類X,如果沒有用戶自定義的賦值運算符函數,那么系統將

自動地為其生成一個默認的賦值運算符函數,例如:

X&X::operator=(constX&source)

(

〃成員間賦值

)

若obj1和obj2是類X的兩個對象,。均2已被創建,則編譯程序遇到

如下語句:

obj1=obj2;

就調用默認的賦值運算符函數,將對象。32的數據成員逐域拷貝到

對象objl中。

通常,默認的賦值運算符函數是能夠勝任工作的。但在某些特殊情

況下,如類中有指針類型時,使用默認賦值運算符函數會產生錯

誤,這個錯誤叫“指針懸掛”,這時程序員不得不自行重載賦值

運算符,請看下面的例子。

1.指針懸掛問題45

例5.11

#include<iostream.h>

#include<string.h>

classstring{

char*ptr;

public:

string(char*s)voidmain()

{ptr=newchar[strk{stringp1("chen");

strcpy(ptr,s);{stringp2("");

}p2=p1;//string類對象間賦值

~string(){deleteptr;cont?"p2:";

voidprint(){cont?|p2.print();

);}

程序運行結果如下:cout?"p1:";

p2:chenp1.print();

pl:□早□早)

NullPointassignment

46

接1例5.11

運行上述程序輸出pl時發生錯誤。原因是執行到賦值語句p2二pl時,

實際是使對象p2的指針ptr與對象pl的指針ptr指向new開辟的同

一個空間。當p2的生命周期(內層的一對花括號間)結束時,系

統自動調用析構函數將這一空間撤消。這樣,P2中的ptr原來所

指向的區域被封鎖起來,并不能再被使用,這就產生了指針懸掛

問題。這時盡管pl的指針ptr存在,其所指向的空間卻無法訪問

了,如圖所示。當pl的生命走其結束時,將再次調用析構函數,

釋放同一空間,從而導致運行錯誤。

47

動態空間1動態空間2

動態空間2

(b)執行P2二Pl之后(c)p2的生命周期結束后

48

2.顯式地定義一個賦值運算符重載函數,解決指針懸掛問題I<

例5.12顯式地定義一個賦值運算符重載函:voidmain()

存儲區。#include<iostream.h>{stringp1("chen");

#include<string.h>

classstring{{stringp2("");

char*ptr;p2=p1;

public:cout?"p2:";

string(char*)p2.print();

{ptr=newchar[strlen(s)+1];

strcpy(ptr,s);}

)cout?"p1:";

-string(){deleteptr;}p1.print();

voidprint(){count?ptr?endl;})

string&operator=(conststring&);〃聲也火隊'I且心畀里軌田

);

string&string::opertor=(conststring&s)〃定義賦值運算符重載

{if(this==&s)return*this;程序運行結果如下:

deleteptr;

ptr=newchar[strlen(s.ptr)+1];p2:chen

strcpy(ptr,s.ptr);pl:chen

return*this;

說明:<

⑴賦值運算符“二”只能重載為成員函數,而不能把它重載為友元

函數,因為若把上述賦值運算符“二”重載為友元函數:

friendstring&operator=(string&p2,string&p1)

這時,表達式:p1="chen^^

將被解釋為:operator=(p1Jchen")這顯然是沒有什么問題的,

但對于表達式"chen"=p1

將被解釋為:operator=("cherT,p1)

即C++編譯器首先將“chen”轉換成一個隱藏的string對象,然后

使用對象p2引用該隱藏對象,編譯器不認為這個表達式是錯誤

的,從而將導致賦值語句上的混亂。因此雙目賦值運算符應重載

為成員函數的形式,而不能重載為友元函數的形式。

(2)類的賦值運算符“二”可以被重載,但重載了的運算符函數

operator=()不能被繼承。

50

537下標運算符“[廠的重載二

C++中運算符“口”可作數組下標,下標運算符的一般使用格式為:

〈基本表達式〉[〈表達式〉]

我們可以對運算符“[廠進行重載,使它起別的作用。

在重載“[『時,C++也把它看成雙目運算符,例如X[Y]可看成

[]雙目運算符

X左操作數

Y右操作數

設X是某個類的對象,類中定義了重載下標運算符

operator□函數。

則表達式X[5];

可被解釋為:X.operator[](5);

對運算符“口”重載定義只能使用成員函數,其形式如下:

返回類型類名::operator[](形參)

(

II函數體

}注意:形參只能有一個。51

重載運算符“[廠來處理數組成員的例子■<

例5.13

#include<iostream.h>

#include<stdlib.h>

classVector4{

private:

intv[4];

public:

Vector4(inta1,inta2,inta3,inta4)

{v[0]=a1;v[1]=a2;v[2]=a3;v[3]=a4;}

);

voidmain()

(

Vector4v(1,2,3,4);

cout?v[2];〃出錯,不能訪問私有成員。

)

52

即使把intv[4];改為公有成員也出錯。要用v.v[2]

我們可用重載運算符“[『的方法,方便的引用數組成員。

例5.14

#include<iostream.h>

#include<stdlib.h>

classVector4{

private:

intv[4];

public:

Vector4(inta1,inta2,inta3,inta4)

{v[0]=a1;v[1]=a2;v[2]=a3;v[3]=a4;}

int&operator[](intbi)

{if(bi<0||bi>=4){

coutvv”數疝下標出界!\n”;

exit(1);

)

returnv[bi];

}

);53

接1例5.14<

voidmain()

(

Vector4v(1,2,3,4);

cout?v[2]?endl;

v[3]=v[2];

cout?v[3]?endl;

v[2]=22;

cout?v[2]?endl;

)

?在上述程序中,并沒有創建Vector4的對象數組。程序中的下標

被重載,以便在使用Vector4的對象內的數組成員時,用一種特

殊的方式工作。下標總是返回和下標對應的對象內數組成員的那

個元素

?重載下標運算符”『時,返回一個int的引用,可使重載的“[廠

用的賦值語句的左邊,因為在main。中,可對某個數組成員元

素v口賦值。這樣,雖然v□是私有的,main()還是能夠直接對其

賦值,而不需要使用函數init()54

?在上述程序中,設有對下標的檢驗,以確保被賦值的數組/L4」

存在,當程序中一旦出現向超出所定義的數組下標范圍的數組元素

的賦值,便自動終止程序,以免造成不必要的破壞。

■與函數調用運算符“()”一樣,下標運算符“[『不能用友元函數

重載,只能采用成員函數重載。

下面再舉一個很有意思的例子:

例如,可以對string數據類型定義以下的”丁運算符重載函數:

classstring{

char*ptr;

II....................

public:

string(constchar*);

char&operator[](int);

II....................

);

55

其中重載“[]”的operator函數定義為:

char&string::opertor[](intI){returnptr[l];}

由于5什M9::(^6「210「[]()的返回值在這里是一個(^2「&,所以函數

可以被用于一個賦值運算符的任一邊,例如:

stringop="abed";

op[1]=op[3];

相當與:op.operator[](1)=op.operator1](3);

因而op的薪值為“aded”

56

5.3.8函數調用運算符“()”的重載

C++中運算符“()”可作函數調用,函數調用的一般使用格式為:

〈函數名〉(〈實參表〉)

我們可以對運算符“()”進行重載,使它起別的作用。

在重載“()”時,C++也把它看成雙目運算符,例如X(Y)可看成

()雙目運算符

X左操作數

Y右操作數

設X是某個類的對象,類中定義了重載下標運算符“()”:

operator()函數。

則表達式X(5);

可被解釋為:X.operator()(5);

對運算符“口”重載定義只能使用成員函數,其形式如下:

返回類型類名::operator()(形參表)

(

II函數體

}注意:形參只能有一個。L

例5.15

#include<iostream.h>

classMatrix{

int*m;

introw,col;

public:

Matrix(int,int);

int&operator()(int,int);〃聲明重載運算符“()”函

);

Matrix::Matrix(introw,intcol)〃構造函數

{this->row=row;

this->col=col;

m=newint[row*col];

for(inti=0;i<row*col;i++

*(m+i)=i;

)

int&Matrix::operator()(intr,intc)〃定義重載運算符“()”函

(

return(*(m+r*col+c));〃取第r行第c列的元素

}

voidmain()

{

MatrixaM(10,10);〃生成對象aM

cout?aM(3,4);

〃重載運算符“()”的調用,可解釋為

aM.perator()(3,4)

aM(3,4)=35;

〃重載運算符“()”的調用,由于引用,可放左邊

cout?endl?aM(3,4)?endl;

}N

運行結果:

Q4

再如:對運算符“O”進行重載,使它起別的作甫出一

我們知道,雙目運算符是由左邊的對象產生對operator函數調用的。

This指針總是指向產生調用的對象。重載運算符“()”的

operator函數可以帶任何類型的操作數,返回任何有效的類型。

下面的程序將幫助我們理解函數謖用運算符“()”的重載。

設obj是類myclass的對象,而且類myclass中定義了重載函數調

用運算符“()”,它起的作用是能把對象。bj的數據擴大k倍

和m倍。

對應的運算符函數為myclassoperator()(intk,intm)

則函數調用(把對象。bj的數據擴大10倍和100倍)

obj(10,100);

可被解釋為:obj.operator()(10,100);

60

例5.16運算符“()的重載。—

#include<iostream.h>

classmyclass{

inti;

floatj;

public:

myclass(intx=0,floaty=0)

{仁x;j=y;}

myclassoperator()(intk,floatm);

〃聲明重載運算符“()”函數

voiddisplay(){00^?1?,5"?j?"\n";}

};

myclassmyclass::operator()(intk,floatm)

〃定義運算符“()”函數

1=1*

j=j*m;〃把myclass的對象中的i放大k倍,j放大m倍。

return*this;

}

61

voidmain()

{myclassobj1(10,11.5);

obj1.display();

obj1(10,100);〃重載運算符“O”的調用,可解

II#^jobj1.perator()(10,.100)

obj1.display();

)

程序運行結果為:

1011.5

1001150.0

說明:函數調用運算符“(

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論