版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、1,第八章 多態(tài)性,C++語言程序設(shè)計(jì),2,本章主要內(nèi)容,多態(tài)性運(yùn)算符重載虛函數(shù)純虛函數(shù)抽象類,3,多態(tài)性的概念,多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計(jì)的重要特征之一。多態(tài)性是指發(fā)出同樣的消息被不同類型的對象接收時導(dǎo)致完全不同的行為。多態(tài)從實(shí)現(xiàn)的角度劃分為:編譯時的多態(tài)和運(yùn)行時的多態(tài)多態(tài)的實(shí)現(xiàn):函數(shù)重載運(yùn)算符重載虛函數(shù),4,靜態(tài)聯(lián)編與動態(tài)聯(lián)編,靜態(tài)聯(lián)編:在編譯連接階段就確定了同名操作的具體對象的情況。重載動態(tài)聯(lián)編:在程序運(yùn)行
2、過程中才動態(tài)地確定操作所針對的具體對象的情況。虛函數(shù),5,問題舉例——復(fù)數(shù)的運(yùn)算,class complex//復(fù)數(shù)類聲明{public:complex(double r=0.0,double i=0.0){real=r;imag=i;} //構(gòu)造函數(shù)void display( );//顯示復(fù)數(shù)的值pr
3、ivate:double real;double imag;};,運(yùn)算符重載,6,問題舉例——復(fù)數(shù)的運(yùn)算,用“+”、“-”能夠?qū)崿F(xiàn)復(fù)數(shù)的加減運(yùn)算嗎?實(shí)現(xiàn)復(fù)數(shù)加減運(yùn)算的方法 ——重載“+”、“-”運(yùn)算符,運(yùn)算符重載,7,運(yùn)算符重載的實(shí)質(zhì),運(yùn)算符重載是對已有的運(yùn)算符賦予多重含義必要性C++中預(yù)定義的運(yùn)算符其運(yùn)算對象只能是基本數(shù)據(jù)類型,而不適用于用戶自定義類型(如類)實(shí)現(xiàn)機(jī)制將指定的運(yùn)算表達(dá)式轉(zhuǎn)化為對
4、運(yùn)算符函數(shù)的調(diào)用,運(yùn)算對象轉(zhuǎn)化為運(yùn)算符函數(shù)的實(shí)參。編譯系統(tǒng)對重載運(yùn)算符的選擇,遵循函數(shù)重載的選擇原則。class A{……}; A c, a, b; c=a+b;c=a+b等價于c=a.operator+(b);或c=operator+(a,b);,運(yùn)算符重載,8,運(yùn)算符重載,規(guī)則和限制,可以重載C++中除下列運(yùn)算符外的所有運(yùn)算符:. .* :: ?: sizeof只能重載C++語言中已有的運(yùn)算符,不可臆
5、造新的。書上P33已給出了C++中全部運(yùn)算符不改變原運(yùn)算符的優(yōu)先級和結(jié)合性。不能改變操作數(shù)個數(shù)。經(jīng)重載的運(yùn)算符,其操作數(shù)中至少應(yīng)該有一個是自定義類型。,9,兩種形式,用戶定義類型的重載運(yùn)算符,要求能訪問運(yùn)算對象的私有成員。為此只能用成員函數(shù)或友元函數(shù)兩種形式定義運(yùn)算符重載。重載為類成員函數(shù)。重載為友元函數(shù)。,運(yùn)算符重載,10,運(yùn)算符函數(shù),聲明形式函數(shù)類型 operator 運(yùn)算符(形參){ ......
6、}重載為類成員函數(shù)時 參數(shù)個數(shù)=原操作數(shù)個數(shù)-1(后置++、--除外)重載為友元函數(shù)時 參數(shù)個數(shù)=原操作數(shù)個數(shù),且至少應(yīng)該有一個自定義類型的形參。,運(yùn)算符重載,11,運(yùn)算符成員函數(shù)的設(shè)計(jì),雙目運(yùn)算符 B如果要重載 B 為A類成員函數(shù),使之能夠?qū)崿F(xiàn)表達(dá)式 oprd1 B oprd2,其中 oprd1 為A 類對象,則 B 應(yīng)被重載為 A 類的成員函數(shù),形參類型應(yīng)該是 oprd2 所屬的類型。經(jīng)重載后,表達(dá)式 oprd1 B
7、 oprd2 相當(dāng)于 oprd1.operator B(oprd2),運(yùn)算符重載,12,運(yùn)算符重載,例 8.1,將“+”、“-”運(yùn)算符重載為復(fù)數(shù)類的成員函數(shù)。 規(guī)則:實(shí)部和虛部分別相加減。 操作數(shù):兩個操作數(shù)都是復(fù)數(shù)類的對象。,#includeclass complex//復(fù)數(shù)類聲明{public://外部接口complex(double r=0.0,double i=0.0) //構(gòu)造函數(shù) {rea
8、l=r;imag=i;} complex operator + (complex c2); //+重載為成員函數(shù)complex operator - (complex c2); //-重載為成員函數(shù)void display( );//輸出復(fù)數(shù)private://私有數(shù)據(jù)成員doub
9、le real;//復(fù)數(shù)實(shí)部double imag;//復(fù)數(shù)虛部};,complex complex::operator +(complex c2) //重載函數(shù)實(shí)現(xiàn){ complex c;c.real= real + c2.real;c.imag= imag + c2.imag;return complex(c.real,c.imag);} //return c;,
10、complex complex::operator -(complex c2) //重載函數(shù)實(shí)現(xiàn){complex c;c.real=real-c2.real;c.imag=imag-c2.imag;return complex(c.real,c.imag);}void complex::display( ){ cout<<"("<<real<<&qu
11、ot;,"<<imag<<")"<<endl; },void main( ) //主函數(shù){complex c1(5,4),c2(2,10),c3; //聲明復(fù)數(shù)類的對象cout<<"c1="; c1.display( );cout<<"c2="; c2.display( );c3=c
12、1-c2;//使用重載運(yùn)算符完成復(fù)數(shù)減法 //等價于c3=c1.operator-(c2); 即operator-(&c1,c2)cout<<"c3=c1-c2=";c3.display( );c3=c1+c2;//使用重載運(yùn)算符完成復(fù)數(shù)加法//等價于c3=c1.operator+(c2); cout<<"c3=c1+c2=";
13、c3.display( );},程序輸出的結(jié)果為:c1=(5,4)c2=(2,10)c3=c1-c2=(3,-6)c3=c1+c2=(7,14),成員函數(shù)operator+( )中的形參用類對象的引用與用類對象有什么不同嗎?complex operator+(complex& c);complex operator+(complex c);c=c1+c2;即c=c1.operator+(c2);,,18,運(yùn)
14、算符成員函數(shù)的設(shè)計(jì),前置單目運(yùn)算符 U如果要重載 U 為類成員函數(shù),使之能夠?qū)崿F(xiàn)表達(dá)式 U oprd,其中 oprd 為A類對象,則 U 應(yīng)被重載為 A 類的成員函數(shù),無形參。經(jīng)重載后,表達(dá)式 U oprd 相當(dāng)于 oprd.operator U( ),運(yùn)算符重載,19,運(yùn)算符成員函數(shù)的設(shè)計(jì),后置單目運(yùn)算符 ++和--如果要重載 ++或--為類成員函數(shù),使之能夠?qū)崿F(xiàn)表達(dá)式 oprd++ 或 oprd-- ,其中 oprd 為A
15、類對象,則 ++或-- 應(yīng)被重載為 A 類的成員函數(shù),且具有一個 int 類型形參(它只是為了區(qū)別前增量與后增量)。經(jīng)重載后,表達(dá)式 oprd++ 相當(dāng)于 oprd.operator ++(0)而++oprd相當(dāng)于oprd.operator++( ),運(yùn)算符重載,20,例8.2,運(yùn)算符前置++和后置++重載為時鐘類的成員函數(shù)。前置單目運(yùn)算符,重載函數(shù)沒有形參,對于后置單目運(yùn)算符,重載函數(shù)需要有一個整型形參。操作數(shù)是時鐘類的
16、對象。實(shí)現(xiàn)時間增加1秒鐘。,運(yùn)算符重載,#includeclass Clock//時鐘類聲明{public://外部接口Clock(int NewH=0, int NewM=0, int NewS=0);void ShowTime( );void operator ++( ); //前置單目運(yùn)算符重載void operator ++(int); //后置單目運(yùn)算符重載private:/
17、/私有數(shù)據(jù)成員int Hour, Minute, Second;};,void Clock::operator ++( )//前置單目運(yùn)算符重載函數(shù){Second++;if(Second>=60) { Second=Second-60;Minute++;if(Minute>=60) {Minute=Minute-60;Hour++;Hour
18、=Hour%24;}}cout<<"++Clock: ";},void Clock::operator ++(int) //后置單目運(yùn)算符重載{Second++;if(Second>=60) {Second=Second-60;Minute++;if(Minute>=60) {Minute=Minute-60;Ho
19、ur++;Hour=Hour%24;}}cout<<"Clock++: ";},//其它成員函數(shù)的實(shí)現(xiàn)略void main( ){Clock myClock(23,59,59);cout<<"First time output:";myClock.ShowTime( );myClock++; //等價于myClock.oper
20、ator++(0); myClock.ShowTime( );++myClock; //等價于myClock.operator++( );myClock.ShowTime( );},程序運(yùn)行結(jié)果為:First time output:23:59:59Clock++: 0:0:0++Clock: 0:0:1,26,運(yùn)算符友元函數(shù)的設(shè)計(jì),如果需要重載一個運(yùn)算符,使之能夠用于操作某類對象的私有成員,可以將此運(yùn)算符重載為
21、該類的友元函數(shù)。函數(shù)的形參代表依自左至右次序排列的各操作數(shù)。后置單目運(yùn)算符 ++和--的重載函數(shù),形參列表中要增加一個int,但不必寫形參名。,運(yùn)算符重載,27,運(yùn)算符友元函數(shù)的設(shè)計(jì),雙目運(yùn)算符 B重載后,表達(dá)式oprd1 B oprd2 等同于operator B(oprd1,oprd2 )前置單目運(yùn)算符 B重載后,表達(dá)式 B oprd 等同于operator B(oprd )后置單目運(yùn)算符 ++和--重載后,表達(dá)式
22、 oprd B 等同于operator B(oprd,0 ),運(yùn)算符重載,28,例8-3,將+、-(雙目)重載為復(fù)數(shù)類的友元函數(shù)。兩個操作數(shù)都是復(fù)數(shù)類的對象。,運(yùn)算符重載,#includeclass complex//復(fù)數(shù)類聲明{public://外部接口complex(double r=0.0,double i=0.0) { real=r; imag=i; }//構(gòu)造函數(shù)friend compl
23、ex operator + (complex c1,complex c2);//運(yùn)算符+重載為友元函數(shù)friend complex operator - (complex c1,complex c2);//運(yùn)算符-重載為友元函數(shù)void display( );//顯示復(fù)數(shù)的值private://私有數(shù)據(jù)成員double real;double imag;};,complex operator +(comple
24、x c1,complex c2)//運(yùn)算符重載友元函數(shù)實(shí)現(xiàn){ return complex(c2.real+c1.real, c2.imag+c1.imag);}complex operator -(complex c1,complex c2)//運(yùn)算符重載友元函數(shù)實(shí)現(xiàn){return complex(c1.real-c2.real, c1.imag-c2.imag);}// 其它函數(shù)和主函
25、數(shù)同例8.1,31,靜態(tài)聯(lián)編與動態(tài)聯(lián)編,聯(lián)編(binding):程序自身彼此關(guān)聯(lián)的過程,確定程序中的操作調(diào)用與執(zhí)行該操作的代碼間的關(guān)系。靜態(tài)聯(lián)編(靜態(tài)束定static binding)聯(lián)編工作出現(xiàn)在編譯階段,用對象名或者類名來限定要調(diào)用的函數(shù)。動態(tài)聯(lián)編(dynamic binding)聯(lián)編工作在程序運(yùn)行時執(zhí)行,在程序運(yùn)行時才確定將要調(diào)用的函數(shù)。,例,例,#includeclass Point{ public:Point
26、(double i, double j) {x=i; y=j;}double Area( ) const{ return 0.0;} private:double x, y;};class Rectangle:public Point{ public:Rectangle(double i, double j, double k, double l);double Area( ) const {retur
27、n w*h;} private:double w,h;};,靜態(tài)聯(lián)編例,Rectangle::Rectangle(double i, double j, double k, double l) :Point(i,j){w=k; h=l; }void fun(Point &s){cout<<"Area="<<s.Area( )<<endl; }vo
28、id main( ){Rectangle rec(3.0, 5.2, 15.0, 25.0);fun(rec);}運(yùn)行結(jié)果:Area=0,,#includeclass Point{ public: Point(double i, double j) {x=i; y=j;} virtual double Area( ) const{ return 0.0;} private:double x, y;
29、};class Rectangle:public Point{ public: Rectangle(double i, double j, double k, double l); virtual double Area( ) const {return w*h;} //在派生類對基類中說明的虛函數(shù)進(jìn)行重定義,它不繼承基類中的虛函數(shù)private:double w,h;};//其它函數(shù)同例 8.8,動態(tài)聯(lián)編例,
30、void fun(Point &s){cout<<"Area="<<s.Area( )<<endl; }void main( ){Rectangle rec(3.0, 5.2, 15.0, 25.0);fun(rec);}運(yùn)行結(jié)果:Area=375,,36,虛函數(shù),虛函數(shù)是動態(tài)聯(lián)編的基礎(chǔ)。它是非靜態(tài)的成員函數(shù)。靜態(tài)成員函數(shù)和友元函數(shù)都不能說明為
31、虛函數(shù)。在類的聲明中,在函數(shù)原型之前寫virtual。virtual 只用來說明類聲明中的函數(shù)原型,不能用在函數(shù)實(shí)現(xiàn)時。具有繼承性,基類中聲明了虛函數(shù),派生類中無論是否說明,同原型函數(shù)都自動為虛函數(shù)。本質(zhì):不是重載聲明而是覆蓋。調(diào)用方式:通過基類指針或引用,執(zhí)行時會根據(jù)指針指向的對象的類,決定調(diào)用哪個函數(shù)。,37,例 8.4,#include class B0//基類B0聲明{public://外部接口virt
32、ual void display( ) {cout<<"B0::display( )"<<endl;} //虛成員函數(shù)};,class B1: public B0//公有派生{ public: void display( ) { cout<<"B1::dis
33、play( )"<<endl; }};class D1: public B1//公有派生{ public: void display( ) { cout<<"D1::display( )"<<endl; }};,void fun(B0 *ptr)//普通函數(shù){ ptr->display( ); }void main(
34、 )//主函數(shù){B0 b0, *p;//聲明基類對象和指針B1 b1;//聲明派生類對象D1 d1;//聲明派生類對象p=&b0;fun(p);//調(diào)用基類B0成員函數(shù)p=&b1;fun(p);//調(diào)用派生類B1成員函數(shù)p=&d1;fun(p);//調(diào)用派生類D1成員函數(shù)},,,B0,B1,,D1,程序的運(yùn)行結(jié)果為:B0::display( )B1::d
35、isplay( )D1::display( ),41,虛函數(shù)的限制,一個虛函數(shù)是屬于它所在的類層次結(jié)構(gòu)的,而不是只屬于某一個類,只不過它在該類層次結(jié)構(gòu)中的不同類中具有不同的形態(tài)。一旦一個函數(shù)被聲明為虛函數(shù),不管經(jīng)歷多少次派生,仍將保持其虛特性。即一個接口,多個形態(tài)。若派生類中沒有對基類中說明的虛函數(shù)進(jìn)行重新定義,則它繼承基類中的虛函數(shù)。構(gòu)造函數(shù)不能是虛函數(shù),因?yàn)闃?gòu)造時,對象還是一片未定型的空間。只有在構(gòu)造完成后,對象才能成為一個類
36、的名副其實(shí)的實(shí)例。,42,虛析構(gòu)函數(shù),不能聲明虛構(gòu)造函數(shù),但可聲明虛析構(gòu)函數(shù)。虛析構(gòu)函數(shù)的聲明語法:virtual ~類名();若一個類的析構(gòu)函數(shù)是虛函數(shù),則由它派生的所有子類的析構(gòu)函數(shù)也是虛函數(shù)。析構(gòu)函數(shù)設(shè)置為虛函數(shù)之后,在使用指針引用時可以動態(tài)聯(lián)編,實(shí)現(xiàn)運(yùn)行時的多態(tài)。例:當(dāng)基類對象和子類對象以不同方式申請了堆空間后void fun(Base *p){ delete p;} 其中p是傳遞過來的一個對象指針,它或者指向基類對
37、象或者指向子類對象。在執(zhí)行delete p時,要調(diào)用析構(gòu)函數(shù),但執(zhí)行基類的析構(gòu)函數(shù)?還是執(zhí)行子類的析構(gòu)函數(shù)?將析構(gòu)函數(shù)聲明為虛的,即可。,43,虛函數(shù)的實(shí)現(xiàn)——虛表,C++的多態(tài)性就是要通過動態(tài)聯(lián)編的方法正確調(diào)用繼承家族中那些同名的虛函數(shù)。C++使用了一個類似函數(shù)指針表的動態(tài)聯(lián)編表(稱為虛表),用vtable表示若一個類含有虛函數(shù),則編譯器在編譯時將為它產(chǎn)生一個虛表。若基類中含有虛函數(shù),則它以及它的每個派生類都將含有自己的虛表。虛表
38、中存放了它所在類的所有虛函數(shù)的指針,包括它從基類繼承來的虛函數(shù)。每個含有虛函數(shù)的類對象都在其數(shù)據(jù)成員中增加一個指針成員vptr,指向該類的虛表(vtable)。,44,class A{public: int a; virtual void funA(); virtual void show();};class C:public A{int c; public: void funC(); vo
39、id show();}; void main(){ C ca; A *pa=&ca; pa->show();},通過基類指針對虛函數(shù)的調(diào)用將由編譯器轉(zhuǎn)換成一個間接調(diào)用。pa->show()將變成 (*(pa->vptr[1]))()由于pa->vptr[1]==C::show,所以盡管指針pa為A類指針,它所指向的對象卻是C類對象,通過它產(chǎn)生的show()調(diào)用仍然是C:
40、:show(),而非A::show(),45,抽象類的一般形式,帶有純虛函數(shù)的類稱為抽象類:class 類名 { virtual 類型 函數(shù)名(參數(shù)表)=0; //純虛函數(shù) ...};純虛函數(shù)是一種特殊的虛函數(shù),它沒有具體實(shí)現(xiàn)。其純虛函數(shù)的實(shí)現(xiàn)由其派生類給出。,純虛函數(shù)與抽象類,46,作用,抽象類為抽象和設(shè)計(jì)的目的而建立,將有關(guān)的數(shù)
41、據(jù)和行為組織在一個繼承層次結(jié)構(gòu)中,保證派生類具有要求的行為。對于暫時無法實(shí)現(xiàn)的函數(shù),可以聲明為純虛函數(shù),留給派生類去實(shí)現(xiàn)。例:由形狀類派生出矩形類和圓形類,每個類中都有求面積的函數(shù)area,在基類中該函數(shù)就定義為純虛函數(shù),純虛函數(shù)與抽象類,47,注意,抽象類一般作為基類來使用,其純虛函數(shù)的實(shí)現(xiàn)由其派生類給出。不能聲明抽象類的對象。但可聲明抽象類的指針或引用。構(gòu)造函數(shù)不能是虛函數(shù),析構(gòu)函數(shù)可以是虛函數(shù)。,純虛函數(shù)與抽象類,48,例
42、 8.5,#include class B0 //抽象基類B0聲明{public: //外部接口virtual void display( )=0; //純虛函數(shù)成員};,純虛函數(shù)與抽象類,class B1: public B0//公有派生{public:void display( ){cout<<"
43、B1::display( )"<<endl;} //虛成員函數(shù)};class D1: public B1//公有派生{public:void display( ){cout<<"D1::display( )"<<endl;}
44、 //虛成員函數(shù)};,void fun(B0 *ptr)//普通函數(shù){ptr->display( ); }void main( )//主函數(shù){B0 *p;//聲明抽象基類指針,不能聲明基類對象B1 b1;//聲明派生類對象D1 d1;//聲明派生類對象p=&b1;
45、fun(p);//調(diào)用派生類B1函數(shù)成員p=&d1;fun(p);//調(diào)用派生類D1函數(shù)成員},程序的運(yùn)行結(jié)果為:B1::display( )D1::display( ),見顯示器類.cpp和形狀類.cpp,52,本章小結(jié),多態(tài):同樣的消息被不同類型的對象接收時導(dǎo)致完全不同的行為,是對類的特定成員函數(shù)的再抽象。運(yùn)算符重載對已有的運(yùn)算符賦予多重含義,使用已有運(yùn)算符對用戶自定義類型(比如類)進(jìn)行運(yùn)算操作。,
46、53,本章小結(jié),聯(lián)編程序自身彼此關(guān)聯(lián)的過程稱為聯(lián)編,聯(lián)編確定程序中的操作調(diào)用與執(zhí)行該操作的代碼間的關(guān)系。靜態(tài)聯(lián)編工作出現(xiàn)在編譯階段。動態(tài)聯(lián)編工作在程序運(yùn)行時執(zhí)行。虛函數(shù)是動態(tài)聯(lián)編的基礎(chǔ)。,54,本章小結(jié),純虛函數(shù)在基類中說明的虛函數(shù),它在該基類中可以不給出函數(shù)體,要求各派生類根據(jù)實(shí)際需要編寫自己的函數(shù)體。抽象類帶有純虛函數(shù)的類是抽象類。抽象類的主要作用是通過它為一個類族建立一個公共的接口,使它們能夠更有效地發(fā)揮多態(tài)特性。
溫馨提示
- 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. 眾賞文庫僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
評論
0/150
提交評論