C++面向對象程序設計教程(二版)講稿_第1頁
C++面向對象程序設計教程(二版)講稿_第2頁
C++面向對象程序設計教程(二版)講稿_第3頁
C++面向對象程序設計教程(二版)講稿_第4頁
已閱讀5頁,還剩79頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

C++面向對象程序設計教程(二版)講稿第5章多態(tài)性(書P162)ー、多態(tài)性概念1、什么是多態(tài)多態(tài)是指同樣的消息被不同類型的對象接收時導致完全不同的行為。或者說,多態(tài)性是指ー個名字可以定義不同的函數(shù),這些函數(shù)執(zhí)行不同功能但又類似的操作。也可以說“一個接口,多種方法”2、多態(tài)實現(xiàn)的分類多態(tài)從實現(xiàn)的角度劃分為兩類:(1)編譯時多態(tài)(靜態(tài)多態(tài)),編譯過程中確定同名操作的具體對象。包括函數(shù)重載和運算符重載兩種。(2)運行時多態(tài)(動態(tài)多態(tài)),程序運行過程オ動態(tài)地確定操作所針對的具體對象。包括虛函數(shù)一種。3、聯(lián)編確定操作的具體對象的過程用聯(lián)與實現(xiàn)(1)定義:聯(lián)編是指計算機程序自身彼此關聯(lián)的過程,也就是把ー個標識符名和一個存儲地址聯(lián)系在ー起的過程。(2)聯(lián)編分類(i)靜態(tài)慶編:聯(lián)編工作在編譯連接階段完成,效率高?!懊謮貉印睍鳳163)是靜態(tài)聯(lián)編的ー種方式,稍后講。(ii)動態(tài)朕編:聯(lián)編工作在運行階段完成,更靈活。二、函數(shù)重載進ー步說明函數(shù)重載是靜態(tài)多態(tài)的典型方式。函數(shù)重載是用同一個函數(shù)名訪問ー組相關操作的函數(shù)。第2章已介紹了用函數(shù)參數(shù)個數(shù)、類型或類型順序不同來定義ー組重載函數(shù)規(guī)則。下面補充在基類和派生類中函數(shù)重載的情況。例5.1(書P163)#include<iostream.h>classpoint 聲明一個基類point(intx,y;public:point(inta,intb) 基類point有2個參數(shù)的構造函數(shù){x=a,y=b;}doubleareaO 基類point的area()函數(shù){return0;});classcircle:publicpoint 定義公有派生類circle,公有繼承pointintradius;public:circle(intx,inty,intrad):point(x,y)/ノ派生類有3個參數(shù)的構造函數(shù),傳遞給基類point構造函數(shù){radius=rad;}doublearea() 派生類circle重新定義的area。函數(shù){return3.1416*radius*radius;});main()(pointp(20,20);〃創(chuàng)建基類對象p,調用基類構造函數(shù)初始化x和ycirclec(8,8,30);創(chuàng)建派生類circle對象c,調用派生類circle構造函數(shù)初始化x和y和radiuscout?p.area()?endl;//執(zhí)行基類point中的area。函數(shù),因p是基類對象,輸出〇cout?c.area()?endl;//執(zhí)行派生類circle中的areaO函數(shù),因c是派生類對象,輸出面積值2827.44cout?c.point::area()?endl;//執(zhí)行基類point中的area。函數(shù),因調用基類名指定的area()函數(shù),輸出0return0;程序的運行結果為:02827.440通過本例得出,在基類和派生類中函數(shù)重載有如下規(guī)則:(1)重載函數(shù)參數(shù)差別,這在前面第2章已講過規(guī)則。(2)重載函數(shù)不帶參數(shù)或參數(shù)完全相同,只是屬于不同類(基類或派生類),這又分成下面調用規(guī)則:(i)使用對象名區(qū)分,例中p.area。就是調用基類的area()函數(shù),而c.area。就是調用派生類的area()函數(shù)。(ii)使用類名加“::”調用例如,point::area() 是調用基類的area()函數(shù)circle::area() 是調用派生類的area()函數(shù)(3)“名字壓延”(書P163)所謂“名字壓延”就是名字壓縮或延伸。這是編譯程序用來區(qū)分重載函數(shù)的ー種方式?!懊謮貉印本褪前阎剌d函數(shù)的函數(shù)名和它的參數(shù)結合起來,采用名字壓縮或延伸方法創(chuàng)造出函數(shù)的新名字,然后在函數(shù)原型和定義及調用處均用新名字替代。于是原來沒有區(qū)別的名字,現(xiàn)在就區(qū)分開來了。例如(書P164)有兩個重載函數(shù)原型:intmyAns(floatx,intj);intmyAns(inti,charc);并用以下調用語句examl=myAns(15.3,15);exam2=myAns(45,'a');利用“名字壓延”方法,在編譯完成之前,相同的函數(shù)名字myAns改成如下:(這僅是示意說明,實際上不同編譯程序處理各不相同)intmyAnsFLTINT(floatx,intj);intmyAnsINTCHAR(inti,charc);顯然利用重載函數(shù)不同類型關鍵字進行壓延,就得到兩個不同的函數(shù)名myAnsFLTINT和myAnsINTCHARo執(zhí)行語句也跟隨修改為:examl=myAnsFLTINT(15.3,15);exam2=myAnsINTCHAR(45,'a');這樣原來相同的函數(shù)名,現(xiàn)在變成不相同了。于是就會依照不同參數(shù)調用相應函數(shù)。這是在編譯階段完成的。三、運算符重載1、什么是運算符重載,為什么運算符還要重載在C++對于常用的標準類型(int、float,double等)C++5章_P5實施通常的運算+、ー、?、人…,是沒有問題的。例如,intc,a=3,b=5;doublex,y=3.5,z=5.4;c=a+b; x=z-y;但如果是ー個復數(shù)類型complex(定義見P165)complexcoml,com2,total;total=coml+com2;若沒有經過定義,C++目前是實現(xiàn)不了的。實際上,運算符是ー種操作,可以看成是一個函數(shù),稱為運算符函數(shù)。在C++運算符函數(shù)表示為operator?(???)〇字符代表某ー個運算符。例如:operator+(…)ヽoperator-(?,,)>operator*(???)>???對于標準類型!nt>float>doub1e等,C++編譯系統(tǒng)已預定義了這些類型的運算符函數(shù)。例如,intoperator+(intx,inty){return(x+y);)doubleoperator—(doublex,doubley)return(y-x);對于3+5系統(tǒng)會調用int類型加法運算符函數(shù)對于5.4-3.5系統(tǒng)會調用double類型減法運算符函數(shù)但對于復數(shù)coml+com2C++編譯系統(tǒng)沒有預定義運算符函數(shù),所以實現(xiàn)不了。只能由用戶自定義復數(shù)類型運算符函數(shù)。一般地用戶自定義的類型,C++系統(tǒng)也不會預定義運算符函數(shù),也要用戶自定義相應的運算符函數(shù)。這就是為什么需要運算符重載(重新定義運算符的功能,使它既適合系統(tǒng)已有的功能,又適合用戶自定義類型運算的功能)。2、運算符重載的實現(xiàn)像系統(tǒng)預定義哪樣,一般的運算符重載也用函數(shù)實現(xiàn),通常按以下方式:(1)定義ー個類(2)類內定義運算符重載函數(shù)(3)或在類內定義運算符重載函數(shù)原型聲明,在類外定義運算符重載函數(shù)體因為用戶自定義類型的運算符重載函數(shù),要求能訪問運算對象的私有成員,所以只能用類的成員函數(shù)或作為友元的外部函數(shù)兩種形式定義運算符重載函數(shù)。3、運算符重載的規(guī)則(1)只能重載C++已有的運算符,不能自行新創(chuàng)運算符,例如“**”,它不是C++已有的運算符,就不能重載。C++5章_P7(2)C++已有的運算符中不能重載的運算符只有5個,它們是:類屬關系運算符成員指針運算符“*”、作用域運算符“::”、條件運算符域:"及“sizeof”運算符。(3)重載之后不能改變運算符原來的優(yōu)先級和結合方向。(4)重載是針對新類型數(shù)據(jù)的實際需要,對原有運算符進行適當?shù)母脑?重載的功能與原功能應相似。不能改變原運算符的操作對象個數(shù),且至少有一個操作對象是自定義類型。4、作為友元的運算符重載函數(shù)(1)一般格式直接在類內定義重載函數(shù)格式:friend函數(shù)返回值類型operator?(形參表){〃函數(shù)體語句)類內聲明重載函數(shù)原型,類外定義重載函數(shù)體格式:class類名(//???friend函數(shù)返回值類型operator?(形參表);原型//-};

