面向?qū)ο蟪绦蛟O(shè)計-第七章_第1頁
面向?qū)ο蟪绦蛟O(shè)計-第七章_第2頁
面向?qū)ο蟪绦蛟O(shè)計-第七章_第3頁
面向?qū)ο蟪绦蛟O(shè)計-第七章_第4頁
面向?qū)ο蟪绦蛟O(shè)計-第七章_第5頁
已閱讀5頁,還剩91頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

第七章多態(tài)性(polymorphism)7.1、編譯時的多態(tài)性與運行時的多態(tài)性7.2、多態(tài)的思考方式7.3、函數(shù)重載7.4、運算符重載7.5、虛函數(shù)7.1、編譯時的多態(tài)性與運行時的多態(tài)性1、多態(tài)性的概念多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計的關(guān)鍵技術(shù)之一,若程序設(shè)計語言不支持多態(tài)性,不能稱為面向?qū)ο蟮恼Z言多態(tài)性就是不同(成員)函數(shù)使用同一個函數(shù)名,即同樣的接口實現(xiàn)不同的操作擴展:多態(tài)性指不同類的對象對同一消息作出不同響應(yīng)有繼承關(guān)系運算符重載7.1、編譯時的多態(tài)性與運行時的多態(tài)性2、聯(lián)編多態(tài)性的實現(xiàn)與聯(lián)編這一個概念有關(guān)一個源程序經(jīng)過編譯,連接,成為可執(zhí)行文件的過程是把可執(zhí)行代碼聯(lián)編(或稱裝配)在一起的過程

也就是一個程序自身彼此關(guān)聯(lián)的過程7.1、編譯時的多態(tài)性與運行時的多態(tài)性舉例#include<iostream.h>

classPoint

{

public:

Point(doublei,doublej){x=i;y=j;}

doubleArea()const{return0.0;}

private:

doublex,y;

};

classRectangle:publicPoint

{

public:

Rectangle(doublei,doublej,doublek,doublel);

doubleArea()const{returnw*h;}

private:

doublew,h;

};7.1、編譯時的多態(tài)性與運行時的多態(tài)性Rectangle::Rectangle(doublei,doublej,doublek,doublel)

:Point(i,j)

{

w=k;h=l;

}

voidfun(Point&s)

{

cout<<s.Area()<<endl;

}

voidmain()

{

Rectanglerec(3.0,5.2,15.0,25.0);

fun(rec);

}

參數(shù)改為:

Rectangle&s上述聯(lián)編過程存在問題,沒有按我們的期望進行聯(lián)編體會聯(lián)編的含義如果Point派生:

Circle7.1、編譯時的多態(tài)性與運行時的多態(tài)性3、靜態(tài)聯(lián)編和動態(tài)聯(lián)編根據(jù)聯(lián)編所執(zhí)行的階段分為:靜態(tài)聯(lián)編:在編譯階段進行聯(lián)編,速度快、效率高,但不靈活動態(tài)聯(lián)編:在程序運行階段進行聯(lián)編,即直到程序運行時在確定調(diào)用哪個函數(shù),靈活性、可維護性在C++中,編譯時的多態(tài)性是通過函數(shù)重載和運算符重載實現(xiàn)的,運行時的多態(tài)性是通過繼承和虛函數(shù)來實現(xiàn)的7.2、多態(tài)的思考方式多態(tài)性允許程序員通過向一個對象發(fā)送消息來完成一系列動作,無需涉及軟件系統(tǒng)如何實現(xiàn)這些動作Area()Area()Rectangle對象Point對象發(fā)送消息(調(diào)用)7.2、多態(tài)的思考方式舉例實現(xiàn)一個計算機繪圖的程序:

假設(shè)有一個Cricle類,一個Square類。每個類都是一個Shape,并且都有一個成員函數(shù)Draw(),該函數(shù)負責(zé)將不同的圖形顯示在屏幕上7.2、多態(tài)的思考方式程序的偽代碼如下:

#include<iostream.h>

enum

ShapeType{SHAPE,CIRCLE,SQUARE};//將圖形形狀定

//義為枚舉類型

classShape

{

public:

Shape()

{

type=SHAPE;

}

ShapeTypetype;

voiddraw(){}//Shape不是具體的圖形,draw()什么也不做

};7.2、多態(tài)的思考方式classCircle:publicShape//定義Circle類

{

public:

Circle()

{

type=CIRCLE;//覆蓋剛賦值的type

}

voiddraw()

{

cout<<“drawcircle”<<endl;//……完成Circle圖形的繪制

}

};

7.2、多態(tài)的思考方式classSquare:publicShape//定義Square類

{

public:

Square()

{

type=SQUARE

}

voiddraw()

{

cout<<"drawSquare"<<endl;//完成Square的繪制工作

}

//….

};

7.2、多態(tài)的思考方式voiddrawShape(Shape&s)//圖形繪制函數(shù)

{

//…

switch(s.type)//判斷對象的type,決定調(diào)用哪個draw()函數(shù)

{

caseCIRCLE:

s.Circle::draw();break;

caseSQUARE

s.Square::draw();break;

//…

}

}

