第五講多態(tài)性_第1頁
第五講多態(tài)性_第2頁
第五講多態(tài)性_第3頁
第五講多態(tài)性_第4頁
第五講多態(tài)性_第5頁
已閱讀5頁,還剩77頁未讀 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第五講多態(tài)性多態(tài)性多態(tài)性概述運算符重載虛函數(shù)與多態(tài)虛析構(gòu)函數(shù)純虛函數(shù)與抽象類程序?qū)嵗鄳B(tài)類型小結(jié)運行時類型識別綜合實例多態(tài)是指操作接口具有表現(xiàn)多種形態(tài)的能力,即能根據(jù)操作環(huán)境的不同采用不同的處理方式。多態(tài)性是面向?qū)ο笙到y(tǒng)的主要特性之一,在這樣的系統(tǒng)中,一組具有相同基本語義的方法能在同一接口下為不同的對象服務(wù)。C++語言支持的多態(tài)性可以按其實現(xiàn)的時機分為編譯時多態(tài)和運行時多態(tài)兩類。多態(tài)性概述多態(tài)的類型多態(tài)的類型重載多態(tài):函數(shù)重載及運算符重載強制多態(tài):隱式類型轉(zhuǎn)換及強制類型轉(zhuǎn)換包含多態(tài):基類及子類中同名成員函數(shù)的多態(tài)行為參數(shù)多態(tài):函數(shù)模板及類模板C++不但提供了固有的多態(tài)性,還提供了實現(xiàn)自定義多態(tài)性的手段。多態(tài)性概述多態(tài)的實現(xiàn)多態(tài)的種類編譯時的多態(tài)運行時的多態(tài)綁定是指把一個標(biāo)識符名和一個存儲地址聯(lián)系在一起的過程編譯時的多態(tài)綁定工作在編譯連接階段完成的情況稱為靜態(tài)綁定運行時的多態(tài)綁定工作在程序運行階段完成的情況稱為動態(tài)綁定多態(tài)性概述多態(tài)性多態(tài)性概述運算符重載虛函數(shù)與多態(tài)虛析構(gòu)函數(shù)純虛函數(shù)與抽象類程序?qū)嵗鄳B(tài)類型小結(jié)運行時類型識別綜合實例用“+”、“-”能夠?qū)崿F(xiàn)復(fù)數(shù)的加減運算嗎?實現(xiàn)復(fù)數(shù)加減運算的方法

——重載“+”、“-”運算符運算符重載是對已有的運算符賦予多重含義,使同一個運算符作用于不同類型的數(shù)據(jù)時導(dǎo)致不同的行為。運算符重載運算符重載的規(guī)則C++幾乎可以重載全部的運算符,而且只能夠重載C++中已經(jīng)有的運算符重載之后運算符的優(yōu)先級和結(jié)合性都不會改變,操作數(shù)個數(shù)也不會改變運算符重載是針對新類型數(shù)據(jù)的實際需要,對原有運算符進行適當(dāng)?shù)母脑靸煞N重載方式:重載為類的非靜態(tài)成員函數(shù)和重載為非成員函數(shù)重載運算符的函數(shù)不能有默認(rèn)的參數(shù),否則就改變了運算符參數(shù)的個數(shù)運算符重載運算符重載的規(guī)則運算符重載C++中可以被重載的操作符:C++中不能被重載的操作符:

“.”、“.*”、“::”、“?:”C++要求,重載時,賦值”=“、下標(biāo)”[]”、調(diào)用”()”和成員訪問箭頭”->”操作符必須被重載為類的成員函數(shù)運算符重載為成員函數(shù)聲明形式函數(shù)類型類名::operator運算符(形參){......}重載為類成員函數(shù)時

參數(shù)個數(shù)=原操作數(shù)個數(shù)-1

(后置++、--除外)重載為非成員函數(shù)時參數(shù)個數(shù)=原操作數(shù)個數(shù),且至少應(yīng)該有一個自定義類型的形參運算符重載運算符前后可以有空格,也可以沒有空格運算符重載為成員函數(shù)雙目運算符B如果要重載B為類成員函數(shù),使之能夠?qū)崿F(xiàn)表達(dá)式oprd1Boprd2,其中

oprd1為A類對象,則B應(yīng)被重載為A類的成員函數(shù),形參類型應(yīng)該是oprd2

所屬的類型。經(jīng)重載后,表達(dá)式

oprd1Boprd2

相當(dāng)于oprd1.operatorB(oprd2)運算符重載這是理解和應(yīng)用函數(shù)重載的關(guān)鍵復(fù)數(shù)類加減法運算重載運算符重載

將“+”、“-”運算重載為復(fù)數(shù)類的成員函數(shù)。規(guī)則:實部和虛部分別相加減。

操作數(shù):兩個操作數(shù)都是復(fù)數(shù)類的對象。復(fù)數(shù)類加減法運算重載運算符重載#include<iostream>usingnamespacestd;classComplex //復(fù)數(shù)類定義{ public: //外部接口

Complex(doubler=0.0,doublei=0.0):m_real(r),m_imag(i){} //構(gòu)造函數(shù)

Complexoperator+(constComplex&c2)const; //運算符+重載成員函數(shù)

Complexoperator-(constComplex&c2)const; //運算符-重載成員函數(shù)

voiddisplay()const; //輸出復(fù)數(shù)private: //私有數(shù)據(jù)成員

doublem_real; //復(fù)數(shù)實部