函數(shù)返回值類型operator?(形參表)類外定義函數(shù)體{〃函數(shù)體語句)(i) ”代表要重載的某個運算符。(ii)類外定義中,不能用“類名::”這是因為友元函數(shù)不是類的成員函數(shù)。(Hi)由于友元函數(shù)不是類的成員函數(shù),所以沒有this指針,全靠參數(shù)傳遞。若友元函數(shù)重載的是雙目運算符,則參數(shù)表中需兩個形參;若友元函數(shù)重載的是單目運算符,則參數(shù)表中只需ー個形參。(2)雙目運算符重載當用友元函數(shù)重載雙目運算符時,兩個操作數(shù)都要傳遞給運算符函數(shù),以復數(shù)運算符為例,復數(shù)加、減、乘、除運算參考書P169例5.3(書P169)Winclude<iostream.h>classcomplex 聲明一1個基類complexpublic:complex(doubler=0.0,doublei=0.0);//基類complex有2個參數(shù)的構造函數(shù)原型voidprint(); 基類complex的print()函數(shù)原型friendcomplexoperator+(complexa,complexb);〃用友元函數(shù)實現(xiàn)運算符+重載函數(shù)的原型friendcomplexoperator-(complexa,complexb);〃用友元函數(shù)實現(xiàn)運算符一重載函數(shù)的原型friendcomplexoperator*(complexa,complexb);//用友元函數(shù)實現(xiàn)運算符?重載函數(shù)的原型friendcomplexoperator/(complexa,complexb);//用友元函數(shù)實現(xiàn)運算符/重載函數(shù)的原型private:doublereal;doubleimag;);complex::complex(doubler,doublei)〃定義基類complex構造函數(shù)(real=r;imag=i;complexoperator+(complexa,complexb)//運算符+重載用友元函數(shù)的實現(xiàn)(complextemp;temp,real=a.real+b.real;temp,imag=a.imag+b.imag;returntemp;)complexoperator-(complexa,complexb)//運算符一重載用友元函數(shù)的實現(xiàn){complextemp;temp,real=a.real-b.real;temp,imag=a.imag-b.imag;returntemp;}complexoperator*(complexa,complexb)〃運算符?重載用友元函數(shù)的實現(xiàn)(complextemp;temp,real=a.real*b.real-a.imag*b.imag;temp,imag=a.real*b.imag+a.imag*b.real;returntemp;)complexoperator/(complexa,complexb)〃運算符/重載用友元函數(shù)的實現(xiàn)|complextemp;doublet;t=1/(b.real*b.real+b.imag*b.imag);temp,real=(a.real*b.real+a.imag*b.imag)*t;temp,imag=(b.real*a.imag-a.real*b.imag)*t;returntemp;}voidcomplex::print()基類complex的print()函數(shù)的實現(xiàn)(cout?real;if(imag>0)cout?"+";if(imag!=0)cout?imag?"i\n";

main(){complexAl(2.3,4.6),A2(3.6,2.8),A3,A4,A5,A6;A3=Al+A2;A4=Al-A2;A5=Al*A2;A6=Al/A2;Al.print();A2.print();A3,print();A4.print();A5.print();A6.print();return0;)程序的運行結果為:3+4.6i6+2.8i5.9+7.4i-1.3+1.8i-4.6+23i/y復數(shù)相加ん/y復數(shù)相加ん復數(shù)相減/ノ復數(shù)相乘/ノ復數(shù)相除/Z輸出復數(shù)A1/ノ輸出復數(shù)A2//輸出復數(shù)相加結果A3//輸出復數(shù)相減結果A4//輸出復數(shù)相乘結果A5//輸出復數(shù)相除結果A6一般而言,如果在類X中采用友元函數(shù)重載雙目運算符@,而aa和bb是類X的兩個對象,則以下兩種函數(shù)調用方法是等價的:aa@bb; 〃表達式方式隱式調用operator?(aa,bb);〃函數(shù)調用方式顯式調用運算符重載函數(shù)實現(xiàn)有時可直接調用類構造函數(shù)來生成一個臨時對象,而不對該對象進行命名。例如(書P171)復數(shù)+的重載函數(shù)complexoperator+(complexa,complexb){complextemp;temp,real=a.real+b.real;temp,imag=a.imag+b.imag;returntemp;)可以改寫為,complexoperators-(complexa,complexb){returncomplex(a.real+b.real,a.imag+b.imag);)在這里創(chuàng)建一個臨時未命名的對象,并把臨時未命名的對C++5章一P14象直接返回主調函數(shù)中。這種函數(shù)效率高。(3)單目運算符重載用友元函數(shù)重載單目運算符時,需要一個顯式的操作數(shù),例如取負運算符一重載函數(shù)例5.4(書P172)ttinclude<iostream.h>classAB 〃聲明一個基類AB(public:AB(intx=0,inty=0)〃基類AB有2個參數(shù)的構造函數(shù){a=x,b=y;}friendABoperator-(ABobj);〃用友元函數(shù)定義單目運算符“-”重載函數(shù)原型voidprint(): 基類AB的print。函數(shù)原型private:inta,b;};ABoperator-(ABobj)//用友元函數(shù)定義單目運算符“-”重載函數(shù)obj.a=-obj.a;