7.2、多態(tài)的思考方式分析在一個繪圖程序中,類似的圖形成員不止兩個,都要進行類似的處理。而且還可能會增加新的圖形類,例如要增加一個Traingle類,我們需要對drawType函數(shù)進行修改,使它能夠支持新增加的類型,這樣應(yīng)用程序的維護量很大,面向?qū)ο蟮膬?yōu)越性被遏制,又回到面向過程程序設(shè)計的老路上去了。我們可以使用多態(tài)機制來必免使用上述的代碼。7.3、函數(shù)重載函數(shù)重載:用同一個名字訪問一組相關(guān)的函數(shù)。也就是說,能使用戶為某一類操作取一個通用的名字,而由編譯程序來選擇具體由哪個函數(shù)來執(zhí)行;意義:因而有助于解決程序的復(fù)雜性問題。7.3、函數(shù)重載#include<iostream.h>

voidprint(intn)//定義函數(shù)print

{cout<<n<<endl;}

voidprint(constchar*str)//重載函數(shù)print

{cout<<str<<endl;}

intmain()

{

print(4);//調(diào)用voidprint(intn)

print("helloworld");//調(diào)用voidprint(constchar*)

return0;

}7.3、函數(shù)重載對于重載函數(shù),編譯程序能夠通過參數(shù)選擇具體由哪個函數(shù)來執(zhí)行。在類中,普通成員函數(shù)和構(gòu)造函數(shù)都可以重載,特別是構(gòu)造函數(shù)的重載(它提供了多種初始化方式)給用戶以更大的靈活性在基類和派生類中的函數(shù)重載有兩種情況:一種是函數(shù)參數(shù)有所差別的重載。另一種是函數(shù)所帶的參數(shù)完全相同,只是它們屬于不同的類??梢允褂脤ο竺右詤^(qū)分和使用“類名::”加以區(qū)分7.4、運算符重載1、運算符重載的基本概念2、成員運算符重載3、友元運算符重載4、值返回和引用返回5、增量運算符的重載6、轉(zhuǎn)換運算符重載7、賦值運算符重載7.4、運算符重載1、運算符重載的基本概念定義:賦予已有的運算符多重含義。

C++中通過重新定義運算符,使它能夠用于特定類的對象執(zhí)行特定的功能。這樣就增強了C++語言的擴充能力用途:運算符重載所定義的操作通常作用在一個類上,來執(zhí)行對這個對象的運算目的:使C++代碼更直觀,易讀。

例如:4+5就比add(4,5)更加直觀7.4、運算符重載語法:

類型[類名::]operator重載的運算符(參數(shù))

函數(shù)相關(guān)操作

[]表示可選的;在運算符重載時如果有類名,就是成員運算符函數(shù),否則就是友元運算符函數(shù)。7.4、運算符重載#include<iostream.h>

classInteger

{

public:

Integer(inti=0)

{

val=i;

}

Integeroperator+(constInteger&a)//重載運算符+

{

returnInteger(val+a.val);

}

voidprint()const

{

cout<<“value=”<<val<<endl;

}

private:

intval;

};生成、返回匿名對象7.4、運算符重載voidmain()

{

Integera(5),b(3),c;

cout<<"objecta";

a.print();

cout<<"objectb";

b.print();

c=a+b;

cout<<"objectc";

c.print();

}

程序運行結(jié)果:objectavalue=5objectbvalue=3objectcvalue=8相當(dāng)于調(diào)用a的成員函數(shù)+因為此處的+是單目運算符7.4、運算符重載分析:

上例中為Integer重載了運算符“+”,使“+”能夠使用于Integer類的對象。運算符重載是通過編寫函數(shù)定義實現(xiàn)的。函數(shù)定義包括函數(shù)首部和函數(shù)體,但是函數(shù)名是由關(guān)鍵字operator和其后的要重載的運算符號組成7.4、運算符重載在C++中:幾乎所有的運算符都可用作重載,包括:

算術(shù)運算符:+,-,*,/,%,++,――

位操作運算符:&,|,~,^,<<,>>

邏輯運算符:!,&&,||

比較運算符:>,<,>=,<=,==,!=

賦值運算符:=,+=,-=,*=,/=,%=,&=,|=,<<=,>>=

其他運算符:[],(),->,new,delete,new[],delete[],->*,甚至int等注意:1、運算符是在C++系統(tǒng)內(nèi)部定義的,它們的運算順序,優(yōu)先級在重載過程中都不改變2、有一些運算符是不能重載的,例如:

.,.*,::,?:,#,##7.4、運算符重載7.4、運算符重載2、成員運算符重載運算符重載包括兩種形式:成員函數(shù)重載和友元運

算符重載成員函數(shù)重載:將運算符重載函數(shù)定義為類中的成員函數(shù)友元運算符重載:將運算符重載函數(shù)定義為類中的友元函數(shù)7.4、運算符重載成員運算符函數(shù)聲明和定義的格式如下:

聲明的格式:

classX{

typeoperator@(參數(shù)表);

};

定義的格式:

TypeX::operator@(參數(shù)表)

{

函數(shù)體

}