doublem_imag; //復(fù)數(shù)虛部};復(fù)數(shù)類加減法運算重載運算符重載//重載運算符函數(shù)實現(xiàn)ComplexComplex::operator+(constComplex&c2)const{

returnComplex(m_real+c2.m_real,m_imag+c2.m_imag);//創(chuàng)建一個臨時無名對象作為返回值}//重載運算符函數(shù)實現(xiàn)ComplexComplex::operator-(constComplex&c2)const{

returnComplex(m_real-c2.m_real,m_imag-c2.m_imag);//創(chuàng)建一個臨時無名對象作為返回值}voidComplex::display()const{ cout<<"("<<m_real<<","<<m_imag<<")"<<endl;}復(fù)數(shù)類加減法運算重載運算符重載intmain(){ //主函數(shù)

Complexc1(5,4),c2(2,10),c3; //定義復(fù)數(shù)類的對象

cout<<"c1=";c1.display(); cout<<"c2=";c2.display();

c3=c1-c2; //使用重載運算符完成復(fù)數(shù)減法

cout<<"c3=c1-c2=";c3.display();

c3=c1+c2; //使用重載運算符完成復(fù)數(shù)加法

cout<<"c3=c1+c2=";c3.display(); return0;}程序輸出的結(jié)果為:c1=(5,4)c2=(2,10)c3=c1-c2=(3,-6)c3=c1+c2=(7,14)運算符成員函數(shù)的設(shè)計運算符重載前置單目運算符U如果要重載U為類成員函數(shù),使之能夠?qū)崿F(xiàn)表達(dá)式Uoprd,其中

oprd為A類對象,則U應(yīng)被重載為A類的成員函數(shù),無形參經(jīng)重載后,

表達(dá)式

Uoprd

相當(dāng)于oprd.operatorU()運算符成員函數(shù)的設(shè)計運算符重載后置單目運算符++和--如果要重載++或--為類成員函數(shù),使之能夠?qū)崿F(xiàn)表達(dá)式

oprd++

或oprd--

,其中

oprd為A類對象,則++或--應(yīng)被重載為A類的成員函數(shù),且具有一個int類型形參經(jīng)重載后,表達(dá)式

oprd++

相當(dāng)于oprd.operator++(0)示例:運算符重載為成員函數(shù)運算符重載運算符前置++和后置++重載為時鐘類的成員函數(shù)前置單目運算符,重載函數(shù)沒有形參,對于后置單目運算符,重載函數(shù)需要有一個整型形參操作數(shù)是時鐘類的對象實現(xiàn)時間增加1秒鐘運算符重載為成員函數(shù)運算符重載#include<iostream>usingnamespacestd;classClock //時鐘類定義{ public: //外部接口

Clock(inthour=0,intminute=0,intsecond=0); voidshowTime()const;

Clock&operator++(); //前置單目運算符重載

Clockoperator++(int); //后置單目運算符重載private: //私有數(shù)據(jù)成員

intm_hour,m_minute,m_second;};Clock::Clock(inthour/*=0*/,intminute/*=0*/,intsecond/*=0*/){

if(0<=hour&&hour<24&&0<=minute&&minute<60 &&0<=second&&second<60) { this->m_hour=hour; this->m_minute=minute; this->m_second=second; }else cout<<"Timeerror!"<<endl;}運算符重載為成員函數(shù)運算符重載voidClock::showTime()const //顯示時間函數(shù){

cout<<m_hour<<":"<<m_minute<<":"<<m_second<<endl;}Clock&Clock::operator++() //前置單目運算符重載函數(shù){

m_second++; if(m_second>=60) { m_second-=60; m_minute++; if(m_minute>=60) { m_minute-=60; m_hour=(m_hour+1)%24; } } return*this;}//后置單目運算符重載(注意形參表中的整型參數(shù))ClockClock::operator++(int){

Clockold=*this; ++(*this); //調(diào)用前置“++”運算符

returnold;}這個參數(shù)僅起到標(biāo)識的作用,不傳遞任何參數(shù)值返回的是值,而不是引用運算符重載為成員函數(shù)運算符重載intmain(){ ClockmyClock(23,59,59); cout<<"Firsttimeoutput:"; myClock.showTime(); cout<<"ShowmyClock++:";

(myClock++).showTime(); cout<<"Show++myClock:";

(++myClock).showTime(); return0;}運行結(jié)果:Firsttimeoutput:23:59:59ShowmyClock++:23:59:59Show++myClock:0:0:1運算符重載為非成員函數(shù)運算符重載函數(shù)的形參代表依自左至右次序排列的各操作數(shù)后置單目運算符++和--的重載函數(shù),形參列表中要增加一個int,但不必寫形參名如果在運算符的重載函數(shù)中需要操作某類對象的私有成員,可以將此函數(shù)聲明為該類的友元運算符重載為非成員函數(shù)運算符重載雙目運算符B重載后,

表達(dá)式oprd1Boprd2

等同于operatorB(oprd1,oprd2)前置單目運算符B重載后,

表達(dá)式Boprd

等同于operatorB(oprd)后置單目運算符++和--重載后,

表達(dá)式oprdB

等同于operatorB(oprd,0)以非成員函數(shù)形式重載Complex的加減法運算和“<<”運算符運算符重載將+、-(雙目)重載為非成員函數(shù),并將其聲明為復(fù)數(shù)類的友元,兩個操作數(shù)都是復(fù)數(shù)類的常引用將<<(雙目)重載為非成員函數(shù),并將其聲明為復(fù)數(shù)類的友元,它的左操作數(shù)是std::ostream引用,右操作數(shù)為復(fù)數(shù)類的常引用,返回std::ostream引用,用以支持下面形式的輸出:

cout<<a<<b;

該輸出調(diào)用的是:

operator<<(operator<<(cout,a),b);運算符重載為非成員函數(shù)運算符重載#include<iostream>usingnamespacestd;classComplex //復(fù)數(shù)類定義{ public: //外部接口

Complex(doubler=0.0,doublei=0.0):m_real(r),m_imag(i){} //構(gòu)造函數(shù)

friendComplexoperator+(constComplex&c1,constComplex&c2); //運算符+重載

friendComplexoperator-(constComplex&c1,constComplex&c2); //運算符-重載

friendostream&operator<<(ostream&out,constComplex&c);//運算符<<重載private: //私有數(shù)據(jù)成員

doublem_real; //復(fù)數(shù)實部

doublem_imag; //復(fù)數(shù)虛部};//重載運算符函數(shù)實現(xiàn)Complexoperator+(constComplex&c1,constComplex&c2){

returnComplex(c1.m_real+c2.m_real,c1.m_imag+c2.m_imag);}運算符重載為非成員函數(shù)運算符重載//重載運算符函數(shù)實現(xiàn)Complexoperator-(constComplex&c1,constComplex&c2){

returnComplex(c1.m_real-c2.m_real,c1.m_imag-c2.m_imag);}//重載運算符函數(shù)實現(xiàn)ostream&operator<<(ostream&out,constComplex&c){

out<<"("<<c.m_real<<","<<c.m_imag<<")"; returnout;}intmain() //主函數(shù){

Complexc1(5,4),c2(2,10),c3; //定義復(fù)數(shù)類的對象

cout<<"c1="<<c1<<endl; cout<<"c2="<<c2<<endl;

c3=c1-c2; //使用重載運算符完成復(fù)數(shù)減法

cout<<"c3=c1-c2="<<c3<<endl;

c3=c1+c2; //使用重載運算符完成復(fù)數(shù)加法

cout<<"c3=c1+c2="<<c3<<endl; return0;}多態(tài)性多態(tài)性概述運算符重載虛函數(shù)與多態(tài)虛析構(gòu)函數(shù)純虛函數(shù)與抽象類程序?qū)嵗鄳B(tài)類型小結(jié)運行時類型識別綜合實例問題的提出虛函數(shù)與多態(tài)回顧前面?zhèn)€人銀行賬戶的例子,其中遺留了一個問題:如何利用一個循環(huán)結(jié)構(gòu)依次處理同一類族中不同類的對象?問題的提出虛函數(shù)與多態(tài)#include"account.h"#include<iostream>usingnamespacestd;intmain(){ Datedate(2008,11,1); //起始日期

//建立幾個賬戶

SavingsAccountsa1(date,"S3755217",0.015); SavingsAccountsa2(date,"02342342",0.015); CreditAccountca(date,"C5392394",10000,0.0005,50);

Account*accounts[]={&sa1,&sa2,&ca}; constintn=sizeof(accounts)/sizeof(Account*); for(inti=0;i<n;i++) {

accounts[i]->show(); cout<<endl; } return0;}問題的提出虛函數(shù)與多態(tài)從show函數(shù)的輸出結(jié)果可以看出,無論accounts[i]指向的是哪種類型的實例,通過accounts[i]->show()調(diào)用的函數(shù)都是Account類中定義的show函數(shù)而我們希望根據(jù)accounts[i]所指向的實例類型決定哪個show函數(shù)被調(diào)用,當(dāng)i==2時被調(diào)用的應(yīng)當(dāng)是CreditAccount類中定義的show函數(shù)要解決這一問題,就要應(yīng)用虛函數(shù)來實現(xiàn)多態(tài)性虛函數(shù)的概念虛函數(shù)與多態(tài)注意區(qū)分“指針類型”與“指針?biāo)笇嵗愋汀?,后者可以是前者的子類型“靜態(tài)綁定”是指根據(jù)“指針類型”在編譯時決定要調(diào)用的函數(shù);“動態(tài)綁定”是指根據(jù)“指針?biāo)笇嵗愋汀痹谶\行時決定要調(diào)用的函數(shù)根據(jù)類型兼容規(guī)則,基類指針(或引用)可以指向其派生類的實例,但通過基類指針(或引用)卻只能調(diào)用基類的函數(shù)成員,無法調(diào)用其所指實例(派生類)的函數(shù)成員解決這一問題的辦法是:如果希望通過基類指針(或引用)訪問其所指派生類實例的函數(shù)成員,則應(yīng)在基類中聲明同名的虛擬函數(shù)成員,表示對相應(yīng)的函數(shù)進行動態(tài)綁定,而不是靜態(tài)綁定一般虛函數(shù)成員虛函數(shù)與多態(tài)C++中引入了虛函數(shù)的機制在派生類中可以對基類中的成員函數(shù)進行覆蓋(重定義)虛函數(shù)必須是非靜態(tài)成員函數(shù)虛函數(shù)的聲明

virtual函數(shù)類型函數(shù)名(形參表) {

函數(shù)體 }virtual關(guān)鍵字只能出現(xiàn)在類定義中的函數(shù)原型聲明中,不能出現(xiàn)在類體外的函數(shù)定義中。注意,不同于“隱藏”的概念虛函數(shù)成員虛函數(shù)與多態(tài)#include<iostream>usingnamespacestd;classBase1 //基類Base1定義{public:

virtualvoiddisplay()const; //虛函數(shù)};voidBase1::display()const{ cout<<"Base1::display()"<<endl;}classBase2:publicBase1 //公有派生類Base2定義{public:

voiddisplay()const; //覆蓋基類的虛函數(shù)};voidBase2::display()const{ cout<<"Base2::display()"<<endl;}注意,派生類中的虛函數(shù)可以不用增加virtual關(guān)鍵字。對于基類中的虛函數(shù),派生類中的同名同參數(shù)函數(shù)自動成為虛函數(shù)。虛函數(shù)成員虛函數(shù)與多態(tài)classDerived:publicBase2 //公有派生類{public:

voiddisplay()const; //覆蓋基類的虛函數(shù)};voidDerived::display()const{ cout<<"Derived::display()"<<endl;}voidfun(Base1*ptr) //參數(shù)為指向基類對象的指針{

ptr->display(); //"對象指針->成員名"}intmain() //主函數(shù){

Base1base1; //定義Base1類對象

Base2base2; //定義Base2類對象

Derivedderived; //定義Derived類對象

fun(&base1);//用Base1對象的指針調(diào)用fun函數(shù)

fun(&base2);//用Base2對象的指針調(diào)用fun函數(shù)

fun(&derived);//用Derived對象的指針調(diào)用fun函數(shù)

return0;}運行結(jié)果:Base1::display()Base2::display()Derived::display()注意,實際調(diào)用的函數(shù)不是由指針類型決定的,而是由指針?biāo)傅呐缮悓嵗龥Q定的虛函數(shù)注意事項虛函數(shù)與多態(tài)同名隱藏規(guī)則對于虛函數(shù)來說仍然有效,并且規(guī)則不變當(dāng)基類構(gòu)造函數(shù)和析構(gòu)函數(shù)調(diào)用虛函數(shù)時,不會調(diào)用派生類的虛函數(shù)。這是因為當(dāng)基類被構(gòu)造時,對象還不是一個派生類對象;當(dāng)基類被析構(gòu)時,對象已經(jīng)不是一個派生類對象一般不要重寫繼承而來的非虛函數(shù)(雖然語法對此沒有強行限制),因為那會導(dǎo)致通過基類指針和派生類的指針或?qū)ο笳{(diào)用同名函數(shù)時,產(chǎn)生不同的結(jié)果,從而引起混亂虛函數(shù)注意事項虛函數(shù)與多態(tài)在重寫繼承來的虛函數(shù)時,如果函數(shù)有默認(rèn)形參值,不要重新定義不同的值。原因是,雖然虛函數(shù)是動態(tài)綁定的,但默認(rèn)形參值卻是靜態(tài)綁定的。也就是說,默認(rèn)形參值只能來自基類的定義。只有通過基類的指針或引用調(diào)用虛函數(shù)時,才會發(fā)生動態(tài)綁定;通過基類的對象調(diào)用虛函數(shù)時,不會發(fā)生動態(tài)綁定。這是因為,基類的指針和引用可以指向派生類的對象,但基類的對象卻不能表示派生類的對象。例如:Derivedd; //定義派生類對象Base*ptr=&d; //基類指針ptr可以指向派生類對象Base&ref=d; //基類引用ref可以作為派生類對象的別名Baseb=d; //調(diào)用Base的復(fù)制構(gòu)造函數(shù)用d構(gòu)造b,b的類型是Base而非Derived對象切片:由于執(zhí)行的是Base的復(fù)制構(gòu)造函數(shù),只有Base類的數(shù)據(jù)成員會被復(fù)制,Derived類中新增的數(shù)據(jù)成員既不會被復(fù)制,也沒有空間去存儲。虛函數(shù)成員虛函數(shù)與多態(tài)#include<iostream>usingnamespacestd;classBase1 //基類Base1定義{public:

virtualvoiddisplay()const; //虛函數(shù)};voidBase1::display()const{ cout<<"Base1::display()"<<endl;}classBase2:publicBase1 //公有派生類Base2定義{public:

voiddisplay()const; //覆蓋基類的虛函數(shù)};voidBase2::display()const{ cout<<"Base2::display()"<<endl;}虛函數(shù)成員虛函數(shù)與多態(tài)classDerived:publicBase2 //公有派生類{public:

voiddisplay()const; //覆蓋基類的虛函數(shù)};voidDerived::display()const{ cout<<"Derived::display()"<<endl;}voidfun(Base1ptr) //參數(shù)為指向基類對象的指針{

ptr.display(); //"對象指針->成員名"}intmain() //主函數(shù){

Base1base1; //定義Base1類對象

Base2base2; //定義Base2類對象

Derivedderived; //定義Derived類對象

fun(base1);//用Base1對象的指針調(diào)用fun函數(shù)

fun(base2);//用Base2對象的指針調(diào)用fun函數(shù)

fun(derived);//用Derived對象的指針調(diào)用fun函數(shù)

return0;}運行結(jié)果:Base1::display()Base1::display()Base1::display()注意,用基類對象無法形成動態(tài)綁定相關(guān)概念小結(jié)虛函數(shù)與多態(tài)重載、覆蓋與隱藏成員函數(shù)被重載的特征:相同的范圍(在同一個類中)函數(shù)名相同函數(shù)參數(shù)個數(shù)或類型不同virtual關(guān)鍵字可有可無覆蓋是指派生類的虛函數(shù)覆蓋基類的虛函數(shù),特征是:不同的范圍(分別位于派生類和基類)函數(shù)名字相同函數(shù)參數(shù)相同基類的被覆蓋函數(shù)必須有virtual關(guān)鍵字相關(guān)概念小結(jié)虛函數(shù)與多態(tài)成員函數(shù)被隱藏的特征是:不同的范圍(分別位于派生類和基類)函數(shù)名字相同函數(shù)參數(shù)可以相同,也可以不同virtual關(guān)鍵字可有可無基類中的同名函數(shù)在派生類中不可見(即被隱藏,除非用域操作符指定基類類型)從內(nèi)向外尋找函數(shù)名多態(tài)性多態(tài)性概述運算符重載虛函數(shù)與多態(tài)虛析構(gòu)函數(shù)純虛函數(shù)與抽象類程序?qū)嵗鄳B(tài)類型小結(jié)運行時類型識別綜合實例虛析構(gòu)函數(shù)為什么需要虛析構(gòu)函數(shù)?可能通過基類指針刪除派生類對象如果你打算允許其他人通過基類指針調(diào)用對象的析構(gòu)函數(shù)(通過delete這樣做是正常的),就需要讓基類的析構(gòu)函數(shù)成為虛函數(shù),否則執(zhí)行delete的結(jié)果是不確定的虛析構(gòu)函數(shù)舉例虛析構(gòu)函數(shù)#include<iostream>usingnamespacestd;classBase{public: ~Base();};Base::~Base(){ cout<<"Basedestructor"<<endl;}classDerived:publicBase{public: Derived(); ~Derived();private: int*p;};Derived::Derived(){