obj.b=-obj.b;returnobj;voidAB::print()(coutくく〃a;〃くくaくく〃)main()|ABobl(50,60),ob2;obi.print();ob2=-obi;ob2.print();return0;b=〃くくb=〃くくbくくendl;〃創(chuàng)建基類對象obi,ob2//對象obi調用基類print()函數(shù)輸出/Z對象ob2取對象obi負值〃對象ob2調用基類print()函數(shù)輸出a=50b=60a二一50b=-60對于前綴方式的單目運算符“++”,“一”一般對象參數(shù)形式重載函數(shù)無法實現(xiàn)。例5.5(書P173)#include<iostream.h>classcoord 聲明一個基類coord(public:coord(inti=0,intj=0);//基類coord有2個參數(shù)的構造函數(shù)原型voidprint(); 基類coord的print()函數(shù)原型friendcoordoperator++(coordop);〃用友元函數(shù)定義單目運算符“++”重載函數(shù)原型private:intx,y;);coord::coord(inti,intj)(x=i;y=j;)voidcoord::print(){coutくく"x:"?x?",y:"<<y?endl;}

coordoperator++(coordop)//用友元函數(shù)定義單目運算符“++”重載函數(shù)++op.X;++op.y;++op.X;++op.y;//函數(shù)內部各項增1returnop;}main()(coordob(10,20); 創(chuàng)建基類對象obob.print(); 對象ob調用基類print()函數(shù)輸出operator++(ob); 顯式調用友元函數(shù)operator++()ob.print(); 對象ob調用基類print()函數(shù)輸出++ob; 〃表達式形式調用友元函數(shù)operator++()ob.print(); 對象ob調用基類print()函數(shù)輸出return0;}程序的運行結果為:TOC\o"1-5"\h\zx : 10 y : 20x : 10 y : 20x : 10 y : 20而我們希望的運算結果應是:x:10y:20x:11y:21x:12y:22顯然上面程序的運算符“++”重載函數(shù)是錯誤的。錯誤的原因是友元運算符重載函數(shù)用的是類對象參數(shù),是通過傳值方法傳遞參數(shù),函數(shù)體內對對象op的所有修改均無法傳到函數(shù)本タI。因此,在operater++()函數(shù)中,任何內容的改變不會影響產生調用的操作數(shù)。也就是說,實際上對象x和y并未增1。而++運算符的原義是要改變操作數(shù)自身的值。為了解決這個問題,只需采用“引用參數(shù)傳遞操作數(shù)”就可以了。因為引用傳遞函數(shù)參數(shù),實際上傳遞是操作數(shù)的地址,這將導致函數(shù)參數(shù)的任何改變都影響產生調用的操作數(shù),從而保持了運算符++的原意。例5.6(書P174)#include<iostream.h>classcoord 聲明—^基類coord(public:coord(inti=0,intj=0);〃基類coord有2個參數(shù)的構造函數(shù)原型C++5章一P19voidprint(); 基類coord的print()函數(shù)原型friendcoordoperator++(coord&op);〃用友元函數(shù)定義單目運算符“++”重載函數(shù),用引用參數(shù)的原型private:intx,y;);coord::coord(inti,intj)(x=i;y=j;)voidcoord::print()(coutくく"x:"?x?",y:"<<y?endl;)coordoperator++(coord&op)//用友元函數(shù)定義單目運算符“++”重載函數(shù),用引用參數(shù){++op.x; 〃函數(shù)內部各項增1++op.y; 〃函數(shù)內部各項增1returnop;main()coordob(10,20); 創(chuàng)建基類對象obob.print(); 對象ob調用基類print()函數(shù)輸出operator++(ob); 顯式調用友元函數(shù)operator++()ob.print(); 〃對象ob調用基類print。函數(shù)輸出++ob; 表達式形式調用友元函數(shù)operator++()ob.print。; 對象ob調用基類print。函數(shù)輸出return0;}程序的運行結果為:x : 10 y : 20x : 11 y : 21x : 12 y : 22如果在X類采用友元函數(shù)重載單目運算符而明是類X的對象,則以下兩種調用方法是等價的:@aa 〃表達式形式隱式調用operator?(aa)函數(shù)形式顯式調用幾點說明:(1)運算符重載函數(shù)operator()通常返回所操作的類類型,可使重載運算符用在復雜的表達式中。例如,可以C++5章_P21連續(xù)進行加、減、乘、除。(2)可以為同一個運算符定義幾個運算符重載函數(shù),C++編譯器根據(jù)參數(shù)的個數(shù)和類型來決定調用哪個重載函數(shù)。(3)不能用友元重載函數(shù)的運算符有:=、()、[]、->5、成員函數(shù)運算符重載函數(shù)(1)類內定義重載函數(shù)格式函數(shù)返回值類型operator?(形參表){〃函數(shù)體語句)(b)類內定義重載函數(shù)原型,類外定義重載函數(shù)體class類名(函數(shù)返回值類型operator?(形參表);//-);函數(shù)返回值類型類名::operator?(形參表)〃函數(shù)體語句其中,形參表,當運算符@是單目的,則參數(shù)表為空,當運算符@是雙目的,則參數(shù)表有一個形參。因為重載函數(shù)是成員函數(shù)定義后有隱含的this指針,哪ー個對象調用,this指針就指向該對象,可以作為ー個參數(shù)傳遞。(2)雙目運算符對雙目運算符,成員運算符重載函數(shù)的形參表僅有一個形參,它作為運算符的右操作數(shù),此時當前對象作為運算符的左操作數(shù),它是通過this指針隱含傳遞給函數(shù)的。也就是說,這個this指針可以作為ー個隱含的參數(shù)。例5.7(書P176)是ー個類內定義原型,類外實現(xiàn)的復數(shù)“+,,、,一,,、“*,,、“/,,運算符成員函數(shù)。#include<iostream.h>classcomplex 聲明一個基類complex(public:complex(doubler=0.0,doublei=0.0);//基類complex有2個參數(shù)的構造函數(shù)原型voidprint(); 基類complex的print()函數(shù)原型complexoperator+(complexc);//運算符+重載用成元函數(shù)的原型complexoperator-(complexc);〃運算符一重載用成元函數(shù)的原型complexoperator*(complexc);//運算符?重載用成元函數(shù)的原型complexoperator/(complexc);〃運算符/重載用成元函數(shù)的原型private:doublereal;doubleimag;);complex::complex(doubler,doublei) 定義基類complex構造函數(shù)(real=r;imag=i;)complexcomplex::operator+(complexc)〃運算符+重載用成元函數(shù)的實現(xiàn)complextemp;temp,real=real+c.real;temp,imag=imag+c.imag;returntemp;complexcomplex::operator-(complexc)//運算符+重載用成元函數(shù)的實現(xiàn)(complextemp;temp,real=real-c.real;temp,imag=imag-c.imag;returntemp;)complexcomplex::operator*(complexc)〃運算符?重載用成元函數(shù)的實現(xiàn)(complextemp;temp,real=real*c.real-imag*c.imag;temp,imag=real*c.imag+imag*c.real;returntemp;)complexcomplex::operator/(complexc)〃運算符/重載用成元函數(shù)的實現(xiàn)complextemp;doublet;t=1/(c.real*c.real+c.imag*c.imag);temp,real=(real*c.real+imag*c.imag)*t;temp,imag=(c.real*imag-real*c.imag)*t;returntemp;)voidcomplex::print()基類complex的print()函數(shù)的實現(xiàn){cout?real;if(imag>0)cout?"+";if(imag!=0)cout?imag?"i\n";)main()(complexAl(2.3,4.6),A2(3.6,2.8),A3,A4,A5,A6;A3 = Al + A2 ; //復數(shù)相加A4 = Al - A2 ; //復數(shù)相減A5 = Al * A2 ; //復數(shù)相乘

