




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第7章多態性
2011年10月12日主要內容多態性的實現類型聯編虛函數抽象類函數重載運算符重載7.1多態性的實現類型多態性就是向不同的對象發送同一個消息,不同的對象在接收時會產生不同的行為。面向對象的多態性分為四類:
7.2聯編多態性的實現過程中,確定調用哪一個同名函數的過程就是聯編,又稱為綁定。按照聯編進行的階段不同,可以分為靜態聯編和動態聯編,這兩種聯編分別對應C++面向對象技術多態特性的兩種實現方式。7.2.1靜態聯編
靜態聯編是指在編譯階段完成的聯編方式。在編譯過程中,編譯系統可以根據參數類型和參數數量的不同來確定調用哪一個同名函數。其特點是函數調用速度快、效率高,不足之處是編程不夠靈活。例7-1.靜態聯編使用#include<iostream>usingnamespacestd;classUndergraduate{public: voidDisplay(){
cout<<"CallBaseClass"<<endl;
cout<<"UndergraduateLiMing"<<endl; }};classMaster:publicUndergraduate{public: voidDisplay(){
cout<<"CallMasterClass"<<endl;
cout<<"MasterWangWei"<<endl; }};classDoctor:publicMaster{
public:
voidDisplay(){
cout<<"CallDoctorClass"<<endl;
cout<<"DoctorZhangHua"<<endl;
}
};
voidmain(){
Undergraduates1,*pointer;//定義基類對象s1和指向基類的指針
Masters2;//定義派生類對象s2
Doctors3;//定義派生類對象s3
pointer=&s1;//指針pointer指向基類對象s1
pointer->Display();
pointer=&s2;//指針pointer指向基類對象s2
//期望調用對象s2的函數display,但實際執行卻調用了對象s1的輸出函數
pointer->Display();
pointer=&s3;
//期望調用對象s3的函數display,但實際執行卻調用了對象s1的輸出函數
pointer->Display();
}程序運行結果
CallBaseClassUndergraduateLiMingCallBaseClassUndergraduateLiMingCallBaseClassUndergraduateLiMing7.2.2動態聯編只有在程序運行時才能確定將要調用哪一個函數。動態聯編的主要優點是使編程更具靈活性,對問題的抽象更方便,程序的易維護性更好;其缺點是與靜態聯編相比,函數調用速度更慢。在編譯階段不能解決的聯編問題,需要等到程序運行以后才能確定,則選擇動態聯編,這就需要虛函數來解決7.3虛函數虛函數是動態聯編的主要實現方式,是動態聯編的基礎。虛函數的作用是允許在派生類中重新定義與基類同名的函數,并且可以通過基類指針或引用來訪問基類和派生類中的同名函數,聲明方式是:virtual返回值類型函數名(參數表){
函數體;}7.3.1虛函數的使用(1)在基類聲明成員函數為虛函數,在類外定義虛函數是不必再加virtual。(2)在派生類中重新定義此函數時,要求函數名、函數類型、參數個數和參數類型全部與虛函數相同,否者會被認為是普通的函數重載。并根據需要重新定義函數體。C++規定當一個成員函數被定義成虛函數后,其派生類中的同名函數都自動成為虛函數,故virtual也可不加,但為了提高可讀性,最好都加上。(3)定義一個指向基類對象的指針變量,并使它指向同一類族中的某一對象。
(4)通過該指針調用此虛函數,此時調用的就是指針變量指向的對象的同名函數。例7-2.通過對象引用調用虛函數實現動態聯編
#include<iostream.h>classUndergraduate{public: virtualvoidprint(){
cout<<"CallBaseClass"<<endl;
cout<<"UndergraduateLiMing"<<endl; }};classMaster:publicUndergraduate{public: virtualvoidprint(){
cout<<"CallMaterClass"<<endl;
cout<<"MasterWangWei"<<endl; }};
voidfunction(Undergraduate&s){
s.print();//通過對象引用調用虛函數
}
voidmain(){
Undergraduates1;
Masters2;
function(s1);
function(s2);
}
程序運行結果:CallBaseClassUndergraduateLiMingCallMaterClassMasterWangWei應當聲明虛函數的情況:(1)首先成員函數所在的類必須會作為基類,然后看成員函數在類的繼承之后有無可能被更改功能,如果希望更改其功能的,一般將其聲明為虛函數,若不更改,則不需聲明虛函數。(2)考慮對成員函數的調用是通過對象名還是通過基類指針或引用,如果是通過基類指針或引用去訪問的,則應當聲明為虛函數。(3)有時定義虛函數時,函數體是空的,其功能留給派生類去增加。7.3.2虛析構函數如果用new運算符建立了臨時對象,若基類中有析構函數,并且定了了一個指向該基類的指針變量。在程序用帶指針參數的delete運算符撤銷對象是會發生一個情況:系統只會執行基類的析構函數,而不執行派生類的析構函數。為解決此問題,可將基類的析構函數聲明為虛函數
virtual~類名(){
函數體
}7.4純虛函數和抽象類純虛函數是在聲明虛函數時被“初始化”為0的函數,一般形式為Virtual函數類型函數名(參數表)=0;不用來定義對象而只作為一種基本類型用作繼承的類,稱為抽象類。由于它常用作基類,通常稱為抽象基類。凡是包含純虛函數的類都是抽象類。例7-3使用純虛函數
#include<iostream.h>classpoint{
public:
point(inti=0,intj=0){x0=i;y0=j;}
virtualvoidset()=0;
virtualvoiddraw()=0;
protected:
intx0,y0;};classline:publicpoint{
public:
line(inti=0,intj=0,intm=0,intn=0):point(i,j){
x1=m;y1=n;
}
voidset(){cout<<"line::set()called.\n";}
voiddraw(){cout<<"line::draw()called.\n";}
protected:
intx1,y1;
};
classellipse:publicpoint{
public:
ellipse(inti=0,intj=0,intp=0,intq=0):point(i,j){
x2=p;y2=q;
}
voidset(){cout<<"ellipse::set()called.\n";}
voiddraw(){cout<<"ellipse::draw()called.\n";}
protected:
intx2,y2;
};
voiddrawobj(point*p){
p->draw();
}
voidsetobj(point*p){
p->set();
}
voidmain(){
line*lineobj=newline;
ellipse*elliobj=newellipse;
drawobj(lineobj);
drawobj(elliobj);
cout<<endl;
setobj(lineobj);
setobj(elliobj);
cout<<"\nRedrawtheobject…\n";
drawobj(lineobj);
drawobj(elliobj);
}
程序運行結果line::draw()called.ellipse::draw()called.
line::set()called.ellipse::set()called.
Redrawtheobject…line::draw()called.ellipse::draw()called.
綜上所述,可將純虛函數歸結為:抽象類的唯一用途是為派生類提供基類,純虛函數的作用是作為派生類的成員函數的基礎,并實現動態多態性。7.5函數重載函數重載是指兩個或兩個以上的函數具有相同的函數名,但參數類型不一致或參數個數不同,從而使重載的函數雖然函數名相同,但功能上卻不完全相同。函數重載包括成員函數的重載和普通函數的重載。例7-4構造函數進行重載#include<iostream.h>#include<string.h>classstring{
public:
string(char*s);
string(string&s1);
string(intsize=80);
~string(){deletesptr;}
int
getlen(){returnlength;}
voidprint(){cout<<sptr<<endl;}
private:
char*sptr;
intlength;};string::string(char*s){length=strlen(s);
sptr=newchar[length+1];
strcpy(sptr,s);}string::string(string&s1){length=s1.length;
sptr=newchar[length+1];
strcpy(sptr,s1.sptr);}string::string(intsize){length=size;
sptr=newchar[length+1];*sptr='\0';}voidmain(){stringstr1("Thisisastring.");str1.print();
cout<<str1.getlen()<<endl;char*s1="Thatisaprogram.";stringstr2(s1);stringstr3(str2);str3.print();
cout<<str3.getlen()<<endl;}運行結果:Thisisastring.17Thatisaprogram.187.6運算符重載運算符重載是對已有的運算符賦予多重含義必要性C++中預定義的運算符其運算對象只能是基本數據類型,而不適用于用戶自定義類型(如類)實現機制將指定的運算表達式轉化為對運算符函數的調用,運算對象轉化為運算符函數的實參。編譯系統對重載運算符的選擇,遵循函數重載的選擇原則。7.6.1運算符重載規則可以重載C++中除下列運算符外的所有運算符:
.*->::sizeof?:只能重載C++語言中已有的運算符,不可臆造新的。不改變原運算符的優先級和結合性。不能改變操作數個數。經重載的運算符,其操作數中至少應該有一個是自定義類型。運算符重載的兩種方式重載為類的成員函數重載為類的友元函數7.6.2運算符重載為成員函數聲明形式返回值類型operator運算符(形參){
函數體}重載為類成員函數時
參數個數=原操作數個數-1 (后置++、--除外)運算符成員函數的設計雙目運算符B如果要重載B為類成員函數,使之能夠實現表達式oprd1Boprd2,其中
oprd1為A類對象,則B應被重載為A類的成員函數,形參類型應該是oprd2
所屬的類型。經重載后,表達式
oprd1Boprd2
相當于oprd1.operatorB(oprd2)例7-5
將“+”、“-”運算重載為復數類的成員函數。規則:實部和虛部分別相加減。操作數:兩個操作數都是復數類的對象。#include<iostream>usingnamespacestd;classcomplex //復數類聲明{public: //外部接口
complex(doubler=0.0,doublei=0.0){real=r;imag=i;}//構造函數
complexoperator+(complexc2);//+重載為成員函數
complexoperator-(complexc2);//-重載為成員函數
voiddisplay(); //輸出復數private: doublereal; //復數實部
doubleimag; //復數虛部}; complexcomplex::operator+(complexc2)//重載函數實現{ complexc;
c.real=c2.real+real;
c.imag=c2.imag+imag; returncomplex(c.real,c.imag);}complexcomplex::operator-(complexc2)//重載函數實現{ complexc;
c.real=real-c2.real;
c.imag=imag-c2.imag; returncomplex(c.real,c.imag);}voidcomplex::display(){cout<<"("<<real<<","<<imag<<")"<<endl;}voidmain()//主函數{ complexc1(5,4),c2(2,10),c3;//聲明復數類的對象
cout<<"c1=";c1.display();
cout<<"c2=";c2.display();
c3=c1-c2; //使用重載運算符完成復數減法
cout<<"c3=c1-c2="; c3.display();
c3=c1+c2; //使用重載運算符完成復數加法
cout<<"c3=c1+c2="; c3.display();}程序輸出的結果為:c1=(5,4)c2=(2,10)c3=c1-c2=(3,-6)c3=c1+c2=(7,14)運算符成員函數的設計前置單目運算符U如果要重載U為類成員函數,使之能夠實現表達式Uoprd,其中
oprd
為A類對象,則U應被重載為A類的成員函數,無形參。經重載后,
表達式
Uoprd
相當于oprd.operatorU()后置單目運算符++和--如果要重載++或--為類成員函數,使之能夠實現表達式
oprd++
或oprd--
,其中
oprd
為A類對象,則++或--應被重載為A類的成員函數,且具有一個int
類型形參。經重載后,表達式
oprd++
相當于oprd.operator++(0)例7-6運算符前置++和后置++重載為時鐘類的成員函數。前置單目運算符,重載函數沒有形參,對于后置單目運算符,重載函數需要有一個整型形參。操作數是時鐘類的對象。實現時間增加1秒鐘。#include<iostream>usingnamespacestd;classClock //時鐘類聲明{public: //外部接口
Clock(int
NewH=0,int
NewM=0,int
NewS=0){ Hour=NewH; Minute=NewM; Second=NewS; } voidShowTime(){cout<<Hour<<":"<<Minute<<":"<<Second<<endl;} Clock&operator++();//前置單目運算符重載
Clockoperator
++(int);//后置單目運算符重載
private: //私有數據成員
int
Hour,Minute,Second;};Clock&Clock::operator++() //前置單目運算符重載函數{ Second++;
if(Second>=60) {Second=Second-60; Minute++;
if(Minute>=60) { Minute=Minute-60; Hour++; Hour=Hour%24; } }return*this;}ClockClock::operator++(int) //后置單目運算符重載{ Clockold=*this;++(*this);
returnold;}voidmain(){ ClockmyClock(23,59,59);
cout<<"Firsttimeoutput:";
myClock.ShowTime();
cout<<"ShowmyClock++:"
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 提高認知裁判員試題及答案
- 2024年籃球裁判員考試實踐部分的注意事項 試題及答案
- 學校冬季傳染病防控課件
- 農作物種子繁育員考試全景分析試題及答案
- 2020年11月中央機關遴選公務員筆試題B卷真題試卷答案解析
- 電刀的安全使用課件
- 2024年籃球裁判員復習工作的重要環節分析 試題及答案
- 植保員職業發展方向與前景試題及答案
- 2024年游泳救生員考試改革試題及答案
- 提高通過率2024年體育經紀人試題及答案
- (正式版)JBT 14581-2024 閥門用彈簧蓄能密封圈
- (2024年)傳染病培訓課件
- 中職學校招生介紹課件
- 新能源業務開發培訓課件
- 初升高物理暑假銜接班課程
- 中建EPC工程總承包項目全過程風險清單(2023年)
- 展會搭建活動方案
- 拼音拼讀音節帶聲調完全版
- 森林生態系統的結構和功能
- 交通運輸的創新技術與應用
- 國家4A級旅游景區評定標準(詳)
評論
0/150
提交評論