p=newint(0);}Derived::~Derived(){ cout<<"Deriveddestructor"<<endl;

deletep;}voidfun(Base*b){

deleteb;}intmain(){ Base*b=newDerived(); fun(b); return0;}虛析構(gòu)函數(shù)運行時結(jié)果:Basedestructor避免上述錯誤的有效方法就是將析構(gòu)函數(shù)聲明為虛函數(shù),運行結(jié)果變?yōu)椋篋eriveddestructorBasedestructor虛析構(gòu)函數(shù)舉例虛析構(gòu)函數(shù)舉例虛析構(gòu)函數(shù)#include<iostream>usingnamespacestd;classBase{public:

virtual~Base();};Base::~Base(){ cout<<"Basedestructor"<<endl;}classDerived:publicBase{public: Derived(); ~Derived();private: int*p;};Derived::Derived(){

p=newint(0);}Derived::~Derived(){ cout<<"Deriveddestructor"<<endl;

deletep;}voidfun(Base*b){

deleteb;}intmain(){ Base*b=newDerived(); fun(b); return0;}多態(tài)性多態(tài)性概述運算符重載虛函數(shù)與多態(tài)虛析構(gòu)函數(shù)純虛函數(shù)與抽象類程序?qū)嵗鄳B(tài)類型小結(jié)運行時類型識別綜合實例純虛函數(shù)與抽象類純虛函數(shù)是一個在基類中聲明的虛函數(shù),它在該基類中沒有定義具體的操作內(nèi)容,要求各派生類根據(jù)實際需要定義自己的版本,純虛函數(shù)的聲明格式為:virtual函數(shù)類型函數(shù)名(參數(shù)表)=0;帶有純虛函數(shù)的類稱為抽象類,這樣的類不可實例化:class類名{virtual類型函數(shù)名(參數(shù)表)=0;//純虛函數(shù)

...}純虛函數(shù)純虛函數(shù)的作用:1)表示相應(yīng)的類為抽象類,不可實例化;子類必須給出實現(xiàn)才能實例化;2)支持通過抽象類指針實現(xiàn)對應(yīng)函數(shù)的動態(tài)綁定(即多態(tài))。純虛函數(shù)與抽象類作用抽象類為抽象和設(shè)計的目的而聲明,將有關(guān)的數(shù)據(jù)和行為組織在一個繼承層次結(jié)構(gòu)中,保證派生類具有要求的行為。對于抽象類中無法實現(xiàn)的函數(shù),可以聲明為純虛函數(shù),留給派生類去實現(xiàn)。注意抽象類只能作為基類來使用不能聲明抽象類的對象析構(gòu)函數(shù)可以是虛函數(shù),但構(gòu)造函數(shù)不能是虛函數(shù)抽象類抽象類相當(dāng)于一個“半成品”,它為派生類的實現(xiàn)提供基礎(chǔ),但自己不可以用于產(chǎn)品實現(xiàn)。抽象類舉例純虛函數(shù)與抽象類#include<iostream>usingnamespacestd;classBase1 //基類Base1定義{public:

virtualvoiddisplay()const=0; //純虛函數(shù)};classBase2:publicBase1

//公有派生類Base2定義{public: voiddisplay()const; //覆蓋基類的虛函數(shù)};voidBase2::display()const{ cout<<"Base2::display()"<<endl;}抽象類舉例純虛函數(shù)與抽象類classDerived:publicBase2

//公有派生類Derived定義{public: voiddisplay()const; //覆蓋基類的虛函數(shù)};voidDerived::display()const{ cout<<"Derived::display()"<<endl;}voidfun(Base1*ptr) //參數(shù)為指向基類對象的指針{

ptr->display(); //"對象指針->成員名"}intmain() //主函數(shù){

Base2base2; //定義Base2類對象

Derivedderived; //定義Derived類對象

fun(&base2); //用Base2對象的指針調(diào)用fun函數(shù)

fun(&derived); //用Derived對象的指針調(diào)用fun函數(shù)