A6=Al/A2;//復數(shù)相除A6=Al/A2;//復數(shù)相除Al.print();A2.print();A3,print();A4.print();A5.print();A6.print();/ノ輸出復數(shù)A2//輸出復數(shù)相加結果A3輸出復數(shù)相減結果A4//輸出復數(shù)相乘結果A5〃輸出復數(shù)相除結果A6return0;)程序的運行結果為:3+4.6i6+2.8i9+7.4i-1.3+1.8i-4.6+23i1.01731+0.4865381結果與例5.3用友元重載函數(shù)結果完全相同。這個例在主函數(shù)用表達式形式調用A3=Al+A2;A4=Al-A2;A5=Al*A2;A6=Al/A2;等價于如下用顯式調用重載函數(shù):A3=Al.operator+(A2);A4=Al.operator-(A2);A5=Al.operator*(A2);A6=Al.operator/(A2);由此可見,成員運算符重載函數(shù)operator@實際上是由雙目運算符左邊的對象A1(當前對象)調用,盡管雙目運算符函數(shù)的參數(shù)只有一個操作數(shù)A2,但另ー個操作數(shù)是對象A!通過this指針隱含地傳遞的。一般來說,aa和bb是類X的兩個對象。則下面調用語句是等價的:aa?bb; 表達式形式隱式調用aa.operator?(bb); 函數(shù)調用形式顯式調用(3)單目運算符重載對單目運算符,成員運算符重載函數(shù)的參數(shù)表中沒有參數(shù),此時當前對象作為運算符的ー個操作數(shù)。例5.8(書P179)重載單目運算符++#include<iostream.h>classcoord 聲明一個基類coord(public:coord(inti=0,intj=0);//基類coord有2個參數(shù)的構造函數(shù)原型voidprint();基類coord的print()函數(shù)原型coordoperator++();〃定義單目運算符“++”重載函數(shù)原型private:intx,y;);coord::coord(inti,intj)〃定義基類coord構造函數(shù)(x=i;y=j;voidcoord::print()coutくく"x:"?x?",y:"?y?endl;)coordcoord::operator++()//定義成員運算符函數(shù)。perator++()++x; 〃函數(shù)內部各項增1++y; 〃函數(shù)內部各項增1return*this;〃返回this指針所指存儲塊里面的值}main()(coordob(10,20); 〃創(chuàng)建基類對象obob.print(); 對象ob調用基類print()函數(shù)輸出++ob;〃表達式調用隱式成員運算符函數(shù)operator++()ob.print(); 對象ob調用基類print()函數(shù)輸出ob.operator++(); 顯式調用成員運算符函數(shù)operator++()ob.print();〃對象。b調用基類print。函數(shù)輸出return0;)程序的運行結果為:x:10y:20x:11y:21x:12y:22由于所有成員函數(shù)都有一個this指針,它是指向對象的指針。因此,任何對對象私有數(shù)據(jù)的修改都將影響實際調用運算符的對象。采用成員函數(shù)重載單目運算符時,下面2種調用方式是等價的:@aa; 〃表達式形式隱式調用aa.operator?(); 〃函數(shù)調用形式顯式調用成員運算符函數(shù)operatorゆ所需的ー個操作數(shù)由對象aa通過this指針隱含地傳遞,因此在參數(shù)表上沒有參數(shù)。(4)作為成員運算符重載函數(shù)與友元運算符重載函數(shù)比較(a)雙目運算符重載函數(shù)一般可任意采用成員或友元函數(shù),但對于運算符所需的操作數(shù)(尤其是第一操作數(shù),即左操作數(shù))是標準數(shù)據(jù)類型(即可以隱式類型轉換),這時運算符必須用友元函數(shù)。例如(書P180)類AB中若采用成員函數(shù)重載“+”運算符(書P172例5.4)〇ABAB::operator+(intx){ABtemp;Temp,a=a+x;Temp,b=b+x;returntemp;)若對類AB的對象ob要做賦值運算和加法運算,下面語句沒問題:ob=ob+100;(或ob=ob.operator+(100))由于對象ob是運算符“+”的左操作數(shù),所以調用“+”運算符重載函數(shù),通過this指針把ー個對象作為參數(shù)參加運算。最終,把ー個整數(shù)100加到了對象op的元素上。如果用如右面語句: ob=100+ob;由于左部操作數(shù)100不是AB類對象,不能進行取成員”操作,這是錯誤的,ob=15&<d^erator(ob)如果用友元函數(shù)來重載運算符“+”函數(shù),就能消除左操作數(shù)是標準數(shù)據(jù)類型帶來的問題。例5.9(書P181)#include<iostream.h>classAB //聲明一個基類AB{inta,b;public:AB(intx=0,inty=0);/,基類AB有2個參數(shù)的構造函數(shù)原型friendABoperator+(ABob,intx);〃用友元函數(shù)定義雙目運算符“+”重載函數(shù)原型(整型參數(shù)在右邊)friendABoperator+(intx,ABob);//用友元函數(shù)定義雙目運算符“+”重載函數(shù)原型(整型參數(shù)在左邊)voidshow(); 基類AB的show。函數(shù)原型I;AB::AB(intx,inty) 基類AB有2個參數(shù)的構造函數(shù){a=x,b=y;}ABoperator+(ABob,intx)//用友元函數(shù)定義雙目運算符“+”重載函數(shù),整型參數(shù)在右邊ABtemp;temp,a=ob.a+x;temp,b=ob.b+x;returntemp;)ABoperator+(intx,ABob)/,用友元函數(shù)定義雙目運算符“+”重載函數(shù),整型參數(shù)在左邊{ABtemp;temp,a=x+ob.a;temp,b=x+ob.b;returntemp;)voidAB::show()(coutくく"a="くくaくく""?"b="?b<<endl;main()ABobl(30,40),ob2; //創(chuàng)建基類對象obi,ob2ob2=obi+30; //對象表達式賦值ob2.show(); 對象ob2調用基類show()函數(shù)輸出ob2=50+obi; Z對象表達式賦值ob2.show(); 對象ob2調用基類show()函數(shù)輸出return0;)程序的運行結果為:a=60b=70a=80b=90(b)成員和友元函數(shù)都可以用習慣的表達式形式調用,也可以用函數(shù)調用形式調用(參看書P182表5.2)。(c)究竟用成員函數(shù)還是友元函數(shù)重載運算符為好,沒有固定規(guī)定。參考書P182的說明。(5)“++”與的重載“++”與“-ノ的重載的前綴形式++ob(先加或先減后用)和后綴形式ob++(先用后加或后減)其作用有區(qū)別。在運算符重載函數(shù)中如何區(qū)別呢?可采用括號關鍵字int來區(qū)分。如書P182,以“++”例,(“一”類似)C++5章_P35設,ob為X類對象。對于前綴方式++ob,可以用運算符函數(shù)重載為:ob.operator++(); 成員函數(shù)重載或friendoperator++(X&ob); 友元函數(shù)重載對于后綴方式ob++,可以用運算符函數(shù)重載為:ob.operator++(int); 成員函數(shù)重載或friendoperator++(X&ob,int); 友元函數(shù)重載調用時,參數(shù)int一般被傳遞給值〇。例如:用成員函數(shù)重載方式:classX{??public:??Xoperator++(); 前綴方式Xoperator++(int); 后綴方式???};main(){Xob;???++ob; 隱式調用ob. operator++()ob++; 隱式調用ob. operator++(int)ob.operator++ (); 顯式調用ob. operator++()ob.operator++ ( 0);顯式調用ob. operator++(int)???)也可以用友元函數(shù)重載為:classY(??public:??friendoperator++(Y&); 前綴方式friendoperator++(Y&,int);后綴方式};main()Yob;???++ob; 隱式調用operator ++(Y &)ob++; 隱式調用operator ++(Y &,int)operators-+ (ob); 顯式調用operator ++(Y &)operator++(ob,0);顯式調用operator++(Y&,int)???)例5.10(書P184)#include<iostream.h>#include<iomanip.h>classover 〃聲明一個基類over(intil,i2,i3;public:voidinit(intII,int12,int13); 初始化函數(shù)原型voidprint(); 輸出數(shù)據(jù)成員函數(shù)print()原型overoperator++();〃成員函數(shù)重載“++”(前綴方式)原型overoperator++(int);//成員函數(shù)重載“++”(后綴方式)原型friendoveroperator一(over&);