其中type是函數(shù)的返回類型,@是要重載的運算符也可以寫成內(nèi)聯(lián)形式7.4、運算符重載#include<iostream.h>

classthree_d

{

public:

intx; //x坐標(biāo)

inty; //y坐標(biāo)

intz; //z坐標(biāo)

public:

three_d(inti,intj,intk); //構(gòu)造函數(shù)

three_d(); //默認構(gòu)造函數(shù)

three_doperator+(three_dc);//成員方式重載+運算符

voidshow(); //顯示結(jié)果

};

7.4、運算符重載three_d::three_d(inti,intj,intk)//構(gòu)造函數(shù)

{

x=i; //給x賦值

y=j; //給y賦值

z=k; //給z賦值

}

three_d::three_d()//默認構(gòu)造函數(shù)

{

x=0; //初始化x

y=0; //初始化y

z=0; //初始化z

}

7.4、運算符重載//重載+三維坐標(biāo)的相加使用成員運算符重載

three_dthree_d::operator+(three_dc)

{

three_dtmp; //定義臨時變量

tmp.x=x+c.x; //取得x坐標(biāo)

tmp.y=y+c.y; //取得y坐標(biāo)

tmp.z=z+c.z; //取得z坐標(biāo)

returntmp; //返回

}

//顯示坐標(biāo)

voidthree_d::show()

{

cout<<“x=”<<x<<“y=”<<y<<“z=”<<z<<endl;

//顯示x,y,z坐標(biāo)

}如何實現(xiàn)局部對象的返回?7.4、運算符重載//主函數(shù)

intmain()

{

three_dob1,ob2(10,11,12),ob3(11,21,31);//定義3個對象

cout<<"-------ob1,ob2.ob3-------\n";

ob1.show(); //顯示第一個對象

ob2.show(); //顯示第二個對象

ob3.show(); //顯示第三個對象

ob1=ob2+ob3; //利用重載+

cout<<"-----ob1=ob2+ob3,--------\n";

ob1.show(); //顯示第一個對象

return0;

}

7.4、運算符重載3、友元運算符重載運算符聲明的格式:

classX{

friendtypeoperator@(參數(shù)表);

};定義的格式:

Typeoperator@(參數(shù)表)

{

函數(shù)體

}其中type是函數(shù)的返回類型,@是要重載的運算符7.4、運算符重載成員運算符函數(shù)與友元運算符函數(shù)的區(qū)別友元運算符函數(shù):(1)不屬于任何類對象;(2)它沒有this指針;(3)若重載的是雙目運算符,則參數(shù)表中有兩個操作數(shù),如果重載的是單目運算符,則參數(shù)表中有一個操作數(shù)。成員運算符函數(shù):(1)屬于一個類對象;(2)它有this指針;(3)若重載的是雙目運算符,則參數(shù)表中有一個操作數(shù),如果重載的是單目運算符,則參數(shù)表中沒有操作數(shù)(隱含this)為什么成員運算符函數(shù)少一個參數(shù)?7.4、運算符重載#include<iostream.h>

classthree_d

//定義類

{

public:

intx;

//x坐標(biāo)

inty;

//y坐標(biāo)

intz; //z坐標(biāo)

public:

three_d(inti,intj,intk); //構(gòu)造函數(shù)

three_d(); //默認構(gòu)造函數(shù)

friendthree_doperator+(three_dc,three_dd);

//重載+使用友元

voidshow(); //顯示結(jié)果

};

7.4、運算符重載three_d::three_d(inti,intj,intk)//構(gòu)造函數(shù)

{

x=i; //給x賦值

y=j; //給y賦值

z=k; //給z賦值

}

three_d::three_d()//默認構(gòu)造函數(shù)

{

x=0; //初始化x

y=0; //初始化y

z=0; //初始化z

}

7.4、運算符重載//重載+三維坐標(biāo)的相加使用友元

three_doperator+(three_dc,three_dd)

{

three_dtmp; //定義臨時變量

tmp.x=c.x+d.x; //取得x坐標(biāo)

tmp.y=c.y+d.y; //取得y坐標(biāo)

tmp.z=c.z+d.z; //取得z坐標(biāo)

returntmp; //返回

}

//顯示坐標(biāo)

voidthree_d::show()

{

cout<<"x="<<x<<"y="<<y<<"z="<<z<<endl;

//顯示x,y,z坐標(biāo)

}

7.4、運算符重載//主函數(shù)

intmain()

{

three_dob1,ob2(10,11,12),ob3(11,21,31);//定義3個對象

cout<<"-------ob1,ob2.ob3-------\n";

ob1.show(); //顯示第一個對象

ob2.show(); //顯示第二個對象

ob3.show(); //顯示第三個對象

ob1=ob2+ob3; //利用重載+

cout<<"-----ob1=ob2+ob3,--------\n";

ob1.show(); //顯示第一個對象

return0;

}

7.4、運算符重載4、值返回和引用返回運算符重載過程中有兩種情況:

(1)返回值的類型是值類型;

(2)返回值的類型是引用;

這兩者有什么區(qū)別呢?看下面的例子:把對象的(數(shù)據(jù)成員)值返回把對象本身返回7.4、運算符重載#include<iostream.h>