return0;}運行結(jié)果:Base2::display()Derived::display()多態(tài)性多態(tài)性概述運算符重載虛函數(shù)與多態(tài)虛析構(gòu)函數(shù)純虛函數(shù)與抽象類程序?qū)嵗鄳B(tài)類型小結(jié)運行時類型識別綜合實例程序?qū)嵗儾介L梯形積分算法求解函數(shù)的定積分我們只考慮最簡單的情況,設(shè)被積函數(shù)是一個一元函數(shù),定積分表達(dá)式為:積分表示的意義是一元函數(shù)f(x)在區(qū)間a到b之間與x軸所夾的面積算法基本原理y

xabxkxk-1hIkf(x)程序?qū)嵗儾介L梯形積分算法求解函數(shù)的定積分在每個小區(qū)間上都用小的梯形面積來近似原函數(shù)的積分,當(dāng)小區(qū)間足夠小時,我們就可以得到原來積分的近似值。每個小區(qū)間的面積值公式:實際計算中步長h逐次減半,反復(fù)利用上述求積公式進行計算,直到所求得的積分結(jié)果滿足要求的精度為止。并得到遞推公式:算法基本原理程序?qū)嵗儾介L梯形積分算法求解函數(shù)的定積分算法基本原理MyFunction<<const>>+operator()(x:double):doubleIntegration<<const,abstract>>+operator()(a:double,b:double,eps:double):doubleFunction<<const,abstract>>+operator()(x:double):doubleTrapz+Trapz(pf:constF&)<<const>>+operator()(a:double,b:double,eps:double):double-f程序?qū)嵗儾介L梯形積分算法求解函數(shù)的定積分我們求一個測試函數(shù)在某給定區(qū)間的積分值,對整個程序進行了測試,誤差為10-7。測試函數(shù):整個程序分為三個獨立的文件,Trapzint.h文件包括類的定義,Trapzint.cpp文件包括類的成員函數(shù)實現(xiàn)。文件intmain.cpp是程序的主函數(shù),主函數(shù)中定義了函數(shù)類Fun和梯形積分類Trapz的對象源程序及說明程序?qū)嵗儾介L梯形積分算法求解函數(shù)的定積分源程序//Trapzint.h文件一,類定義classFunction //抽象類Function的定義{ public:

virtualdoubleoperator()(doublex)const=0; //純虛函數(shù)重載運算符() virtual~Function(){}};classMyFunction:publicFunction //公有派生類MyFunction定義{ public:

virtualdoubleoperator()(doublex)const; //覆蓋虛函數(shù)};classIntegration //抽象類Integration定義{ public:

virtualdoubleoperator()(doublea,doubleb,doubleeps)const=0; virtual~Integration(){}};程序?qū)嵗儾介L梯形積分算法求解函數(shù)的定積分源程序classTrapz:publicIntegration //公有派生類Trapz定義{ public: Trapz(constFunction&f):f(f){} //構(gòu)造函數(shù)

virtualdoubleoperator()(doublea,doubleb,doubleeps)const;private: constFunction&f; //私有成員,F(xiàn)unction類對象的引用};//Trapzint.cpp文件二,類實現(xiàn)#include"Trapzint.h" //包含類的定義頭文件#include<cmath>doubleMyFunction::operator()(doublex)const//被積函數(shù){

returnlog(1.0+x)/(1.0+x*x);}//積分運算過程,重載為運算符()doubleTrapz::operator()(doublea,doubleb,doubleeps)const{ booldone=false; //是Trapz類的虛函數(shù)成員

intn=1; doubleh=b-a; doubletn=h*(f(a)+f(b))/2; //計算n=1時的積分值

doublet2n;

引用成員只能在初始化列表中初始化程序?qū)嵗儾介L梯形積分算法求解函數(shù)的定積分源程序 do{ doublesum=0; for(intk=0;k<n;k++) { doublex=a+(k+0.5)*h; sum+=f(x); }

t2n=(tn+h*sum)/2.0; //變步長梯形法計算

if(fabs(t2n-tn)<eps) done=true; //判斷積分誤差

else { //進行下一步計算

tn=t2n; n*=2; h/=2; } }while(!done); returnt2n;}程序?qū)嵗儾介L梯形積分算法求解函數(shù)的定積分源程序#include"Trapzint.h" //類定義頭文件#include<iostream>#include<iomanip>usingnamespacestd;intmain() //主函數(shù){

MyFunctionf; //定義MyFunction類的對象

Trapztrapz(f); //定義Trapz類的對象

//計算并輸出積分結(jié)果