〃友元函數(shù)重載“一”(前綴方式)原型friendoveroperator—(over&,int);//友元函數(shù)重載“一”(后綴方式)原型C++5章_P38);voidover::init(intII,int12,int13) 定義初始化函數(shù){il=II;i2=12;i3=13;}voidover::print() 定義輸出數(shù)據(jù)成員函數(shù)cout?〃il:〃?il?"i2:"?i2くく"i3:"?i3?endl;overover::operator++(){++il;++i2; ++i3;return*this;)overover::operator++(){++il;++i2; ++i3;return*this;)overover::operator++(int){il++;i2++; i3++;return*this;)overoperator—(over&op)〃定義成員函數(shù)重載“++”(前綴方式)〃定義成員函數(shù)重載“++”(后綴方式)〃定義友元函數(shù)重載”(前綴方式){一op.il:一op.i2; 一op.i3;returnop;

)overoperator—(over&op,int) 定義友元函數(shù)重載“一”(后綴方式)C++5章_P390P.il—;op.i2一;op.i3一;returnop;0P.il—;op.i2一;op.i3一;returnop;)main()(overobjl,obj2objl.init(4,obj2.init(2,obj3.init(8,obj4.init(3,++objl;obj2++;一obj3;obj4——;,obj3,obj4,9); /,調用初始化函數(shù)給對象obj2賦初值,8); /,調用初始化函數(shù)給對象obj3賦初值,7); /,調用初始化函數(shù)給對象obj4賦初值隱式調用overoperator++()隱式調用overoperator++(int)隱式調用overoperator--(over&)隱式調用overoperator--(over&,int)

objl.print();obj2.print();obj3.print();obj4.print();cout?objl.operator++();obj2.operator++(0);operator—(obj3);operator一(obj4,0);objl.print();obj2.print();obj3.print();obj4.print();調用print。調用print。輸出objl'/顯式函數(shù)調用形式,意為++objl//顯式函數(shù)調用形式,意為obj2++//顯式函數(shù)調用形式,意為一obj3〃顯式函數(shù)調用形式,意為obj4一調用print。輸出objl程序的運行結果為:il:5i2:3i3:6il:3i2:6i3:10il:7i2:2i3:7il:2i2:5i3:6il:6i2:4i3:7il:4i2:7i3:11il:6i2:1i3:6il:1i2:4i3:5說明:(i)運算符都對單值操作產生影響的,因此成員運算符函數(shù)重載通常返回this指針。(ii)由于友元函數(shù)不是成員函數(shù),所以沒有this指針,不能引用this指針所指的對象。使用友元函數(shù)應該采用引用參數(shù)傳遞數(shù)據(jù)。(iii)前綴和后綴方式的函數(shù)內部語句可以相同,也可以不同,取決于編程者的要求。(6)賦值運算符的重載如果用戶沒有自定義的賦值運算符重載函數(shù),系統(tǒng)將自動生成一個默認的重載函數(shù)。例如(書P186)C++5章_P42類名&類名::operator=(const類名&source)(〃成員間賦值)對于已創(chuàng)建的兩個對象obi和ob2則表達式obi=ob2;就調用默認的賦值符重載函數(shù),將對象ob2的數(shù)據(jù)成員逐域拷貝到obi中。通常默認函數(shù)能勝任工作,但對于含有指針成員的問題會出現(xiàn)“指針懸掛”的問題。例5.11(書P186)#include<iostream.h>#include<string.h>classstring 〃聲明一個基類string{char*ptr;public:string(char*s) 基類string有1個參數(shù)的構造函數(shù){ptr=newcharEstrlen(s)+1];strcpy(ptr,s);

"string(){deleteptr;}voidprint(){cout?ptr?endl;});voidmain()(stringpl("book");(stringp2("pen");p2=pl;cout?"p2:";p2.print();cout?"pl:";pl.print();〃創(chuàng)建基類對象pl,置初值"book〃創(chuàng)建基類對象P2,置初值"〃創(chuàng)建基類對象pl,置初值"book〃創(chuàng)建基類對象P2,置初值"pen"〃對象pl賦值給p2//輸出p2的值,后自調用析構函數(shù)刪除〃〃輸出P1的值程序運行結果是不正確的。因為執(zhí)行到賦值語句P2=pl;時,實際是使兩個指針P2與pl指向new開辟的同一個存儲塊。當p2的生存期(內層花括號范圍)結束時,系統(tǒng)自動調用析構函數(shù)將這一存儲塊撤消。這時即使P1仍存在,其所指的存儲塊已無法訪問,這就是“指針懸掛”的問題??赡芫彤a生成嚴重的錯誤,下圖5.1(書P187)分析這個問題。(a)執(zhí)行p2二pl之前動態(tài)存儲塊2動態(tài)存儲塊1動態(tài)存儲塊2動態(tài)存儲塊1(b)執(zhí)行p2=pl之后動態(tài)存儲塊1動態(tài)存儲塊1(c)p2的生命周期結束后?「ーー「已撤消動態(tài)存儲塊1[一,必須重載賦值運算符,使得對目標對象數(shù)據(jù)成員指針的賦值是把原對象指針ptr所指向的內容傳遞給它,而不是簡單地傳遞指針值。例5.12(書P188)#include<iostream.h>#include<string.h>classstring 聲明一個基類string{char*ptr;public:string(char*s) 基類string有1個參數(shù)的構造函數(shù)(ptr=newcharEstrlen(s)+1];strcpy(ptr,s);)string(){deleteptr;}voidprint(){cout?ptr?endl;}string&operator=(conststring&);〃賦值運算符重載函數(shù)原型);string&string::operator=(conststring&s)//賦值運算符重載函數(shù)實現(xiàn){if(this=&s)return*this: 〃防止s==s的賦值deleteptr; 釋放掉原存儲區(qū)域ptr=newchartstrlen(s.ptr)+1]; 重新分配新存儲區(qū)域strcpy(ptr,s.ptr); 字符串內容拷貝return*this;)voidmain(){stringpl("book"); 〃創(chuàng)建基類對象pl,置初值"book"{stringp2("pen"); 創(chuàng)建基類對象p2,置初值"pen"p2=pl;/ノ對象pl賦值給p2,用自定義賦值運算符重載函數(shù)實現(xiàn)cout?"p2:";p2.print(); //輸出p2的值,后自調用析構函數(shù)刪除)cout?"pl:";pl.print(); 〃輸出pl的值)程序的運行結果為:p2:bookpl:book五、虛函數(shù)(書P200)虛函數(shù)是重載的另ー種重要形式,是ー種動態(tài)的重載方式,提供ー種更為靈活的多態(tài)性機制。函數(shù)允許函數(shù)調用與函數(shù)體之間的聯(lián)系在運行時オ建立,也就是在運行時ォ決定執(zhí)行哪ー個函數(shù),即所謂動態(tài)聯(lián)編。1、基類指針與派生類對象地址在第4章根據(jù)賦值兼容規(guī)則,指向基類對象的指針可以指向它的公有派生類的對象。因此,C++中把公有派生類對象地址賦給基類指針變量是可以的。例如,classB(public:inti; 〃基類B的公有數(shù)據(jù)成員ifloatf; 〃基類B的公有數(shù)據(jù)成員f};classD:publicB 基類B的公有派生類D(public:charch; 派生類D新增公有成員chintj; 〃派生類D新增公有成員j};

voidmainO 外部函數(shù){Dd: 〃公有派生類D創(chuàng)建對象dB*b=&d;〃派生類對象d的地址賦給基類指針bb->!=12;〃給b所指i存儲塊賦值12)在函數(shù)main。中,派生類對象d的地址賦給了基類指針b,并且用此指針修改派生類繼承基類的公有數(shù)據(jù)成員i的值為12是正確的。為什么可以這樣做?因為C++的繼承是采用“復制繼承”方式,也就是派生類的對象要為繼承來的基類的所有成員分配存儲空間,然后再分配派生類新增成員的存儲空基類B的成員派生類新增成員間。 基類B的成員派生類新增成員本例如右圖。當把基類B指針當把基類B指針b指向派生類對象后,由于派生類中繼承來的基類成員的結構和順序都與原基類完全相同??梢哉J為i和f重新組成了基類B的對象。b指向此對象的首地址,因而b可以隨意訪問這個基類對象成員,實際上b訪問的是派生類對象的成員,它訪問了派生類對象成員中從基類繼承來的成員。(a)但是用基類指針b指向派生類新增成員就不行。例,b->ch; 〃錯誤b->j; 〃錯誤(b)如果私有派生,派生類對象地址賦給基類指針是非法的。因為派生類中基類成員是私有的。2、基類指針用于派生類基類指針只能用于公有派生類中由基類繼承來的成員(函數(shù)),不能指向派生類新增的成員(函數(shù))。例5.20(書P200)#include<iostream.h>classmy_base 聲明一個基類my_base(inta,b;public:my_base(intx,inty) 基類my_base有2個參數(shù)的構造函數(shù)(a=x;

voidshow()定義基類voidshow()定義基類my_base的輸出函數(shù)show()cout?"my_base \n;coutくくaくく〃“くくbくくendl;));classmy_class:publicmy_base定義基類my_base的公有派生類my_class(intc;public:my_class(intx,inty,intz):my_base(x,y){c=z;} 派生類my_class的構造函數(shù)voidshow() 定義派生類my_class的輸出函數(shù)show。(cout?"my_class \n";cout?"c="?c?endl;});voidmain()my_basemb(50,50),*mp; 〃mb是基類對象,mp是基類指針mp=&mb; 基類指針mp指向基類對象mbmp->show(); 操作由基類的show。函數(shù)my_classme(10,20,30); me是派生類對象mp=&mc;/ノ派生類對象me的地址賦給基類指針,基類指針mp指向派生類對象memp->show();//操作派生類show。函數(shù),實際上操作由基類繼承來的基類成員函數(shù)show()手基類指針不能訪問派生類新增的成員。例,書P202classA{//-public:voidprintl();};classB:publicA{//-public:voidprint2();};voidmain(){Aopl,*ptr;定義基類A的對象opl和指針ptrBop2; 〃定義派生類B的對象op2ptr=&opl;將指針ptr指向基類A的對象oplptr->printl():調用基類函數(shù)printl()ptr=&op2; 將指針ptr指向派生類B的對象op2ptr->printl();〃正確,調用對象op2從基類繼承來的成員函數(shù)printl()ptr->print2();〃錯誤,基類指針Ptr不能訪問派生類新增的print2())若想訪問其公有派生類的特定成員,可以將基類指針用顯式類型轉換為派生類指針。((B*)ptr)->print2();外層的括號表示對Ptr的強制轉換而不是返回類型。3、虛函數(shù)的概念及定義(1)為什么引入虛函數(shù)書P200例5.20中盡管在基類my_base定義了成員函數(shù)show。,在公有派生類my_class又定義了成員函數(shù)show(),但利用基類指針mp盡管賦了派生類對象me的地址,企圖調用派生類show。函數(shù)并不成功。執(zhí)行結果(書P201)仍是調用了基類的show。函數(shù)結果。如果把基類和派生類的show()函數(shù)被聲明為虛函數(shù),情況就不同了。就可以用基類指針通過賦了派生類對象地址可以調用派生類同名虛函數(shù)。例5.21虛函數(shù)即虛擬函數(shù)(virtualfunction)是指某個函數(shù)基類中被聲明為virtua!函數(shù),該函數(shù)與同名函數(shù)可在派生類重新定義痂3〇由基類指針作為單ー接口進行調用。(2)虛函數(shù)定義格式virtual函數(shù)返回值類型函數(shù)名([參數(shù)表])(〃函數(shù)體)在基類定義虛函數(shù)可以在類內定義,也可以類內聲明函數(shù)原型,在類外定義函數(shù)體(此時不必重寫關鍵字virtual)〇C++5章_P55虛函數(shù)可以在ー個或多個派生類中被重新定義(此時就不必重寫關鍵字virtual)。在派生類中重新定義時,其函數(shù)原型,包括返回值類型、函數(shù)名、參數(shù)個數(shù)、參數(shù)的類型及其順序都必須與基類中的原型完全一致。例5.22(書P204)基類是虛函數(shù)Winclude<iostream.h>classparent 〃聲明一個基類parent{protected:charversion;public:parent() 基類parent有地個參數(shù)的構造函數(shù){version二'A'; }virtualvoidprint() 定義虛函數(shù)print。(coutくヘ\nTheparent,version?version;));classderivedl:publicparent 定義派生類derivedl(private:intinfo;public:derivedl(intnumber) 派生類derivedl的構造函數(shù)原型{info=number;version= 1,;)voidprint() 派生類derivedl重新定義的print()虛函數(shù)(cout?”\nThederived1info:"<<infoくくversion*?version;));classderived2:publicparent 定義派生類derivedl(private:intinfo;public:derived2(intnumber) 派生類derivedl的構造函數(shù)原型{info=number;version='2';voidprint()〃派生類derived2重新定義的print()虛函數(shù)voidprint()coutくく\nThederived2info:"<<infoくくversion?version;cout?endl;});voidmain()(parentob,*op; mp是基類指針op=&ob;op->print();derivedldl(3);op=&dl;op->print();derived2d2(15);op=&d2;op->print();/ノ操作派生類showO函數(shù),實際上操作由基類繼承來的showO函數(shù))例5_22」基類有非虛函數(shù)

幾點說明:(a)在基類中,用關鍵字virtua!可以將其public或protected部分的成員函數(shù)聲明為虛函數(shù)。友元、靜態(tài)成員函數(shù)則不可以。(b)虛函數(shù)在派生類被重新定義時,其函數(shù)原型與基類中的函數(shù)原型必須完全相同。(c)在派生類對基類中聲明的虛函數(shù)進行重新定義時,關鍵字virtual可以寫也可以不寫。但容易引起混淆時最好寫上。如果派生類沒有用virtual聲明為虛函數(shù),系統(tǒng)按以下原則判斷該成員函數(shù)是否是虛函數(shù)。視其與基類虛函數(shù)是否返回值類型、函數(shù)名、參數(shù)個數(shù)、參數(shù)的類型及其順序完全一致。(d)定義了虛函數(shù)后,在主函數(shù)中如果用對象名”運算符可以調用虛函數(shù)。例如,dl.print()可以調用虛函數(shù)derived::print。,但這種調用是在編譯時進行的靜態(tài)聯(lián)編,就沒有充分利用虛函數(shù)運行時的特性。(e)一個虛函數(shù)無論公有派生多少層,仍然保持其虛函數(shù)的特性。(f)只有類的成員函數(shù)オ可以定義為虛函數(shù)。友元、靜態(tài)成員函數(shù)不可以,因為虛函數(shù)調用要靠特定的對象來決定該激活哪個函數(shù)。

但虛函數(shù)可以在另ー類中被聲明為友元函數(shù)。(g)構造函數(shù)不能定義成虛函數(shù),但析構函數(shù)可以定義成虛函數(shù)。(3)虛函數(shù)與重載函數(shù)在ー個派生類中重新定義基類的虛函數(shù)是函數(shù)重載的另ー種形式,但它不同于一般的函數(shù)重載。普通函數(shù)重載其函數(shù)參數(shù)或參數(shù)類型或類型順序必須不同。編譯時以這些不同靜態(tài)聯(lián)編確定執(zhí)行哪ー個函數(shù)。派生類中重新定義虛函數(shù)時,要求函數(shù)名、返回值類型、參數(shù)個數(shù)、參數(shù)的類型及其順序都要與基類的虛函數(shù)原型完全相同。編譯時,不是按照靜態(tài)聯(lián)編生成“調用此函數(shù)的版本”,而是只“為它們生成一個虛函數(shù)表(表中存放與此函數(shù)同名、同參數(shù)、同返回值的虛函數(shù)的入口地址)”。程序執(zhí)行時,再根據(jù)實際對象的類型(基類型或派生類型)查虛函數(shù)表,找出相應版本的虛函數(shù)オ執(zhí)行它。這就是動態(tài)聯(lián)編過程。若派生類中沒有重新定義基類的虛函數(shù),而基類指針又賦了派生類的對象地址,執(zhí)行該虛函數(shù)功能時C++系統(tǒng)會引導程序執(zhí)行派生類內繼承下來基類中的虛函數(shù)。

例5.23(書P206)#include<iostream.h>classbase(public:virtualvoidfund();virtualvoidfunc2();virtualvoidfunc3();voidfunc4(););classderived:publicbase聲明一個基類聲明一個基類base〃定義虛函數(shù)funcl()原型〃定義虛函數(shù)func2()原型〃定義虛函數(shù)func3()原型〃定義基類普通成員函數(shù)funclO原型定義派生類derivedvirtualvoidfunclO;定義虛函數(shù)fund()原型,這里可不寫virtualvoidfunc2(intx): 〃作為普通函數(shù)重載,虛特性消失//charfunc3(); /,錯誤,因為只有返回類型不同,應刪去voidfunc4(); 〃是普通函數(shù)重載,不是虛函數(shù));voidbase::funcl() 定義基類虛函數(shù)funcl(){cout<<" basefuncl \n;}

voidbase::func2(){coutくく basefunc2 ヽn”;)voidbase::func3(){coutくく basefunc3 \n;}voidbase::func4(){cout?" basefunc4 \n;}voidderived::fund()//定義基類虛函數(shù)func2()〃定義基類虛函數(shù)func3()〃定義基類成員函數(shù)func4()//定義基類虛函數(shù)func2()〃定義基類虛函數(shù)func3()〃定義基類成員函數(shù)func4()//重新定義派生類虛函數(shù)fund()voidderived::func2(intx)〃定義派生類重載函數(shù)func2(){cout?” derivedfunc2 \n";}voidderived::func4()〃定義派生類重載函數(shù)func4()voidderived::func4()〃定義派生類重載函數(shù)func4(){cout?” derivedfunc4 \n”;}voidmain()voidmain(){basedl,*bp;derivedd2;bp=&d2;bp->fund();bp->func2();〃創(chuàng)建基類對象dl,指針bp〃創(chuàng)建派生類對象d2//把派生類對象d2的地址賦給基類指針bp調用derived::funcl()函數(shù)調用base::func2()函數(shù)//(不是調用derived::func2(),因為重定義時不是虛函數(shù))bp->func4();〃調用base::func4()函數(shù)(引導調用基類成員函數(shù))(4)多繼承與虛函數(shù)在多繼承情況下同名函數(shù)有不同的繼承路徑,所以呈現(xiàn)不同的性質。例5.24(書P208)#include<iostream.h>classbasel 〃聲明一個基類basel{public:virtualvoidfun() 定義虛函數(shù)fun(){coutくく baselvirtual \n";});classbase2 聲明一個基類base2{public:voidfun() 定義普通成員函數(shù)fun(){cout?* base2notvirtual \n";});classderived:publicbasel,publicbase2〃定義派生類derived是雙基派生public:voidfun() 定義虛函數(shù)fun(),因為它是基類basel中定義的{coutくく derivedvirtual \n”;});voidmain()(basel*ptrl: 定義指向基類basel的指針ptrlbase2*ptr2; 定義指向基類base2的指針ptr2derivedobj3; 定義派生類derived的對象obj3ptrl=&obj3;基類base!的指針ptrl指向派生類derived的對象obj3ptrl->fun();因為fun()為虛函數(shù),所以調用派生類derived的fun()ptr2=&obj3;基類base2的指針ptr2指向派生類derived的對象obj3ptr2->fun();因為ptr2為base2的指針,而且fun()為非虛函數(shù),//因此調用基類base2的fun())這里派生類derived是基類basel和base2雙基派生的。所以同名函數(shù)fun()有不同的繼承路徑。對于basel的派生路徑,由于fun()在基類定義是虛函數(shù),所以基類指針ptrl指向派生類對象obj3時訪問派生類的fun()是虛函數(shù)derived::fun()〇而基類base2由于fun()是一般成員函數(shù),所以只是一個重載函數(shù),當聲明指向base2的指針指向derived的對象obj3時,函數(shù)fun()是重載函數(shù)。Ptr2->fun()只是調用由基類繼承來的base2::fun()(5)虛函數(shù)舉例書P209例5.25(書P209)應用C++動態(tài)多態(tài)性,計算三角形、矩形和園的面積。#include<iostream.h>classfigure 聲明一個基類figure{protected:doublex,y;public:figure(doublea,doubleb) 定義構造函數(shù){x=a;y=b;}virtualvoidshow_area() 定義虛函數(shù)show_area(),作為公共接口{coutく、Noareacomputationdefined^;coutくくforthisclass.\n”;});classtriangle:publicfigure 定義三角形派生類public:triangle(doublea,doubleb):figure(a,b)〃定義構造函數(shù)給基類傳遞參數(shù){} 〃空函數(shù)體virtualvoidshow_area()〃重新定義虛函數(shù)show_area(),用作求三角形面積{cout?”Trianglewithheight"<<x;cout?"andbase"?y?"hasanareaof";cout?x*y*0.5?endl;));classsquare:publicfigure 定義矩形派生類(public:square(doublea,doubleb):figure(a,b)〃定義構造函數(shù)給基類傳遞參數(shù){} //空函數(shù)體virtualvoidshow_area()〃重新定義虛函數(shù)show_area(),用作求三角形面積{cout?"Squarewithdimension"?x;cout?"*"?y?"hasanareaof";cout?x*y?endl;);classcircle:publicfigure 定義園形派生類(public:circle(doublea):figure(a,a)〃定義構造函數(shù)給基類傳遞參數(shù){} //空函數(shù)體virtualvoidshow_area()〃重新定義虛函數(shù)show_area(),用作求三角形面積{coutくくCirclewithradius”くくx;cout?"hasanareaof";cout?x*x*3.1416?endl;});main()(figure*p; 定義指向基類figure的指針ptrianglet(10.0,6.0); 創(chuàng)建三角形對象tsquares(10.0,6.0); 創(chuàng)建矩形對象scirclec(10.0); 創(chuàng)建園形對象cP=&t: 〃基類指針賦給三角形派生類對象地址p->show_area(); 調用虛函數(shù)show_area()計算三角形面積P=&s; //基類指針賦給矩形派生類對象地址p->show_area(); 調用虛函數(shù)show_area()計算矩形面積P=&c; //基類指針賦給園形派生類對象地址p->show_area(); 調用虛函數(shù)show_area()計算園形面積return0;)4、純虛函數(shù)和抽象類(1)為什么引入純虛函數(shù)基類往往表示一般性抽象的概念,并不與具體的事物相聯(lián)系。如例5.25(書P209)基類figure(形狀)只是表示一個封閉的圖形,沒有具體指明是什么圖形,要計算這種抽象圖形面積是不可能的。所以基類的虛函數(shù)的函數(shù)體是空的。它只為派生類提供ー個公共接口,由各派生類根據(jù)具體的幾何圖形重新定義虛函數(shù)計算具體圖形的面積。

對于這種在基類中無具體功能需要實現(xiàn)的函數(shù),能碓基類中只說明函數(shù)原型作為派生類的公共接口,由派生類再給出函數(shù)的具體實現(xiàn)?C++就引入純虛函數(shù)。(2)純虛函數(shù)的定義純虛函數(shù)是一個在基類中說明的虛函數(shù),它在該基類中沒有定義具體的操作內容,要求各派生類根據(jù)實際需要定義自己的虛函數(shù)版本。純虛函數(shù)的定義格式:virtual函數(shù)返回值類型函數(shù)名([形參表])=0;此格式與一般虛函數(shù)成員的原型在書寫格式上基本相同,不同的僅是在最后用“二0”代替函數(shù)體部份。聲明為純虛函數(shù)之后,基類中就不再給出函數(shù)的實現(xiàn)部分(即函數(shù)體)。

溫馨提示

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

評論

0/150

提交評論