classRMB{

public:

RMB(unsignedintd,unsignedintc);//構(gòu)造函數(shù)

friendRMBoperator+(RMB&,RMB&);//重載+

friendRMB&operator++(RMB&);//重載++

voiddisplay()

{

cout<<(yuan+f/100.0)<<endl;

}

protected:

unsignedintyuan;

unsignedintf;

};

能否定義為成員運算符函數(shù):RMBoperator+(RMB&,RMB&);7.4、運算符重載RMB::RMB(unsignedintd,unsignedintc)//構(gòu)造函數(shù)

{

yuan=d;

f=c;

while(f>=100)//規(guī)整人民幣單位,分不超過100

{

yuan++;

f-=100;

}

}

RMB

operator+(RMB&s1,RMB&s2)//兩個不同數(shù)目的人民幣相加

{

unsignedintf=s1.f+s2.f;

unsignedintyuan=s1.yuan+s2.yuan;

RMBresult(yuan,f);//使用構(gòu)造函數(shù)規(guī)整相加后的結(jié)果

returnresult;//返回值

}7.4、運算符重載RMB&operator++(RMB&s)//定義重載運算符++

{

s.f++;

if(s.f>=100)

{

s.f-=100;

s.yuan++;

}

returns;

}

voidmain(){//主函數(shù)

RMBd1(1,60);

RMBd2(2,50);

RMBd3(0,0);

d3=d1+d2;

++d3;

d3.display();

}程序運行結(jié)果:4.117.4、運算符重載在這個程序中:

(1)operator+()兩個對象相加,不改變其中任何一個對象。而且必須生成一個結(jié)果對象來儲存結(jié)果,所以返回的是值類型;

(2)而++運算改變自身對象的值,所以要返回該值的引用。7.4、運算符重載運算符重載的注意事項(比較成員、友元運算符函數(shù))(1)對于二元運算符,成員運算符函數(shù)帶一個參數(shù),而友元運算符函數(shù)帶兩個參數(shù);對于一元運算符,成員運算符函數(shù)不帶參數(shù),而友元運算符帶一個參數(shù)(2)C++的大部分運算符既可以聲明為成員運算符函數(shù),又可以聲明為友元運算符函數(shù),具體選擇哪一種情況,主要取決于實際情況和程序員的習(xí)慣7.4、運算符重載(3)當(dāng)運算符函數(shù)是一個成員函數(shù)時,最左邊的操作數(shù)(或者只有左邊的操作數(shù))必須是運算符類的一個對象(或者是該類的一個引用)。如果左邊的操作數(shù)必須是一個不同類的對象或者是一個內(nèi)部類的對象,該運算符函數(shù)必須作為一個友元函數(shù)實現(xiàn)(4)在重載運算符()、[]、->或者任何賦值運算符時,運算符重載函數(shù)必須聲明為類的一個成員(5)編譯程序?qū)\算符重載選擇,遵循著函數(shù)重載的選擇。當(dāng)遇到不很明顯的運算符時,編譯程序?qū)⑷ふ覅?shù)匹配的運算符函數(shù)系統(tǒng)已定義的類7.4、運算符重載(6)重新定義運算符,不改變原運算符的優(yōu)先級和結(jié)合性。并且運算符重載后,也不改變運算符的語法結(jié)構(gòu),即單目運算符只能重載為單目運算符,雙目運算符只能重載為雙目運算符(7)不可臆造新的運算符。必須把重載運算符限制在C++語言中已經(jīng)有的運算符范圍允許重載的運算符中(8)重載運算符的含義必須清楚;重載運算符不能有二義性7.4、運算符重載5、增量運算符的重載(減量運算法類似)#include<iostream.h>

classInteger{//Integer類聲明

public:

Integer(intx)

{

value=x;

}

Integer&operator++();//前置增量運算符

Integeroperator++(int);//后置增量運算符

friend

ostream&operator<<(ostream&,constInteger&);//重載<<運算符

private:intvalue;

};對對象進行++內(nèi)部類新功能:輸出對象雙目運算符7.4、運算符重載Integer&Integer::operator++()//定義前置增量運算符重載函數(shù)

{

value++;//先增量

return*this;//返回原有對象

}

IntegerInteger::operator++(int)//定義后置增量運算符重載函數(shù)

{

Integertemp(value);//臨時對象保存原有對象值

value++;//原有對象增量修改

returntemp;

}

有this指針I(yè)nt

aaa7.4、運算符重載ostream&operator<<(ostream&output,constInteger&a)//定義<<運算符

{

output<<"Thevalueis"<<a.value;

returnoutput;

}

intmain()

{

Integera(20);

cout<<a<<endl;

cout<<(a++)<<endl;

cout<<a<<endl;

cout<<(++a)<<endl;

return0;

}Cout<<“Thevalueis”<<a.value;程序輸出結(jié)果:Thevalueis20Thevalueis20Thevalueis21Thevalueis227.4、運算符重載在C++中,前置增量運算符和后置增量運算符的意義是不同的:

使用前置增量時,對對象進行增量修改,然后再返回該對象。所以前置增量運算符操作時,參數(shù)與返回的是同一對象。

使用后置增量時,必須在增量返回之前返

回原有的對象值。后置增量操作返回的是原有對象值,不是原有對象,原有對象已經(jīng)被增量修改。7.4、運算符重載6、轉(zhuǎn)換運算符重載賦值、計算、給函數(shù)傳值以及從函數(shù)返回值都可能會進行類型轉(zhuǎn)換對于內(nèi)部數(shù)據(jù)類型,編譯器知道如何轉(zhuǎn)換類型。程序員可以用強制類型轉(zhuǎn)換運算符實現(xiàn)內(nèi)部類型之間的轉(zhuǎn)換對于用戶自定義數(shù)據(jù)類型,可以使用以下兩種途徑實現(xiàn)類型轉(zhuǎn)換:

(1)通過構(gòu)造函數(shù)進行類型轉(zhuǎn)換

(2)通過類型轉(zhuǎn)換函數(shù)進行類型轉(zhuǎn)換7.4、運算符重載#include<iostream.h>

classInteger{//Integer

public:

Integer(inti);//類型轉(zhuǎn)換構(gòu)造函數(shù)

friendostream&operator<<(ostream&,constInteger&);//重載<<運算符

private:

intvalue;

};

通過構(gòu)造函數(shù)進行類型轉(zhuǎn)換:

實現(xiàn)整型數(shù)對象的轉(zhuǎn)換7.4、運算符重載Integer::Integer(inti)//定義類型轉(zhuǎn)換函數(shù)

{

cout<<"Typeconvertconstructor"<<endl;

value=i;

}

ostream&operator<<(ostream&output,constInteger&a)

{

output<<"Integervalue="<<a.value;

returnoutput;

}

7.4、運算符重載voidmain()

{

Integera=Integer(3);//顯式調(diào)用類型轉(zhuǎn)換構(gòu)造函數(shù)

cout<<a<<endl;

Integerb=6;//隱式調(diào)用類型轉(zhuǎn)換構(gòu)造函數(shù)

cout<<b<<endl;

}

程序運行結(jié)果:TypeconvertconstructorIntegervalue=3TypeconvertconstructorIntegervalue=67.4、運算符重載#include<iostream.h>

classInteger{//Integer類聲明

public:

Integer(inti=0);//類型轉(zhuǎn)換構(gòu)造函數(shù)

friendostream&operator<<(ostream&,constInteger&);

//重載<<運算符

operatorint();//重載類型轉(zhuǎn)換運算符

private:

intvalue;

};類型轉(zhuǎn)換函數(shù)進行類型轉(zhuǎn)換含義:它原來就是類型轉(zhuǎn)換運算符現(xiàn)在是新含義的類型轉(zhuǎn)換運算符沒有返回類型、參數(shù)7.4、運算符重載Integer::Integer(intx)

{

cout<<"Integerconstructor"<<endl;

value=x;

}

Integer::operator

int()

{

cout<<"Typechangedtoint"<<endl;

returnvalue;

}

7.4、運算符重載ostream&operator<<(ostream&output,constInteger&a)

{

output<<"Integervalue="<<a.value;

returnoutput;

}

voidmain()

{

Integera(5),b(3),c;

cout<<a<<endl;

cout<<int(a)*2<<endl;//顯式轉(zhuǎn)換

c=a+b;

//隱式轉(zhuǎn)換

cout<<c<<endl;

}7.4、運算符重載程序運行結(jié)果:

Integerconstructor

Integerconstructor

Integerconstructor

Integervalue=5

Typechangedtoint

10

Typechangedtoint

Typechangedtoint

Integerconstructor

Integervalue=8

構(gòu)造了什么對象?7.4、運算符重載類型轉(zhuǎn)換函數(shù)聲明的一般格式為:

operator類型名();

(1)沒有返回類型,因為類型名就代表了它的返回類型,故返回類型顯得多余。

(2)類型轉(zhuǎn)換運算符將對象轉(zhuǎn)換成類型名規(guī)定的類型。轉(zhuǎn)換形式就像強制類型轉(zhuǎn)換一樣。

(3)如果沒有重載類型轉(zhuǎn)換運算符,直接使用強制轉(zhuǎn)換是不行的。因為強制轉(zhuǎn)換運算符只能對基本數(shù)據(jù)類型進行操作,對自定義類型的操作沒有定義7.4、運算符重載重載類型轉(zhuǎn)換運算符的注意事項:(1)類型轉(zhuǎn)換運算符重載函數(shù)只能定義為類的成員函數(shù),不能定義為類的友元函數(shù)(2)類型轉(zhuǎn)換運算符重載函數(shù)既沒有參數(shù),也沒有返回類型(3)類型轉(zhuǎn)換運算符重載函數(shù)中必須把目的類型的數(shù)據(jù)做為函數(shù)的返回值(4)一個類可以定義多個類型的類型轉(zhuǎn)換函數(shù),C++編譯器根據(jù)操作數(shù)的類型自動選擇一個合適的類型轉(zhuǎn)函數(shù)與之匹配,在可能出現(xiàn)歧義的情況下,必須顯式地使用相應(yīng)類型的類型轉(zhuǎn)換函數(shù)進行轉(zhuǎn)換7.4、運算符重載7、賦值運算符重載一般情況,用于類的對象的運算符必須重載但是賦值運算符(=)無需重載就可以用于每一個類。在不提供重載的賦值運算符時,賦值運算符的默認行為是復(fù)制對象的數(shù)據(jù)成員,比如前例通常情況下,默認的賦值運算符操作是能勝任工作但有些應(yīng)用中,僅使用默認的賦值運算符的操作不夠,還需要根據(jù)用戶的情況對賦值運算符進行重載。