cout<<"TRAPZInt:"<<setprecision(7)<<trapz(0,2,1e-7)<<endl; return0;}運行結(jié)果:TRAPZInt:0.5548952多態(tài)性多態(tài)性概述運算符重載虛函數(shù)與多態(tài)虛析構(gòu)函數(shù)純虛函數(shù)與抽象類程序?qū)嵗鄳B(tài)類型小結(jié)運行時類型識別綜合實例多態(tài)類型小結(jié)多態(tài)類型與非多態(tài)類型有虛函數(shù)的類類型稱為多態(tài)類型其它類型皆為非多態(tài)類型二者的差異語言層面的差異多態(tài)類型支持運行時類型識別多態(tài)類型對象占用額外的空間設(shè)計原則上的差異多態(tài)類型與非多態(tài)類型多態(tài)類型小結(jié)多態(tài)類型多態(tài)類型的析構(gòu)函數(shù)一般應(yīng)為虛函數(shù)非多態(tài)類型非多態(tài)類型不宜作為公共基類由于沒有利用動態(tài)多態(tài)性,一般可以用組合,而無需用公有繼承如果繼承,則由于析構(gòu)函數(shù)不是虛函數(shù),刪除對象時所執(zhí)行操作與指針類型有關(guān),易引起混亂把不需被繼承的類型設(shè)定為非多態(tài)類型由于成員函數(shù)都是靜態(tài)綁定,調(diào)用速度較快對象占用空間較小設(shè)計原則多態(tài)性多態(tài)性概述運算符重載虛函數(shù)與多態(tài)虛析構(gòu)函數(shù)純虛函數(shù)與抽象類程序?qū)嵗鄳B(tài)類型小結(jié)運行時類型識別綜合實例運行時類型識別運行時類型識別允許在運行時通過基類指針(或引用)辨別對象所屬的具體派生類只對多態(tài)類型適用比虛函數(shù)動態(tài)綁定的開銷更大,因此應(yīng)僅對虛函數(shù)無法解決的問題使用運行時類型識別的方式用dynamic_cast做類型轉(zhuǎn)換的嘗試用typeid直接獲取類型信息運行時類型識別語法形式dynamic_cast<目的類型>(表達(dá)式)功能將基類指針轉(zhuǎn)換為派生類指針,將基類引用轉(zhuǎn)換為派生類引用;轉(zhuǎn)換是有條件的如果指針(或引用)所指對象的實際類型與轉(zhuǎn)換的目的類型兼容,則轉(zhuǎn)換成功進行否則如執(zhí)行的是指針類型的轉(zhuǎn)換,則得到空指針;如執(zhí)行的是引用類型的轉(zhuǎn)換,則拋出異常使用dynamic_castdynamic_cast用法示例運行時類型識別#include<iostream>usingnamespacestd;classBase{public: virtualvoidfun1(){cout<<"Base::fun1()"<<endl;} virtual~Base(){}};classDerived1:publicBase{public: virtualvoidfun1(){cout<<"Derived1::fun1()"<<endl;} virtualvoidfun2(){cout<<"Derived1::fun2()"<<endl;}};classDerived2:publicDerived1{public: virtualvoidfun1(){cout<<"Derived2::fun1()"<<endl;} virtualvoidfun2(){cout<<"Derived2::fun2()"<<endl;}};dynamic_cast用法示例運行時類型識別voidfun(Base*b){ b->fun1(); //嘗試將b轉(zhuǎn)換為Derived1指針

Derived1*d=dynamic_cast<Derived1*>(b); //判斷轉(zhuǎn)換是否成功

if(d!=0)d->fun2();}intmain(){ Baseb; fun(&b);

Derived1d1; fun(&d1); Derived2d2; fun(&d2); return0;}運行結(jié)果:Base::fun1()Derived1::fun1()Derived1::fun2()Derived2::fun1()Derived2::fun2()運行時類型識別語法形式typeid(表達(dá)式)typeid(類型說明符)功能獲得表達(dá)式或類型說明符的類型信息表達(dá)式有多態(tài)類型時,會被求值,并得到動態(tài)類型信息否則,表達(dá)式不被求值,只能得到靜態(tài)的類型信息類型信息用type_info對象表示type_info是typeinfo頭文件中聲明的類typeid的結(jié)果是type_info類型的常引用可以用type_info的重載的“==”、“!=”操作符比較兩類型的異同type_info的name成員函數(shù)返回類型名稱,類型為constchar*用typeid獲取運行時類型信息typeid用法示例運行時類型識別#include<iostream>#include<typeinfo>usingnamespacestd;classBase{public: virtual~Base(){}};classDerived:publicBase{};voidfun(Base*b){ //得到表示b和*b類型信息的對象

consttype_info&info1=typeid(b); consttype_info&info2=typeid(*b);

cout<<"typeid(b):"<<()<<endl;cout<<"typeid(*b):"<<()<<endl;//判斷*b是否為Base類型if(info2==typeid(Base)) cout<<"Abaseclass!"<<endl;}intmain(){Baseb;fun(&b);Derivedd;fun(&d);return0;}結(jié)果:typeid(b):classBase*typeid(*b):classBaseAbaseclass!typeid(b):classBase*typeid(*b):classDerived多態(tài)性多態(tài)性概述運算符重載虛函數(shù)與多態(tài)虛析構(gòu)函數(shù)純虛函數(shù)與抽象類程序?qū)嵗鄳B(tài)類型小結(jié)運行時類型識別綜合實例綜合實例

