




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
第9章多態(tài)性制作人:楊進才沈顯君C++語言程序設(shè)計教程第9章多態(tài)性第9章多態(tài)性學(xué)習(xí)目標(biāo)
掌握多態(tài)性的概念;掌握運算符的重載規(guī)則,會重載常用的運算符;掌握用虛函數(shù)實現(xiàn)動態(tài)聯(lián)編;理解靜態(tài)多態(tài)性與動態(tài)多態(tài)性的區(qū)別與實現(xiàn)機制掌握抽象類的概念與設(shè)計方法。C++語言程序設(shè)計教程第9章多態(tài)性C++語言程序設(shè)計教程第9章多態(tài)性9.1多態(tài)性概述
多態(tài)性(polymorphism)是面向?qū)ο蟪绦蛟O(shè)計的重要特性之一。多態(tài)是指同樣的消息被不同類型的對象接收時導(dǎo)致完全不同的行為。所謂消息是指對類的成員函數(shù)的調(diào)用,不同的行為是指不同的實現(xiàn),也就是調(diào)用了不同的函數(shù)。運算符的使用,就是多態(tài)的一種體現(xiàn)。從實現(xiàn)的角度來看,多態(tài)可以劃分為兩類:編譯時的多態(tài)和運行時的多態(tài)。前者是在編譯的過程中確定了同名操作的具體操作對象,而后者則是在程序運行過程中才動態(tài)地確定操作所針對的具體對象。這種確定操作的具體對象的過程就是聯(lián)編(binding),也稱為綁定。
聯(lián)編可以在編譯和連接時進行,稱為靜態(tài)聯(lián)編。在編譯、連接過程中,系統(tǒng)就可以根據(jù)類型匹配等特征確定程序中操作調(diào)用與執(zhí)行該操作的代碼的關(guān)系,即確定了某一個同名標(biāo)識到底是要調(diào)用哪一段程序代碼,函數(shù)的重載、函數(shù)模板的實例化均屬于靜態(tài)聯(lián)編。聯(lián)編也可以在運行時進行,稱為動態(tài)聯(lián)編。在編譯、連接過程中無法解決的聯(lián)編問題,要等到程序開始運行之后再來確定。C++語言程序設(shè)計教程第9章多態(tài)性9.2運算符重載
使同一個運算符作用與不同類型的數(shù)據(jù)時導(dǎo)致不同的行為的這種機制稱為運算符重載。9.2.1運算符重載機制C++編譯器在對運算符進行編譯處理時,將一個運算符編譯成如下形式:一元運算符:
@obj
編譯成
operator@(obj)二元運算符:
obj1@obj2
編譯成
operator@(obj1,obj2)
其中,關(guān)鍵字operator加上運算符名的函數(shù)稱為運算符函數(shù)
由于C++中有前置++、--,后置++、--,為了區(qū)分它們,C++將后置++、--編譯成:
后置++:obj++編譯成operator++(obj,0)
后置--:obj--編譯成operator--(obj,0)C++語言程序設(shè)計教程第9章多態(tài)性9.2.2運算符重載規(guī)則
1.可重載的運算符
C++中的運算符除以下五個運算符之外,其余全部可以被重載。
.成員選擇運算符
.*
成員指針運算符::作用域分辨符
?:三目選擇運算符
sizeof
計算數(shù)據(jù)大小運算符
2.運算符的重載規(guī)則
(1)重載后運算符的優(yōu)先級與結(jié)合性不會改變。
(2)不能改變原運算符操作數(shù)的個數(shù)。
(3)不能重載C++中沒有的運算符。
(4)不能改變運算符的原有語義。C++語言程序設(shè)計教程第9章多態(tài)性9.2.3重載為類的友元函數(shù)
運算符之所以要重載為類的友元函數(shù),是因為這樣可以自由地訪問該類的任何數(shù)據(jù)成員。將運算符重載為類的友元函數(shù)要在類中使用friend關(guān)鍵字來聲明該運算符函數(shù)為友元函數(shù)。在類中定義友元函數(shù)的格式如下:
【例9-1】重載運算符為類的友元函數(shù)進行復(fù)數(shù)類數(shù)據(jù)運算
分析:C++的算術(shù)運算符沒有提供復(fù)數(shù)運算的功能,使用(a,b)表示一個復(fù)數(shù)a+bi,其中a為實部,b為虛部。加、減法運算規(guī)則為:
(a,b)+(c,d)=(a+c,c+d);(a,b)-(c,d)=(a-c,b-d);取負(fù)運算規(guī)則定義為:-(a,b)=(-a,-b);++運算規(guī)則定義為:(a,b)++=(a+1,b)當(dāng)運算符重載為類的友元函數(shù)時,函數(shù)參數(shù)個數(shù)與運算符原操作數(shù)個數(shù)相同。friend函數(shù)類型operator運算符(形參表)
{
函數(shù)體;
}重載為友元函數(shù)時,友元函數(shù)對某個對象的數(shù)據(jù)進行操作,就必須通過該對象的名稱來進行,函數(shù)中用到的數(shù)據(jù),包括對象,均通過參數(shù)表傳遞。對于雙目運算符,如果它的一個操作數(shù)為類A的對象,就可以將其重載為類A的友元函數(shù),該函數(shù)有兩個形參,其中一個形參的類型是類A。C++語言程序設(shè)計教程第9章多態(tài)性9.2.3重載為類的友元函數(shù)12345678910111213141516171819202122232425262728293031/********************************************p9_1.cpp**重載+、-、++為類的友元函數(shù),進行復(fù)數(shù)運算********************************************/#include<iostream>//usingnamespacestd;usingstd::cout;usingstd::endl;classComplex
//復(fù)數(shù)類定義{private: doublereal; //復(fù)數(shù)實部
doubleimage; //復(fù)數(shù)虛部public:
Complex(doublereal=0.0,doubleimage=0.0)//構(gòu)造函數(shù)
{ this->real=real,this->image=image; }
voiddisplay() { cout<<"("<<real<<","<<image<<")"<<endl; }
friendComplexoperator+(ComplexA,ComplexB)//重載+為友元函數(shù)
{//重載運算符+的函數(shù)實現(xiàn)
returnComplex(A.real+B.real,A.image+B.image); } friendComplexoperator-(ComplexA,ComplexB);//重載-為友元函數(shù)
friendComplexoperator-(ComplexA); //重載-(取負(fù))為友元函數(shù)
friendComplexoperator++(Complex&A);//重載前置++為友元函數(shù)
friendComplexoperator++(Complex&A,int);//重載后置++為友元函數(shù)}; C++語言程序設(shè)計教程第9章多態(tài)性9.2.3重載為類的友元函數(shù)32333435363738394041424344454647484950515253545556575859606162636465Complexoperator-(ComplexplexB) //重載運算符-的函數(shù)實現(xiàn){ returnComplex(A.real-B.real,A.image-B.image);}Complexoperator-(ComplexA) //重載運算符-(取負(fù))的函數(shù)實現(xiàn){ returnComplex(-A.real,-A.image);}Complexoperator++(Complex&A) //重載運算符前置++的函數(shù)實現(xiàn){ returnComplex(++A.real,A.image);}Complexoperator++(Complex&A,int) //重載運算符后置++的函數(shù)實現(xiàn){ returnComplex(A.real++,A.image);}intmain() { ComplexA(100.0,200.0),B(-10.0,20.0),C; cout<<"A=",A.display(); cout<<"B=",B.display(); C=A+B;//使用重載運算符完成復(fù)數(shù)加法 cout<<"C=A+B=",C.display();C=A-B; //使用重載運算符完成復(fù)數(shù)減法 cout<<"C=A-B=",C.display();C=-A+B;cout<<"C=-A+B=",C.display(); C=A++; cout<<"C=A++,C=",C.display();C=++A;cout<<"C=++A,C=",C.display(); C=A+5; C.display();return0;}運行結(jié)果:A=(100,200)
B=(-10,20)
C=A+B=(90,220)
C=A-B=(110,0)
C=-A+B=(-110,-180)
C=A++,C=(100,200)
C=++A,C=(102,200)
(107,200)C++語言程序設(shè)計教程第9章多態(tài)性9.2.3重載為類的友元函數(shù)對于運算符重載為類的友元函數(shù),VC++6.0不允許在聲明重載運算符之前使用usingnamespacestd;所以,分別列出對cout,endl的使用:
usingstd::cout;usingstd::endl;如果仍然使用usingnamespacestd,需下載安裝MicrosoftVisualStudio6.0ServicePack5。編譯時,運算式中的運算符加操作數(shù)形式編譯成運算符函數(shù)形式,操作數(shù)成了運算符函數(shù)的實參。對整型數(shù)、浮點數(shù)等基本類型數(shù)據(jù)的運算,調(diào)用系統(tǒng)提供的運算符函數(shù),執(zhí)行原有的功能;對于復(fù)數(shù)類數(shù)據(jù),調(diào)用新增的運算符函數(shù),執(zhí)行新的功能。程序在運行C=A++時,由于A++編譯成運算符函數(shù)調(diào)用形式:operator++(A,0);A作為實參以傳值的方式傳進operator++(Complex,0)中,函數(shù)體內(nèi)對A的改變不會影響到實參A,所以執(zhí)行A++后,A的值仍然為(100,200)。
程序解釋:
C++語言程序設(shè)計教程第9章多態(tài)性9.2.4重載為類的成員函數(shù)類名是要重載該運算符的類,如果在類中定義運算符函數(shù),類名與作用域分辨符可以省略;。operator與運算符構(gòu)成運算符函數(shù)名;當(dāng)運算符重載為類的成員函數(shù)時,函數(shù)的參數(shù)個數(shù)將比原來的操作數(shù)個數(shù)要少一個;原因是通過對象調(diào)用該運算符函數(shù)時,對象本身充當(dāng)了運算符函數(shù)最左邊的操作數(shù),少了的操作數(shù)就是該對象本身。因此:
①雙目運算符重載為類的成員函數(shù)時,函數(shù)只顯式說明一個參數(shù),該形參是運算符的右操作數(shù)。
②前置單目運算符重載為類的成員函數(shù)時,不需要顯式說明參數(shù),即函數(shù)沒有形參。
③后置單目運算符重載為類的成員函數(shù)時,為了與前置單目運算符區(qū)別,函數(shù)要帶有一個整型形參。
將運算符函數(shù)重載為類的成員函數(shù),這樣運算函數(shù)可以自由地訪問本類的數(shù)據(jù)成員。重載運算符函數(shù)為類的成員函數(shù)語法形式為:返回類型類名::operator運算符(形參表){
函數(shù)體;}C++語言程序設(shè)計教程第9章多態(tài)性9.2.4重載為類的成員函數(shù)【例9-2】重載運算符進行復(fù)數(shù)類數(shù)據(jù)運算123456789101112131415161718192021222324252627/********************************************p9_2.cpp**重載+、-、++為類的成員函數(shù),進行復(fù)數(shù)運算********************************************/#include<iostream>usingnamespacestd;classComplex
//復(fù)數(shù)類定義{private: doublereal; //復(fù)數(shù)實部
doubleimage; //復(fù)數(shù)虛部public:
Complex(doublereal=0.0,doubleimage=0.0)//構(gòu)造函數(shù)
{
this->real=real,this->image=image;}
voiddisplay(){ cout<<"("<<real<<","<<image<<")"<<endl;}Complexoperator+(ComplexB);
//運算符+重載成員函數(shù)
Complexoperator-(ComplexB); //運算符-重載成員函數(shù)
Complexoperator-(); //運算符-(取負(fù))重載成員函數(shù)
Complexoperator++();
//前置++重載成員函數(shù)
Complexoperator++(int);
//后置++重載成員函數(shù)};
C++語言程序設(shè)計教程第9章多態(tài)性9.2.4重載為類的成員函數(shù)29303132333435363738394041424344454647484951525354555657585960616263646566ComplexComplex::operator+(ComplexB) //重載運算符+的函數(shù)實現(xiàn){returnComplex(real+B.real,image+B.image);//創(chuàng)建一個臨時對象作為返回值}ComplexComplex::operator-(ComplexB) //重載運算符-的函數(shù)實現(xiàn){returnComplex(real-B.real,image-B.image);}ComplexComplex::operator-() //重載運算符-(取負(fù))的函數(shù)實現(xiàn){returnComplex(-real,-image);}ComplexComplex::operator++() //重載運算符前置++的函數(shù)實現(xiàn){returnComplex(++real,image);}ComplexComplex::operator++(int) //重載運算符后置++的函數(shù)實現(xiàn){returnComplex(real++,image);}intmain(){ ComplexA(100.0,200.0),B(-10.0,20.0),C; cout<<"A=",A.display(); cout<<"B=",B.display(); C=A+B;//使用重載運算符完成復(fù)數(shù)加法
cout<<"C=A+B=",C.display();C=A-B; //使用重載運算符完成復(fù)數(shù)減法
cout<<"C=A-B=",C.display();C=-A+B;cout<<"C=-A+B=",C.display(); C=A++; cout<<"C=A++,C=",C.display();C=++A;cout<<"C=++A,C=",C.display(); C=A+5; C.display();return0;}A=(100,200)
B=(-10,20)
C=A+B=(90,220)
C=A-B=(110,180)
C=-A+B=(-110,-180)
C=A++,C=(100,200)
C=++A,C=(102,200)
(107,200)運行結(jié)果C++語言程序設(shè)計教程第9章多態(tài)性9.2.4重載為類的成員函數(shù)程序中,除了在運算符函數(shù)定義及實現(xiàn)的時候使用了關(guān)鍵字operator與運算符外,運算符成員函數(shù)與類的普通成員函數(shù)沒有什么區(qū)別。本例中重載的+、-函數(shù)中,在返回復(fù)數(shù)對象時,均創(chuàng)建了一個臨時的對象作為返回值:returnComplex(real+B.real,image+B.image);也可以按以下形式返回函數(shù)值:Complexcomplex::operator+(ComplexB)//重載運算符重載函數(shù)實現(xiàn){ComplexC(real+B.real,imag+B.imag);
returnC;}這兩種方法的執(zhí)行效率是完全不同的。后者的執(zhí)行過程是這樣的:創(chuàng)建一個局部對象C(這時會調(diào)用構(gòu)造函數(shù)),執(zhí)行return語句時會調(diào)用拷貝構(gòu)造函數(shù),將C的值拷貝到主調(diào)函數(shù)中的一個無名臨時對象中。當(dāng)函數(shù)operator+()調(diào)用結(jié)束時,會調(diào)用析構(gòu)函數(shù)析構(gòu)對象C。前一種方法的效率就高得多了,直接將一個無名臨時對象創(chuàng)建到主調(diào)函數(shù)中。執(zhí)行程序64行C=A+5時,C=A+5編譯成調(diào)用形式C=A.operator+(5),將5當(dāng)成復(fù)數(shù)(5,0)與復(fù)數(shù)對象A相加。
程序解釋:
C++語言程序設(shè)計教程第9章多態(tài)性9.2.4重載為類的成員函數(shù)
在多數(shù)情況下,既可將運算符重載為類的成員函數(shù),也可以重載為類的友元函數(shù)。但成員函數(shù)運算符與友元函數(shù)運算符也具有各自的一些特點:
(1)
一般情況下,單目運算符最好重載為類的成員函數(shù);雙目運算符則最好重載為類的友元函數(shù)。
(2)
一些雙目運算符不能重載為類的友元函數(shù):=、()、[]、->。
(3)
類型轉(zhuǎn)換函數(shù)只能定義為一個類的成員函數(shù)而不能定義為類的友元函數(shù)。
(4)
若一個運算符的操作需要修改對象的狀態(tài),選擇重載為成員函數(shù)較好。
(5)
若運算符所需的操作數(shù)(尤其是第一個操作數(shù))希望有隱式類型轉(zhuǎn)換,則只能選用友元函數(shù)。
(6)
當(dāng)運算符函數(shù)是一個成員函數(shù)時,最左邊的操作數(shù)(或者只有最左邊的操作數(shù))必須是運算符類的一個類對象(或者是對該類對象的引用)。如果左邊的操作數(shù)必須是一個不同類的對象,或者是一個基本數(shù)據(jù)類型的對象,該運算符函數(shù)必須作為一個友元函數(shù)來實現(xiàn)。
(7)
當(dāng)需要重載運算符的運算具有可交換性時,選擇重載為友元函數(shù)。兩種重載形式的比較
:
C++語言程序設(shè)計教程第9章多態(tài)性9.2.5典型運算符重載
【例9-3】重載運算符=為類的成員函數(shù)進行復(fù)數(shù)類數(shù)據(jù)賦值
分析:=是一個二元運算符,A=B被編譯成operator=(A,B),其中A應(yīng)是一個左值。
1.重載復(fù)數(shù)賦值=運算
123456789101112131415161718192021222324/********************************************p9_3.cpp**重載=為類的成員函數(shù),進行復(fù)數(shù)賦值********************************************/#include<iostream>usingnamespacestd;classComplex
//復(fù)數(shù)類定義{private: doublereal; //復(fù)數(shù)實部
doubleimage; //復(fù)數(shù)虛部public:
Complex(doublereal=0.0,doubleimage=0.0)//構(gòu)造函數(shù)
{
this->real=real,this->image=image; }
voiddisplay() { cout<<"("<<real<<","<<image<<")"<<endl; } Complexoperator+(ComplexB);
//運算符+重載成員函數(shù)
Complexoperator=(ComplexB);
//運算符=重載成員函數(shù)
}; C++語言程序設(shè)計教程第9章多態(tài)性252627282930313233343536373839404142434445ComplexComplex::operator+(ComplexB) //重載運算符+的函數(shù)實現(xiàn){ returnComplex(real+B.real,image+B.image);//創(chuàng)建一個臨時對象作為返回值}ComplexComplex::operator
=(ComplexB) //重載運算符+的函數(shù)實現(xiàn){ real=B.real,image=B.image; cout<<"operator=calling..."<<endl; return*this;//returnComplex(real,image);}intmain() { ComplexA(100.0,200.0),B(-10.0,20.0),C; cout<<"A=",A.display(); cout<<"B=",B.display(); C=A+B;//使用重載運算符完成復(fù)數(shù)加法
cout<<"C=A+B=",C.display();C=A; cout<<"C=A=",C.display();return0;}
1.重載復(fù)數(shù)賦值=運算
A=(100,200)
B=(-10,20)
operator=calling...
C=A+B=(90,220)
operator=calling...
C=A=(100,200)運行結(jié)果:C++語言程序設(shè)計教程第9章多態(tài)性
2.重載成員指針運算符->
重載成員指針運算符,這樣能確保指向類對象的指針總是指向某個有意義的對象(有效內(nèi)存地址),即創(chuàng)建一個指向?qū)ο蟮摹爸悄苤羔槨?smartpointer),否則就返回錯誤信息。這樣避免了對空指針或垃圾指針(garbagepointer)內(nèi)容的存取。
【例9-4】重載成員指針運算符->為類的成員函數(shù)
分析:成員指針運算符->的操作對象有兩個,左邊是一個對象指針,右邊是對象的成員。由于右邊的對象成員的類型不確定,因此,->只能作為一元運算符重載12345678910111213141516171819202122/********************************************p9_4.cpp**重載->為類的成員函數(shù)********************************************/#include<iostream>usingnamespacestd;classComplex //復(fù)數(shù)類定義{private: doublereal; //復(fù)數(shù)實部
doubleimage; //復(fù)數(shù)虛部public:
Complex(doublereal=0.0,doubleimage=0.0)//構(gòu)造函數(shù)
{this->real=real,this->image=image;}
voiddisplay(){cout<<"("<<real<<","<<image<<")"<<endl;}}; C++語言程序設(shè)計教程第9章多態(tài)性(0,0)
(100,200)運行結(jié)果:23242526272829303132333435363738394041424344454647484950classplex//復(fù)數(shù)指針類定義{private: Complex*PC;public:plex(Complex*PC=NULL){this->PC=PC;}Complex*operator->(){staticComplexplex(0,0);//避免指針為空if(PC==NULL){return&plex;}returnPC;}};intmain() {plexP1;//指針未初始化P1->display();//顯示預(yù)先定義的(0,0)ComplexC1(100,200);P1=&C1;//指針未初始化P1->display();//顯示有效數(shù)據(jù)return0;}2.重載成員指針運算符->
C++語言程序設(shè)計教程第9章多態(tài)性
3.重載下標(biāo)運算符[]
【例9-5】重載下標(biāo)運算符[]為類的成員函數(shù)
分析:下標(biāo)運算符[]的操作對象有兩個,左邊是一個對象指針,[]中間是一個作為下標(biāo)的整型數(shù)。因此,[]作為二元運算符重載。下面重載下標(biāo)運算符,使之能夠按下標(biāo)訪問字符串中指定位置字符2567891011121314151617181920212223242526272829//p9_5.cpp#include<iostream>usingnamespacestd;classString
{private:char*Str; intlen;public:
voidShowStr(){ cout<<"string:"<<Str<<",length:"<<len<<endl;}
String(constchar*p=NULL){ if(p) {len=strlen(p); Str=newchar[len+1]; strcpy(Str,p); }else {len=0;Str=NULL; }}C++語言程序設(shè)計教程第9章多態(tài)性string:abcdef,length:16
S1[10]=A
string:Abcdef,length:16
S2[10]=K運行結(jié)果:3.重載下標(biāo)運算符[]30313233343536373839404142434445464748495051525354~String(){if(Str!=NULL) delete[]Str;}char&operator[](intn)//重載運算符[],處理String對象{return*(Str+n);}constchar&operator[](intn)const//重載運算符[],處理constString對象{return*(Str+n);}};intmain(void){StringS1("abcdef");S1.ShowStr();S1[10]='A';cout<<"afterS1[10]=A"<<endl;S1.ShowStr();constStringS2("ABCDEFGHIJKLMN");cout<<"S2[10]="<<S2[10]<<endl;return0;}C++語言程序設(shè)計教程第9章多態(tài)性9.3虛函數(shù)
9.3.1
靜態(tài)聯(lián)編與動態(tài)聯(lián)編調(diào)用重載函數(shù)時,編譯器根據(jù)調(diào)用時參數(shù)的類型與個數(shù)在編譯時實現(xiàn)靜態(tài)聯(lián)編,將調(diào)用體與函數(shù)綁定。靜態(tài)聯(lián)編支持的多態(tài)性也稱為編譯時的多態(tài)性,或靜態(tài)多態(tài)性。
【例9-6】演示靜態(tài)多態(tài)性123456789101112131415161718192021/*********************************************p9_6.cpp**演示靜態(tài)多態(tài)性*********************************************/#include<iostream>usingnamespacestd;classPoint{private: intX,Y;public:
Point(intX=0,intY=0) {
this->X=X,this->Y=Y; }
doublearea()//求面積
{ return0.0;}};constdoublePI=3.14159;C++語言程序設(shè)計教程第9章多態(tài)性9.3.1靜態(tài)聯(lián)編與動態(tài)聯(lián)編222324252627282930313233343536373839404142434445464748classCircle:publicPoint{private: doubleradius;//半徑public:
Circle(intX,intY,doubleR):Point(X,Y) { radius=R; }
doublearea()//求面積
{ returnPI*radius*radius;}};intmain(){PointP1(10,10);cout<<"P1.area()="<<P1.area()<<endl;CircleC1(10,10,20);cout<<"C1.area()="<<C1.area()<<endl;Point*Pp;Pp=&C1;cout<<"Pp->area()="<<Pp->area()<<endl;Point&Rp=C1;cout<<"Rp.area()="<<Rp.area()<<endl;return0;}運行結(jié)果:P1.area()=0
C1.area()=1256.64
Pp->area()=0
Rp.area()=0C++語言程序設(shè)計教程第9章多態(tài)性9.3.1靜態(tài)聯(lián)編與動態(tài)聯(lián)編程序解釋:程序39行調(diào)用P1.area()顯示了Point類對象P1的面積;程序41行通過調(diào)用C1.area()顯示了Circle類對象C1的面積;由于在類Circle中重新定義了area(),它覆蓋了基類的area(),故通過C1.area()調(diào)用的是類Circle中的area(),返回了正確結(jié)果。依照類的兼容性,程序43行用一個point型的指針指向了Circle類的對象C1,第44行通過Pp->area()調(diào)用area(),那么究竟調(diào)用的是哪個area(),C++此時實行靜態(tài)聯(lián)編,根據(jù)Pp的類型為Point型,將從Point類中繼承來的area()綁定給Pp,因此,此時調(diào)用的是Point派生的area(),顯示的結(jié)果為0。同樣,在第46行由引用Rp調(diào)用area()時,也進行靜態(tài)聯(lián)編,調(diào)用的是Point派生的area(),顯示的結(jié)果為0。顯然,靜態(tài)聯(lián)編盲目根據(jù)指針和引用的類型而不是根據(jù)實際指向的目標(biāo)確定調(diào)用的函數(shù),導(dǎo)致了錯誤。動態(tài)聯(lián)編則在程序運行的過程中,根據(jù)指針與引用實際指向的目標(biāo)調(diào)用對應(yīng)的函數(shù),也就是在程序運行時才決定如何動作。虛函數(shù)(virtualfunction)允許函數(shù)調(diào)用與函數(shù)體之間的聯(lián)系在運行時才建立,是實現(xiàn)動態(tài)聯(lián)編的基礎(chǔ)。虛函數(shù)經(jīng)過派生之后,可以在類族中實現(xiàn)運行時的多態(tài),充分體現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計的動態(tài)多態(tài)性。C++語言程序設(shè)計教程第9章多態(tài)性9.3.2虛函數(shù)的定義與使用虛函數(shù)定義的一般語法形式如下:virtual函數(shù)類型函數(shù)表(形參表)
{
函數(shù)體;}
其中:virtual關(guān)鍵字說明該成員函數(shù)為虛函數(shù)。在定義虛函數(shù)時要注意:
(1)
虛函數(shù)不能是靜態(tài)成員函數(shù),也不能是友元函數(shù)。因為靜態(tài)成員函數(shù)和友元函數(shù)不屬于某個對象。
(2)
內(nèi)聯(lián)函數(shù)是不能在運行中動態(tài)確定其位置的,即使虛函數(shù)在類的內(nèi)部定義,編譯時,仍將其看作非內(nèi)聯(lián)的。
(3)
只有類的成員函數(shù)才能說明為虛函數(shù),虛函數(shù)的聲明只能出現(xiàn)在類的定義中。因為虛函數(shù)僅適用于有繼承關(guān)系的類對象,普通函數(shù)不能說明為虛函數(shù)。
(4)
構(gòu)造函數(shù)不能是虛函數(shù),析構(gòu)函數(shù)可以是虛函數(shù),而且通常聲明為虛函數(shù)。在正常情況下,對虛函數(shù)的訪問與其它成員函數(shù)完全一樣。只有通過指向基類的指針或引用來調(diào)用虛函數(shù)時才體現(xiàn)虛函數(shù)與一般函數(shù)的不同。使用虛函數(shù)是實現(xiàn)動態(tài)聯(lián)編的基礎(chǔ)。要實現(xiàn)動態(tài)聯(lián)編,概括起來需要滿足三個條件:
(1)
應(yīng)滿足類型兼容規(guī)則。
(2)
在基類中定義虛函數(shù),并且在派生類中要重新定義虛函數(shù)。
(3)
要由成員函數(shù)或者是通過指針、引用訪問虛函數(shù)。C++語言程序設(shè)計教程第9章多態(tài)性9.3.2虛函數(shù)的定義與使用【例9-7】演示動態(tài)多態(tài)性123456789101112131415161718192021222324252627282930/*********************************************p9_7.cpp**演示動態(tài)多態(tài)性*********************************************/#include<iostream>usingnamespacestd;classPoint{private:intX,Y;public:
Point(intX=0,intY=0){this->X=X,this->Y=Y;}
virtualdoublearea()//求面積
{return0.0;}};constdoublePI=3.14159;classCircle:publicPoint{private:doubleradius;//半徑public:
Circle(intX,intY,doubleR):Point(X,Y){ radius=R;}C++語言程序設(shè)計教程第9章多態(tài)性9.3.2虛函數(shù)的定義與使用313233343536373839404142434445464748doublearea()//求面積{returnPI*radius*radius;}};intmain(){PointP1(10,10);cout<<"P1.area()="<<P1.area()<<endl;CircleC1(10,10,20);cout<<"C1.area()="<<C1.area()<<endl;Point*Pp;Pp=&C1;cout<<"Pp->area()="<<Pp->area()<<endl;Point&Rp=C1;cout<<"Rp.area()="<<Rp.area()<<endl;return0;}運行結(jié)果:P1.area()=0
C1.area()=1256.64
Pp->area()=1256.64
Rp.area()=1256.64
程序解釋:
程序16行在基類Point中,將area()聲明為虛函數(shù)。第39通過Point類對象P1調(diào)用虛函數(shù)area()。第41通過circle類對象C1調(diào)用area(),實現(xiàn)靜態(tài)聯(lián)編,調(diào)用的是Circle類area()。第44、46行分別通過Point類型指針與引用調(diào)用area(),由于area()為虛函數(shù),此時進行動態(tài)聯(lián)編,調(diào)用的是實際指向?qū)ο蟮腶rea()。C++語言程序設(shè)計教程第9章多態(tài)性9.3.2虛函數(shù)的定義與使用虛函數(shù)名為“虛”其實不虛:
①虛函數(shù)有函數(shù)體;
②虛函數(shù)在靜態(tài)聯(lián)編是當(dāng)成一般成員函數(shù)使用;③虛函數(shù)可以派生,如果在派生類中沒有重新定義虛函數(shù),虛函數(shù)就充當(dāng)了派生類的虛函數(shù)。
例①:將p9_7.cpp中Circle類的doublearea()改為:doublearea(inti){
returnPI*radius*radius;}
在Circle中,virtualdoublearea()被doublearea(inti)覆蓋,下面調(diào)用形式是錯誤的:
CircleC1;
cout<<"C1.area()="<<C1.area()<<endl;Circle*Pp1=&C1;cout<<"Pp1->area()="<<Pp1->area()<<endl;注意:當(dāng)在派生類中定義了虛函數(shù)的重載函數(shù),但并沒有重新定義虛函數(shù)時,與虛函數(shù)同名的重載函數(shù)覆蓋了派生類中的虛函數(shù)。此時試圖通過派生類對象、指針、引用調(diào)用派生類的虛函數(shù)就會產(chǎn)生錯誤。當(dāng)在派生類中未重新定義虛函數(shù),雖然虛函數(shù)被派生類繼承,但通過基類、派生類類型指針、引用調(diào)用虛函數(shù)時,不實現(xiàn)動態(tài)聯(lián)編,調(diào)用的是基類的虛函數(shù)。C++語言程序設(shè)計教程第9章多態(tài)性9.3.2虛函數(shù)的定義與使用例②:在p9_7.cpp的Circle類中加入:
doublearea(inti){ returnPI*radius*radius;}
程序運行結(jié)果與p9_7.cpp結(jié)果相同。例③:在p9_7.cpp的Circle類中去掉函數(shù)doublearea(),程序運行的結(jié)果如下:P1.area()=0C1.area()=0//調(diào)用了基類派生的virtualdoublearea()Pp->area()=0//調(diào)用了基類的virtualdoublearea()Rp.area()=0//調(diào)用了基類的virtualdoublearea()C++語言程序設(shè)計教程第9章多態(tài)性9.3.3虛析構(gòu)函數(shù)
在C++中,不能定義虛構(gòu)造函數(shù),因為當(dāng)開始調(diào)用構(gòu)造函數(shù)時,對象還未完成實例化,只有在構(gòu)造完成后,對象才能成為一個類的名副其實的對象;但析構(gòu)函數(shù)可以是虛函數(shù),而且通常聲明為虛函數(shù),即虛析構(gòu)函數(shù)。
虛析構(gòu)函數(shù)定義形式如下:
virtual
~類名();當(dāng)基類的析構(gòu)函數(shù)被聲明為虛函數(shù),則派生類的析構(gòu)函數(shù),無論是否使用virtual關(guān)鍵字進行聲明,都自動成為虛函數(shù)。析構(gòu)函數(shù)聲明為虛函數(shù)后,程序運行時采用動態(tài)聯(lián)編,因此可以確保使用基類類型的指針就能夠自動調(diào)用適當(dāng)?shù)奈鰳?gòu)函數(shù)對不同對象進行清理工作。當(dāng)使用delete運算符刪除一個對象時,隱含著對析構(gòu)函數(shù)的一次調(diào)用,如果析構(gòu)函數(shù)為虛函數(shù),則這個調(diào)用采用動態(tài)聯(lián)編,保證析構(gòu)函數(shù)被正確執(zhí)行?!纠?-8】用虛析構(gòu)函數(shù)刪除派生類動態(tài)對象C++語言程序設(shè)計教程第9章多態(tài)性9.3.3虛析構(gòu)函數(shù)278910111213141516171819202122232425262728293031323334353637383940//**p9_8.cpp**classA{public:
virtual~A()//虛析構(gòu)函數(shù)
{cout<<"A::~A()iscalled."<<endl;}
A(){cout<<"A::A()iscalled."<<endl;}};classB:publicA//派生類{private:int*ip;public:
B(intsize=0){ ip=newint[size];cout<<"B::B()iscalled."<<endl;}
~B(){cout<<"B::~B()iscalled."<<endl;
delete[]ip;}};intmain(){A*b=newB(10);//類型兼容
deleteb;return0;}運行結(jié)果:A::A()iscalled.
B::B()iscalled.
B::~B()iscalled.
A::~A()iscalled.程序解釋:
由于定義基類的析構(gòu)函數(shù)是虛析構(gòu)函數(shù),所以當(dāng)程序運行結(jié)束時,通過基類指針刪除派生類對象時,先調(diào)用派生類析構(gòu)函數(shù),然后調(diào)用基類的析構(gòu)函數(shù)。如果基類的析構(gòu)函數(shù)不是虛析構(gòu)函數(shù),則程序運行結(jié)果如下:A::A()iscalled.
B::B()iscalled.
A::~A()iscalled.C++語言程序設(shè)計教程第9章多態(tài)性9.4
抽象類
抽象類是一種特殊的類,是為了抽象的目的而建立的,建立抽象類,就是為了通過它多態(tài)地使用其中的成員函數(shù),為一個類族提供統(tǒng)一的操作界面。抽象類處于類層次的上層,一個抽象類自身無法實例化,也就是說我們無法聲明一個抽象類的對象,而只能通過繼承機制,生成抽象類的非抽象派生類,然后再實例化。9.4.1純虛函數(shù)
純虛函數(shù)(purevirtualfunction)是一個在基類中說明的虛函數(shù),它在該基類中沒有定義具體實現(xiàn),要求各派生類根據(jù)實際需要定義函數(shù)實現(xiàn)。純虛函數(shù)的作用是為派生類提供一個一致的接口。
純虛函數(shù)定義的形式為:virtual函數(shù)類型函數(shù)名(參數(shù)表)=0;
實際上,它與一般虛函數(shù)成員的原型在書寫格式上的不同就在于后面加了=0。C++中,有一種函數(shù)體為空的空虛函數(shù),它與純虛函數(shù)的區(qū)別為:
①純虛函數(shù)根本就沒有函數(shù)體,而空虛函數(shù)的函數(shù)體為空。
②純虛函數(shù)所在的類是抽象類,不能直接進行實例化,空虛函數(shù)所在的類是可以實例化的。
③它們共同的特點是都可以派生出新的類,然后在新類中給出新的虛函數(shù)的實現(xiàn),而且這種新的實現(xiàn)可以具有多態(tài)特征。C++語言程序設(shè)計教程第9章多態(tài)性9.4.2
抽象類與具體類抽象類具有下述一些特點:
(1)
抽象類只能作為其它類的基類使用,抽象類不能定義對象,純虛函數(shù)的實現(xiàn)由派生類給出;
(2)
派生類仍可不給出所有基類中純虛函數(shù)的定義,繼續(xù)作為抽象類;如果派生類給出所有純虛函數(shù)的實現(xiàn),派生類就不再是抽象類而是一個具體類,就可以定義對象。
(3)
抽象類不能用作參數(shù)類型、函數(shù)返回值或強制類型轉(zhuǎn)換。
(4)
可以定義一個抽象類的指針和引用。通過抽象類的指針和引用,可以指向并訪問各派生類成員,這種訪問是具有多態(tài)特征的?!纠?-9】抽象類的使用前面,在學(xué)習(xí)多重繼承時,我們定義了點(Point)、圓(Circle)、圓柱體(Cylinder)三者的關(guān)系,點派生出圓、圓派生出圓柱體,但無論是點、圓、還是圓柱體,它們都具有一定外形,屬于一種形狀(Shape)。因此,我們可以對前面的示例加以豐富和修改,定義一個抽象類Shape,由抽象類Shape派生出類Point,類Point派生出類Circle,類Circle進一步派生出類Cylinder,…等等,最終形成一個類族。C++語言程序設(shè)計教程第9章多態(tài)性【例9-9】抽象類的使用278910111213141516171819202122232425262728293031323334353637384142//p9_9.cpp*classShape
溫馨提示
- 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)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 湖北生態(tài)溫室施工方案
- 鐵嶺水源井施工方案
- 鋼渣施工方案
- 預(yù)制水磨石樓地面施工方案
- 六安鋼纖維窨井蓋施工方案
- 綠化種植工程施工方案
- 云南學(xué)校草坪施工方案
- 青浦區(qū)老舊廠房施工方案
- 鋪設(shè)水泥路面基礎(chǔ)施工方案
- 蘇州螺旋風(fēng)管安裝施工方案
- 2025年安徽工貿(mào)職業(yè)技術(shù)學(xué)院單招職業(yè)適應(yīng)性測試題庫(有一套)
- 2025年哈爾濱傳媒職業(yè)學(xué)院單招職業(yè)技能測試題庫完整
- 2025年河南林業(yè)職業(yè)學(xué)院單招職業(yè)技能測試題庫完整版
- 地理-浙江省強基聯(lián)盟2025年2月高三年級聯(lián)考試題和答案
- 糧食儲運與質(zhì)量安全基礎(chǔ)知識單選題100道及答案
- (必刷)湖南省醫(yī)學(xué)院校高職單招職業(yè)技能測試必會題庫(含往年真題)
- 第一篇 專題一 第2講 勻變速直線運動 牛頓運動定律
- 廣東廣州歷年中考語文現(xiàn)代文閱讀之非連續(xù)性文本閱讀7篇(截至2024年)
- 做自己的英雄主題班會
- 《農(nóng)藥學(xué)課程殺菌劑》課件
- 充電樁的建設(shè)合作方案
評論
0/150
提交評論