有時使用默認的賦值運算符會出現(xiàn)不能正常工作的情況,此時,程序員必須自己實現(xiàn)賦值運算符重載。7.4、運算符重載#include<iostream.h>

#include<string.h>

#include<assert.h>

classString

{

public:

String(constchar*s)

{

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

assert(ptr!=0);//確保內(nèi)存分配成功

strcpy(ptr,s);

len=strlen(s);

}

7.4、運算符重載~String()

{

delete[]ptr;

}

friendostream&operator<<(ostream&output,constString&s);//重載<<運算符

private:

char*ptr;

int

len;

};

ostream&operator<<(ostream&output,constString&s)//重載<<運算符

{

output<<s.ptr;

returnoutput;

}7.4、運算符重載voidmain()

{

Strings1("test1");

{

Strings2("test2");

s2=s1;

cout<<"s2:"<<s2<<endl;

}

cout<<"s1:"<<s1<<endl;

}

賦的什么值?7.4、運算符重載上述程序雖然能夠正確編譯,但是該程序運行時會發(fā)生指針錯誤。

因為我們沒有為String類重載賦值運算符,當(dāng)程序執(zhí)行到語句s2=s1時,使用默認的賦值運算符操作,將對象s1的數(shù)據(jù)成員逐個拷貝到對象s2中。此時s2和s1中的指針成員ptr指向同一塊內(nèi)存空間。當(dāng)s2的生存期(main()函數(shù)內(nèi)層的一對花括號間)結(jié)束時,編譯器調(diào)用析構(gòu)函數(shù)將這一內(nèi)存空間回收。此時,盡管對象s1的成員ptr存在,但其指向的空間卻無法訪問了7.4、運算符重載賦值運算符重載的注意事項:(1)類的賦值運算符"="只能定義為類的成員函數(shù),不能定義為類的友元函數(shù)(2)C++編譯器默認為每個類重載了賦值運算符"=",其默認行為是復(fù)制對象的數(shù)據(jù)成員。但有時程序員必須自己實現(xiàn)賦值運算符重載,否則會出現(xiàn)錯誤(3)重載賦值運算符"="時,賦值運算符函數(shù)的返回類型應(yīng)是類的引用,這與賦值的語義相匹配。因為C++中要求賦值表達式左邊的表達式是左值(4)賦值運算符可以被重載,但重載了的運算符函數(shù)operator

=()不能被繼承7.5、虛函數(shù)1、引入派生類后的對象指針2、虛函數(shù)定義3、虛函數(shù)和重載函數(shù)的關(guān)系4、純虛函數(shù)和抽象類動態(tài)聯(lián)編實現(xiàn)的多態(tài)不同類的對象對同一消息作出不同響應(yīng)便于程序擴充7.5、虛函數(shù)1、引入派生類后的對象指針公有派生類的對象可以作為其基類的對象處理比如:基類

Point,派生類Rectangle、Circle

函數(shù)voidfun(Point&s)但是,反過來是不行的,基類的對象不能自動成為派生類的對象總結(jié):派生類對象也是基類對象,可以實現(xiàn)一般性處理(在公有派生時)7.5、虛函數(shù)通常通過將派生類對象指針轉(zhuǎn)換為基類指針來實現(xiàn)一般性處理舉例:例7.15通俗地講:可以用基類指針指向一個派生類的對象(在公有派生時)7.5、虛函數(shù)引入派生類指針后,對象指針的注意事項:1、對于public繼承,總是可以將派生類指針賦給基類指針,因為派生類對象也是基類對象?;愔羔樦?看到"派生類對象的基類部分。編譯器進行派生類指針向基類指針的隱式轉(zhuǎn)換見上例2、反之,因為把基類指針直接賦給派生類指針蘊含著危險性,所以編譯器不允許這么做,也不執(zhí)行隱式類型轉(zhuǎn)換

即:不能用派生類指針指向基類對象7.5、虛函數(shù)3、聲明為指向基類的指針,當(dāng)它指向公有派生類對象時,只能用它來訪問派生類中從基類繼承來的成員,不能訪問派生類中定義的成員。因為編譯器認為該指針指向的是基類對象4、對于private繼承,不允許將聲明為指向基類對象的指針指向它的派生類的對象7.5、虛函數(shù)2、虛函數(shù)定義虛函數(shù)就是在基類中被關(guān)鍵字virtual說明,并在派生類中重新定義的函數(shù)在派生類中重新定義時,其函數(shù)原型,包括返回類型、函數(shù)名、參數(shù)個數(shù)與參數(shù)類型的順序,都必須與基類中的原型完全相同7.5、虛函數(shù)#include<iostream.h>