——對個人銀行賬戶管理程序的改進本例在第七章例7-10的基礎(chǔ)上,對Account類做了如下改進:(1)將show函數(shù)聲明為虛函數(shù),因此通過指向CreditAccount類實例的Accout類型的指針來調(diào)用show函數(shù)時,被實際調(diào)用的將是為CreditAccount類定義的show函數(shù),這樣,如果創(chuàng)建一個Account指針類型的數(shù)組,使各個元素分別指向各個賬戶對象,就可以通過一個循環(huán)來調(diào)用它們的show函數(shù);(2)在Account類中添加deposit、withdraw、settle這3個函數(shù)的聲明,且將它們都聲明為純虛函數(shù),這使得通過基類的指針可以調(diào)用派生類的相應(yīng)函數(shù),而且無需給出它們在基類中的實現(xiàn)。經(jīng)過這一改動之后,Account類就變成了抽象類。- acc:Accumulator- rate:doubleSavingsAccount+ SavingsAccount(date:Date,id:int,rate:double)<<const>>+ getRate():double+ deposit(date:Date,amount:double,desc:string)+ withdraw(date:Date,amount:double,desc:string)+ settle(date:Date)11- acc:Accumulator- credit:double- rate:double- fee:doubleCreditAccount<<const>>- getDebt():double+ CreditAccount(date:Date,id:int,credit:double,rate:double,fee:double)<<const>>+ getCredit():double<<const>>+ getRate():double<<const>>+ getFee():double<<const>>+ getAvailableCredit():double+ deposit(date:Date,amount:double,desc:string)+ withdraw(date:Date,amount:double,desc:string)+ settle(date:Date)<<const>>+ show()- year:int- month:int- day:int- totalDays:int+ Date(year:int,month:int,day:int)<<const>>+ getYear():int<<const>>+ getMonth():int<<const>>+ getDay():int<<const>>+getMaxDay():int<<const>>+isLeapYear():bool<<const>>+show()<<const>>+operator-(date:Date):intDate- lastDate:Date- value:double- sum:double+ Accumulator(date:Date,value:double)<<const>>+ getSum(date:Date):double+ change(date:Date,value:double)+ reset(date:Date,value:double)Accumulator# Account(date:Date,id:int)# record(date:Date,amount:double,desc:string)<<const>>#error(msg:string) <<const>>+ getId():int<<const>>+ getBalance():double<<abstract>>+deposit(date:Date,amount:double,desc:string)<<abstract>>+withdraw(date:Date,amount:double,desc:string)<<abstract>>+settle(date:Date)<<virtual>><<const>>+show()<<static>>+ getTotal():doubleAccount- id:string- balance:double- total:double1綜合實例——對個人銀行賬戶管理程序的改進//date.h#ifndef__DATE_H__#define__DATE_H__classDate //日期類{ private: intm_year; //年

intm_month; //月

intm_day; //日

intm_totalDays; //該日期是從公元元年1月1日開始的第幾天public: Date(intyear,intmonth,intday); //用年、月、日構(gòu)造日期

intgetYear()const{returnm_year;} intgetMonth()const{returnm_month;} intgetDay()const{returnm_day;} intgetMaxDay()const; //獲得當(dāng)月有多少天

boolisLeapYear()const //判斷當(dāng)年是否為閏年 {

returnm_year%4==0&&m_year%100!=0||m_year%400==0; } voidshow()const; //輸出當(dāng)前日期 //計算兩個日期之間差多少天

intoperator-(constDate&date)const {

returnm_totalDays-date.m_totalDays; }};#endif//__DATE_H__綜合實例——對個人銀行賬戶管理程序的改進//accumulator.h#ifndef__ACCUMULATOR_H__#define__ACCUMULATOR_H__#include"date.h"classAccumulator //將某個數(shù)值按日累加{ private: Datem_lastDate; //上次變更數(shù)值的時期

doublem_value; //數(shù)值的當(dāng)前值

doublem_sum; //數(shù)值按日累加之和public: doublegetSum(constDate&date)const { returnm_sum+m_value*(date-m_lastDate); } //該類其它成員函數(shù)的原型和實現(xiàn)與第四講中例子完全相同,不再重復(fù)給出};#endif//__ACCUMULATOR_H__綜合實例——對個人銀行賬戶管理程序的改進//account.h#ifndef__ACCOUNT_H__#define__ACCOUNT_H__#include"date.h"#include"accumulator.h"#include<string>classAccount //賬戶類{private: std::stringm_id; //帳號

doublem_balance; //余額

staticdoubles_total;//所有賬戶的總金額protected: //供派生類調(diào)用的構(gòu)造函數(shù),id為賬戶

Account(constDate&date,conststd::string&id); //記錄一筆帳,date為日期,amount為金額,desc為說明

voidrecord(constDate&date,doubleamount,conststd::string&desc); //報告錯誤信息

voiderror(conststd::string&msg)const;綜合實例——對個人銀行賬戶管理程序的改進public: conststd::string&getId()const{returnm_id;} doublegetBalance()const{returnm_balance;}

staticdoublegetTotal(){returns_total;} //存入現(xiàn)金,date為日期,amount為金額,desc為款項說明

virtualvoiddeposit(constDate&date,doubleamount,conststd::string&desc)=0; //取出現(xiàn)金,date為日期,amount為金額,desc為款項說明

virtualvoidwithdraw(constDate&date,doubleamount,conststd::string&desc)=0; //結(jié)算(計算利息、年費等),每月結(jié)算一次,date為結(jié)算日期

virtualvoidsettle(constDate&date)=0; //顯示賬戶信息

virtualvoidshow()const;};//SavingsAccount和CreditAccount兩個類的定義與第四講中的例子完全相同,不再重復(fù)給出#endif//__ACCOUNT_H__綜合實例——對個人銀行賬戶管理程序的改進//account.cpp//與第四講中相同,不再重復(fù)給出

//main.cpp#include"account.h"#include<iostream>usingnamespacestd;intmain(){ Datedate(2008,11,1); //起始日期

//建立幾個賬戶

SavingsAccountsa1(date,"S3755217",0.015); SavingsAccountsa2(date,"02342342",0.015); CreditAccountca(date,"C5392394",10000,0.0005,50);

Account*accounts[]={&sa1,&sa2,&ca}; constintn=sizeof(accounts)/sizeof(Account*);//賬戶總數(shù)

cout<<"(d)deposit(w)withdraw(s)show(c)changeday(n)nextmonth(e)exit"<<endl; charcmd;綜合實例——對個人銀行賬戶管理程序的改進 do{ //顯示日期和總金額

date.show(); cout<<"\tTotal:"<<Account::getTotal()<<"\tcommand>"; intindex,day

溫馨提示

  • 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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論