classparent{

protected:

charversion;

public:

parent()

{

version='A'; }

virtualvoidprint()//定義虛函數(shù)print()

{

cout<<"\nTheparent.version"<<version;

}

};

7.5、虛函數(shù)classderived1:publicparent{

private:

intinfo;

public:

derived1(intnumber)

{

info=number;

version='1';

}

voidprint()//重新定義虛函數(shù)print()

{

cout<<"\nThederived1info:"<<info

<<"version"<<version;

}

};省略“:parent()”7.5、虛函數(shù)intmain()

{

parentob,*op;//聲明了基類的對象和指針

op=&ob;

op->print();//通過基類指針調(diào)用基類的parent的print()

derived1d1(3);

op=&d1;

op->print();//通過基類指針調(diào)用派生類的derived1的print()——多態(tài)

return0;

}程序中語句op->print();出現(xiàn)了兩次,由于op指向的對象不同,每次出現(xiàn)都執(zhí)行了print()的不同的版本。程序運行的結(jié)果為:

Theparent.VersionA

Theparentinfo:3version17.5、虛函數(shù)虛函數(shù)定義與使用的注意事項:1、只有類的成員函數(shù)才能聲明為虛函數(shù),因為虛函數(shù)僅適用于有繼承關(guān)系的類對象,普通函數(shù)不能聲明為虛函數(shù)2、在派生類中對基類聲明的虛函數(shù)進行重定義時,關(guān)鍵字virtual可以寫也可以不寫3、不能把靜態(tài)成員函數(shù)聲明為虛函數(shù),因為靜態(tài)成員函數(shù)不屬于某個對象,而虛函數(shù)是通過對象指針或引用來調(diào)用的7.5、虛函數(shù)4、構(gòu)造函數(shù)不能是虛函數(shù),因為執(zhí)行構(gòu)造函數(shù)時,對象還沒有實例化。但析構(gòu)函數(shù)可以是虛函數(shù)5、在派生類中重定義虛函數(shù)時,函數(shù)的原型必須與基類中的函數(shù)原型完全相同6、定義了虛函數(shù)后,我們通過使用基類指針或引用指明派生類對象并使用該指針或引用來調(diào)用虛函數(shù)的方式來實現(xiàn)動態(tài)關(guān)聯(lián)。

雖然可以使用對象名和點運算符的方式來調(diào)用虛函數(shù),但是這種調(diào)用是在編譯時進行的靜態(tài)關(guān)聯(lián),它沒有充分利用虛函數(shù)的特性7.5、虛函數(shù)3、虛函數(shù)和重載函數(shù)的關(guān)系在派生類中重新定義基類的虛函數(shù)是函數(shù)重載另一種形式,但它不同于一般的函數(shù)重載(1)當(dāng)普通的函數(shù)重載時,其函數(shù)的參數(shù)或參數(shù)類型必須有所不同,函數(shù)的返回類型也可以不同;

但是,當(dāng)重載一個虛函數(shù)時,也就是說在派生類中重新定義虛函數(shù)時,要求函數(shù)名、返回類型、參數(shù)個數(shù)、參數(shù)的類型和順序與基類中的虛函數(shù)原型完全相同(2)多態(tài)的實現(xiàn)不同7.5、虛函數(shù)4、純虛函數(shù)和抽象類純虛函數(shù)是一個在基類中說明的虛函數(shù),它在該基類中沒有定義,但要求在它的派生類中定義自己的版本,或重新說明為純虛函數(shù)純虛函數(shù)的一般形式如下:

virtualtypefunc_name(參數(shù)表)=0;

這里,type是函數(shù)的返回類型,func_name是函數(shù)名,此形式與一般的虛函數(shù)的形式相同,只是在后面多了“=0”

7.5、虛函數(shù)#include<iostream.h>

classcircle{//聲明類circle

protected:

intr;//圓的半徑

public:

voidsetr(intx)//設(shè)定圓的半徑

{r=x; }

virtualvoidshow()=0;//定義純虛函數(shù)show()

};

//聲明派生類area——從circle類派生

classarea:publiccircle{

public:

voidshow()//重新定義虛函數(shù)show()

{cout<<"areais"<<3.14*r*r<<endl; }

};7.5、虛函數(shù)//聲明派生類perimeter——從circle類派生

classperimeter:publiccircle{

public:

voidshow() //重新定義虛函數(shù)show()

{

cout<<"perimeteris"<<2*3.14*r<<endl;

}

};

voidmain()

{

circle*ptr;//聲明基類circle指針

areaob1;//聲明派生類area對象

perimeterob2;//聲明派生類perimeter對象7.5、虛函數(shù)

ob1.setr(10);//調(diào)用基類的方法設(shè)定對象的屬性

ob2.setr(10);//調(diào)用基類的方法設(shè)定對象的屬性

ptr=&ob1;//初始化基類指針

ptr->show();//多態(tài):指向基類的指針調(diào)用area類的成員函數(shù)

ptr=&ob2;

ptr->show();//多態(tài):指向基類的指針調(diào)用perimeter類的成員函數(shù)

}7.5、虛函數(shù)

在以上的例子中,circle是一個基類,它表示一個圓。從它可以派生出面積類area和周長類perimeter。

顯然,基類中定義的show()函數(shù)是沒有任何意義的,它只是用來提供派生類使用的公共接口,所以程序中將其定義為純虛函數(shù),但在派生類中,則根據(jù)它們自己的需要,具體地重新定義虛函數(shù)。編程過程中常常有這樣的情況,設(shè)計的基類是一個很抽象的概念,如形狀。其中計算面積的成員函數(shù)不能夠完全實現(xiàn),只能聲明為純虛函數(shù),而不必要定義函數(shù)體。7.5、虛函數(shù)含有純虛函數(shù)的被稱為抽象類:

classshape{//抽象類

virtualfloatarea()=0;//純虛函數(shù)

};設(shè)計抽象類的目的是為了多態(tài)地使用它的成員函數(shù);

對于抽象類是不能實例化的。必須通過繼承得到派生類后,在派生類中對純虛函數(shù)進行了定義,再獲得派生類的對象7.5、虛函數(shù)抽象類和純虛函數(shù)的使用總結(jié):1、抽象類只能用作其他類的基類,不能實例化,必須通過繼承得到派生類后,在派生類中對純虛函數(shù)進行了定義,再獲得派生類的對象2、抽象類中至少包含一個未定義功能的純虛函數(shù)設(shè)計抽象類的目的是為了多態(tài)地使用它的成員函數(shù)3、因為抽象類不能表示具體的類型,因此,抽象類不能用作參數(shù)類型、函數(shù)返回類型或進行顯示類型轉(zhuǎn)換的類型。一般聲明為抽象類的指針或引用,該指針指向它的派生類,從而實現(xiàn)多態(tài)性7.5、虛函數(shù)4、純虛函數(shù)是在基類中聲明的虛函數(shù),并不在基類中進行定義。如果在派生類中,并沒有對其抽象基類中的純虛函數(shù)進行定義,則該虛函數(shù)在派生類中仍為純虛函數(shù),而該派生類也是一個抽象類5、在抽象基類中除了聲明純虛函數(shù)以外,還可以定義普通成員函數(shù)和虛函數(shù)??梢酝ㄟ^派生類的對象來訪問它們本章小結(jié)本章介紹了C++中關(guān)于多態(tài)性機制的相關(guān)知識。多態(tài)性使得程序內(nèi)需設(shè)計和實現(xiàn)易于擴展和維護,程序可以對層次中所有現(xiàn)有類的對象進行一般性處理。多態(tài)性是通過虛函數(shù)機制來實現(xiàn)的,我們對虛函數(shù)的定義,使用進行了詳細的介紹。同時介紹了抽象類和純虛函數(shù)在實際設(shè)計中的作用,并結(jié)合具體的例子介紹了抽象類和純虛函數(shù)的定義和使用。

運算符重載在一定程度上提高了程序的可讀性,本章我們對運算符重載的定義和使用進行了介紹,并結(jié)合例子說明了幾種常用運算符重載的實現(xiàn)。正確的使用運算符可以增加程序的可讀性,但不合理的或過度的使用運算符重載會使程序語義不清且難以閱讀。綜合訓(xùn)練1題目:

利用虛函數(shù)實現(xiàn)的多態(tài)性來求四種幾何圖形的面積之和。這四種幾何圖形是:三角形、矩形、正方形和圓。

幾何圖形的類型可以通過構(gòu)造函數(shù)或通過成員函數(shù)來設(shè)置綜合訓(xùn)練1分析:1、定義一個包含兩個虛函數(shù)的類:

class

Shape{

public:

virtual

float

Area(

void)

=0;//求面積

virtual

void

Setdata(float

,float

=0)

=0;//設(shè)置圖形數(shù)據(jù)

};因為Shape的形狀是抽象的,無法計算面積和設(shè)置形狀數(shù)據(jù)綜合訓(xùn)練12、定義一個計算面積之和的類,該類中定義一個指向基類Shape的指針數(shù)組,其元素分別指向由基類Shape派生出的不同的幾何圖形類,并完成求出所有幾何圖形面積之和,以及設(shè)置參數(shù)的函數(shù)。綜合訓(xùn)練1classShape{

public:

virtualfloatArea(void)=0; //虛函數(shù)

virtualvoidSetdata(float,float=0)=0;//虛函數(shù)

};

classTriangle:publicShape{

floatW,H; //三角形邊長為W,高為H

public:

Triangle(floatw=0,floath=0)

{W=w;H=h;}

floatArea(void) //定義虛函數(shù)

{returnW*H/2;}

voidSetdata(float

w,floath=0)//定義虛函數(shù)

{

W=w;H=h;

}

};綜合訓(xùn)練1classRectangle:publicShape{

floatW,H; //矩形邊長為W,高為H

public:

Rectangle(floatw=0,floath=0){W=w;H=h;}

floatArea(void)

溫馨提示

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

評論

0/150

提交評論