第10章 繼承與多態(tài)_第1頁
第10章 繼承與多態(tài)_第2頁
第10章 繼承與多態(tài)_第3頁
第10章 繼承與多態(tài)_第4頁
第10章 繼承與多態(tài)_第5頁
已閱讀5頁,還剩93頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第十章繼承與多態(tài)第一節(jié)

繼承繼承的概念繼承的權(quán)限類型兼容什么是類的繼承?10.1.1繼承的概念10.1.1繼承的概念鑿壁借光《西京雜記》卷二:匡衡字稚圭,勤學(xué)而無燭,鄰舍有燭而不逮。衡乃穿壁引其光,以書映光而讀之。原指西漢匡衡鑿穿墻壁引鄰舍之燭光讀書。后用來形容讀書勤奮刻苦。也稱“鑿壁偷光”。10.1.1繼承的概念頭懸梁錐刺股《戰(zhàn)國策·秦策一》:“(蘇秦)讀書欲睡,引錐自刺其股?!毙稳菘炭鄬W(xué)習(xí)10.1.1繼承的概念約法三章

漢·司馬遷《史記·高祖本紀(jì)》:“與父老約法三章耳:殺人者死;傷人及盜抵罪?!?/p>

原指劉邦進入咸陽,廢除秦法之后制定出三條簡單法令。后泛指約好或提出幾條規(guī)定,大家共同遵守。10.1.1繼承的概念10.1.1繼承的概念所謂繼承就是從“先輩”處獲得特性,它是客觀世界事物之間的一種重要關(guān)系。10.1.1繼承的概念C++中,繼承就是在現(xiàn)有類的基礎(chǔ)上建立新類,即新類從已有類中得到屬性和行為,并可以在新類中添加新的屬性及方法,新構(gòu)建的類稱為子類或派生類,現(xiàn)有類稱為父類或基類?;惐扰缮惛鼮槌橄蠛鸵话慊?,派生類比基類更為具體和個性化。在C++中,聲明一個類繼承另一個類的格式如下所示:class派生類名稱:繼承方式基類名稱{

派生類成員聲明};10.1.1繼承的概念10.1.1繼承的概念classTraditional{public:voidvirtues(){cout<<“中華民族優(yōu)良傳統(tǒng)美德"<<endl;}}; classOffspring:publicTraditional{public:offspring(stringname);voidInherit();private:string_name;};Offspring::offspring(stringname){_name=name;}voidOffspring::Inherit(){cout<<_name<<endl;}intmain(){Offspringoffspring(“新時代的我們傳承");offspring.Inherit();

offspring.virtues();return0;}10.1.1繼承的概念新時代的我們傳承中華民族優(yōu)良傳統(tǒng)美德運行結(jié)果:上述代碼中,首先定義了類Traditional,Traditional類中有一個virtues()函數(shù),表示傳統(tǒng)美德;然后定義了一個后代類Offspring公有繼承Traditional類,Offspring新增了Inherit()函數(shù),表示“傳承”行為。由于Offspring類繼承自Traditional類,它也繼承了Traditional類的virtues()函數(shù),因此使用對象Offspring可以調(diào)用Inherit(),也可以調(diào)用virtues()函數(shù)。10.1.1繼承的概念基類的構(gòu)造函數(shù)和析構(gòu)函數(shù)不可被繼承。派生類繼承了基類的全部數(shù)據(jù)成員和除了構(gòu)造、析構(gòu)函數(shù)之外的所有成員函數(shù)。派生類對于基類成員的繼承是沒有選擇的,不能選擇接收或舍棄基類中的某些成員。派生類中除了與基類同名的成員外還可以添加新成員,用于實現(xiàn)新功能,保證了派生類的功能在基類基礎(chǔ)上的發(fā)展。注意多個派生類可以繼承自一個基類,示例代碼如下所示: classA{……}; classB:publicA{……};//類B繼承自類A classC:publicA{……};//類C繼承自類AC++中可以通過派生形成類的層次結(jié)構(gòu),即一個基類可以是另一個更高層次類的派生類,而另一個派生類也可以繼續(xù)產(chǎn)生派生類,示例代碼如下所示: classA{……}; classB:publicA{……};//類B是類A的派生類 classC:publicB{……};//類C是類B的派生類注意10.1.1繼承的概念10.1.2類成員的訪問權(quán)限第五十三條中華人民共和國公民必須遵守憲法和法律,保守國家秘密,愛護公共財產(chǎn),遵守勞動紀(jì)律,遵守公共秩序,尊重社會公德。中華人民共和國憲法10.1.2類成員的訪問權(quán)限public成員protected成員private成員

類內(nèi)和派生類可以訪問。派生類訪問權(quán)限由繼承方式?jīng)Q定。類外不可訪問,不可繼承。類成員的訪問權(quán)限類外和類內(nèi)可以訪問。其訪問權(quán)限由繼承方式?jīng)Q定。10.1.2繼承的權(quán)限

C++中,繼承方式有public、protected、private訪問方式的不同主要體現(xiàn)在以下兩個方面:

派生類新增成員對從基類繼承來的成員的訪問方式。

派生類對象對從基類繼承來的成員的訪問方式。繼承的權(quán)限公有繼承是指通過public方式繼承基類,公有繼承的派生類定義形式如下所示: class派生類名稱:public基類名稱 {

派生類成員聲明 };公有繼承方式-public方式10.1.2繼承的權(quán)限公有繼承對基類成員的訪問屬性控制公有繼承基類成員public成員protected成員private成員作為派生類成員public成員protected成員不可訪問成員10.1.2繼承的權(quán)限私有繼承是指通過private方式繼承基類,私有繼承的派生類定義形式如下: class派生類名稱:private基類名稱 {

派生類成員聲明 };私有繼承方式-private方式10.1.2繼承的權(quán)限私有繼承對基類成員的訪問屬性控制私有繼承基類成員public成員protected成員private成員作為派生類成員private成員private成員不可訪問成員10.1.2繼承的權(quán)限保護繼承是指通過protected方式繼承基類,保護繼承的派生類定義形式如下所示: class派生類名稱:protected基類名稱 {

派生類成員聲明 };保護繼承-protected方式10.1.2繼承的權(quán)限保護繼承對基類成員的訪問屬性控制保護繼承基類成員public成員protected成員private成員作為派生類成員protected成員protected成員不可訪問成員10.1.2繼承的權(quán)限繼承方式對基類成員的訪問屬性控制繼承方式基類成員public成員protected成員private成員公有繼承public成員protected成員不可訪問成員保護繼承protected成員protected成員不可訪問成員私有繼承private成員private成員不可訪問成員10.1.2繼承的權(quán)限10.1.2繼承的權(quán)限信息安全法是指維護信息安全,預(yù)防信息犯罪的刑事法律規(guī)范的總稱。作為一名合格的中華人民共和國公民,我們有義務(wù)和責(zé)任保護國家信息不受侵害信息安全法10.1.3類型兼容基類與派生類對象之間也具有賦值兼容的關(guān)系,可以進行類型間的轉(zhuǎn)換。派生類是從它的直接和間接基類繼承而來,尤其是公有繼承的派生類保持了基類的所有特征。C++中類對象的內(nèi)存空間大小完全取決于類的數(shù)據(jù)成員。類型兼容例3-5詳解10.1.3類型兼容派生類對象可以向基類對象賦值。派生類對象可以替代基類對象向基類對象的引用進行賦值或初始化。如果函數(shù)的參數(shù)是基類對象或基類對象的引用,函數(shù)調(diào)用時的實參可以是派生類對象。派生類對象的地址可以賦值給基類指針變量。派生類對象操作基類對象的四種方法10.1.3類型兼容派生類對象向基類對象賦值時,將基類數(shù)據(jù)成員賦值,派生類新增的數(shù)據(jù)成員值被舍棄,不存在對成員函數(shù)的賦值。由派生類中數(shù)據(jù)成員的排列情況可知,基類數(shù)據(jù)成員排列在最前端,因此可以使用派生類對象向基類對象賦值,基類對象會獲取派生類對象中的基類數(shù)據(jù)。 ClassAobj_a;//定義基類ClassA對象obj_a ClassBobj_b;//定義類ClassA的公有派生類ClassB的對象obj_b obj_a=obj_b;//用派生類ClassB對象obj_b對基類對象obj_a賦值向基類對象賦值10.1.3類型兼容引用refa是obj_a的別名,refa和obj_a共享同一段存儲單元??梢杂米宇悓ο蟪跏蓟胷efa,將上面最后一行改為: //定義基類ClassA對象的引用變量refa并用派生類ClassB對象obj_b對其初始化 ClassAobj_a; ClassBobj_b;

ClassA&refa=obj_b;替代基類對象向基類對象的引用進行賦值或初始化10.1.3類型兼容賦值后不能通過基類對象obj_a訪問派生類對象obj_b的成員,因為obj_b的成員與obj_a的成員不同。假設(shè)newmember是派生類ClassB中增加的公有數(shù)據(jù)成員,若有下列代碼,執(zhí)行錯誤。派生類型關(guān)系是單向的,不可逆。ClassB是ClassA的派生類,只能用派生類對象對其基類對象賦值,而不能用基類對象對其派生類對象賦值,原因顯而易見,因為基類對象不包含派生類的成員,無法對派生類的成員賦值。同理,同一基類的不同派生類對象之間也不能賦值。注意10.1.3類型兼容定義函數(shù)func(),其形參為基類引用,具體代碼如下所示: voidfunc(ClassA&ref)//形參是類ClassA的對象的引用 { cout<<ref.num<<endl;//輸出該引用所代表的對象的數(shù)據(jù)成員num }函數(shù)的形參是類ClassA對象的引用,本來實參應(yīng)該為ClassA類的對象。由于派生類對象與基類對象賦值兼容,派生類對象能自動轉(zhuǎn)換類型,在調(diào)用func()函數(shù)時可以用派生類ClassB的對象obj_b作實參。 func(obj_b);//輸出類ClassB的對象obj_b的基類數(shù)據(jù)成員num的值如果函數(shù)的參數(shù)是基類對象或基類對象的引用,函數(shù)調(diào)用時的實參可以是派生類對象10.1.3類型兼容指向基類對象的指針變量也可以指向派生類對象派生類對象的地址可以賦值給基類指針變量Catcat; //定義派生類對象catAnimal*panimal=&cat; //定義基類指針并初始化為cat地址panimal->set_age(5); //基類指針調(diào)用基類set_age()函數(shù)cout<<"basetype:age="<<panimal->get_age()<<endl;//指針指針調(diào)用基類get_age()函數(shù)cat.speak(); //派生類對象調(diào)用speak()函數(shù)panimal->speak(); //基類指針調(diào)用speak()函數(shù)例3-6詳解第二節(jié)派生類構(gòu)造函數(shù)析構(gòu)函數(shù)隱藏基類函數(shù)10.2.1構(gòu)造函數(shù)由于派生類繼承了基類的成員,在構(gòu)建派生類對象時,需要創(chuàng)建從基類繼承的部分。因為基類的構(gòu)造函數(shù)和析構(gòu)函數(shù)是不能被繼承的。派生類的構(gòu)造函數(shù)需要調(diào)用基類的構(gòu)造函數(shù)對繼承下來的數(shù)據(jù)進行初始化,同時還要對新增加的成員初始化。引出問題如何對基類構(gòu)造函數(shù)進行初始化呢?10.2.1構(gòu)造函數(shù)

若基類的構(gòu)造函數(shù)為無參的構(gòu)造函數(shù),派生類構(gòu)造函數(shù)的定義不必顯式調(diào)用基類無參的構(gòu)造函數(shù)系統(tǒng)會自動調(diào)用基類無參的構(gòu)造函數(shù),用來給從基類繼承來的成員初始化。此時,派生類構(gòu)造函數(shù)定義方式和普通類相同?;惖臉?gòu)造函數(shù)為無參構(gòu)造函數(shù)(1)執(zhí)行基類的無參構(gòu)造函數(shù)。(2)執(zhí)行派生類的構(gòu)造函數(shù)。構(gòu)造函數(shù)的調(diào)用順序如下:(1)執(zhí)行派生類的析構(gòu)函數(shù)。(2)執(zhí)行基類的析構(gòu)函數(shù)。析構(gòu)函數(shù)的調(diào)用順序如下:10.2.1構(gòu)造函數(shù)

派生類名::派生類構(gòu)造函數(shù)名(參數(shù)列表):基類構(gòu)造函數(shù)名(基類構(gòu)造函數(shù)參數(shù)表) {

派生類新增成員的初始化語句 }派生類中定義帶參數(shù)構(gòu)造函數(shù)的形式如下所示10.2.1構(gòu)造函數(shù)派生類構(gòu)造函數(shù)名后面括號內(nèi)的參數(shù)列表中,應(yīng)包含基類和派生類構(gòu)造函數(shù)需要進行初始化的所有數(shù)據(jù)成員的參數(shù)值。冒號后面的內(nèi)容是要調(diào)用的基類構(gòu)造函數(shù),以及常量、全局變量的初始化。

當(dāng)基類的構(gòu)造函數(shù)使用一個或多個參數(shù)時,派生類中必須定義構(gòu)造函數(shù),提供將參數(shù)傳遞給基類構(gòu)造函數(shù)的方法,從而實現(xiàn)對基類數(shù)據(jù)成員的初始化。此時,派生類的構(gòu)造函數(shù)體可能為空,僅僅為了向基類傳遞數(shù)據(jù)。注意10.2.1構(gòu)造函數(shù)#include<iostream>//程序舉例usingnamespacestd;classAnimal//定義基類{public:

Animal(intcon_weight,intcon_age);//聲明帶參數(shù)的構(gòu)造函數(shù)private: intm_nWeight; intm_nAge;};Animal::Animal(intcon_weight,intcon_age)//定義基類帶參數(shù)的構(gòu)造函數(shù){ m_nWeight=con_weight; m_nAge=con_age; cout<<"Animalconstructorwithparam!"<<endl;}10.2.1構(gòu)造函數(shù)classCat:publicAnimal

//定義派生類{public:

Cat(stringcon_name,intcon_weight,intcon_age);//聲明派生類帶參數(shù)的構(gòu)造函數(shù)private: stringm_strName;};Cat::Cat(stringcon_name,intcon_weight,intcon_age):Animal(con_weight,con_age){ m_strName=con_name; cout<<"Catconstructorwithparam!"<<endl;}intmain(){ Catcat("Persian",5,7);//創(chuàng)建派生類對象 system("pause"); return0;}10.2.2析構(gòu)函數(shù)與構(gòu)造函數(shù)類似,派生類也不可以繼承基類的析構(gòu)函數(shù),若想完成派生類中新增數(shù)據(jù)成員的資源釋放,需要在派生類中定義析構(gòu)函數(shù)。同樣,若派生類中沒有顯式定義析構(gòu)函數(shù),編譯系統(tǒng)會提供一個默認(rèn)的析構(gòu)函數(shù)。析構(gòu)函數(shù)執(zhí)行次序與構(gòu)造函數(shù)相反,先要調(diào)用派生類的析構(gòu)函數(shù),然后再調(diào)用基類的析構(gòu)函數(shù)。析構(gòu)函數(shù)10.2.2析構(gòu)函數(shù)#include<iostream>usingnamespacestd;classAnimal//定義基類{public:

Animal(intcon_weight,intcon_age);//聲明帶參構(gòu)造函數(shù)

~Animal() { cout<<"Animaldestructor!"<<endl; system("pause"); } intget_age(){returnm_nAge;}//獲取m_nAge屬性值private: intm_nWeight,m_nAge;};Animal::Animal(intcon_weight,intcon_age)//定義基類帶參構(gòu)造函數(shù){ m_nWeight=con_weight; m_nAge=con_age; cout<<"Animalconstructorwithparam!"<<endl;}案例10.2.2析構(gòu)函數(shù)classCat:publicAnimal{//定義派生類public: //聲明帶參構(gòu)造函數(shù) Cat(stringcon_name,intcon_weight,intcon_age); ~Cat(){cout<<"Catdestructor!"<<endl;}//定義析構(gòu)函數(shù)private: stringm_strName;};//定義派生類帶參的構(gòu)造函數(shù)Cat::Cat(stringcon_name,intcon_weight,intcon_age):Animal(con_weight,con_age){ m_strName=con_name; cout<<"Catconstructorwithparam!"<<endl;}intmain(){

Catcat("Persian",3,4);//定義派生類對象 cout<<"catage="<<cat.get_age()<<endl;//調(diào)用get_age()函數(shù) return0;}10.2.3隱藏基類函數(shù)有時派生類需要根據(jù)自身特點改寫從基類繼承的函數(shù),比如動物都有叫聲,在描述動物的類中可以定義speak()函數(shù),不同的動物,叫聲也不同,比如貓、狗都有特定叫聲。若定義貓科類,該類繼承自動物類,繼承了speak()函數(shù),但在貓科類中需要改寫speak()函數(shù),用于描述貓?zhí)赜械慕新暋E缮愔兄匦露x基類同名函數(shù)的方法,稱為對基類函數(shù)的覆蓋或改寫,覆蓋后基類同名函數(shù)在派生類中被隱藏。定義派生類對象調(diào)用該函數(shù)時,調(diào)用的是自身的函數(shù),基類同名函數(shù)不被調(diào)用。如果要調(diào)用父類的同名成員函數(shù)怎么辦呢?調(diào)用格式如下:

派生類對象.基類名::函數(shù)名()隱藏基類函數(shù)10.2.2析構(gòu)函數(shù)#include<iostream>usingnamespacestd;classAnimal//定義基類{public:

voidspeak(){cout<<"animallanguage!"<<endl;//定義speak()函數(shù)}};classCat:publicAnimal//定義派生類{public:

voidspeak(){cout<<"catlanguage:miaomiao!"<<endl;

//定義speak()函數(shù)}};intmain(){

Catcat;//定義派生類對象 cat.speak();//通過派生類調(diào)用speak()函數(shù) cat.Animal::speak();

//調(diào)用基類同名函數(shù) system("pause"); return0;}第三節(jié)多重繼承問題一個類可以繼承自多個父類不?如果可以,如果多個父類有同名的函數(shù),那么派生類調(diào)用時如何區(qū)分?聲明多重繼承的方式多重繼承派生類的構(gòu)造函數(shù)多重繼承引起的二義性虛基類10.3.1聲明多重繼承的方式前面介紹的繼承方式都是單繼承,即派生類的基類只有一個。現(xiàn)實世界中,一個派生類往往會有多個基類。 classBird//定義鳥類 classFish//定義魚類 classWaterBird:publicBird,publicFish//定義水鳥類聲明多重繼承的方式10.3.2多重繼承派生類的構(gòu)造函數(shù)多重繼承派生類的構(gòu)造函數(shù)不但要對派生類中新增成員完成初始化,還要依次對各基類的繼承成員進行初始化。多重繼承派生類的構(gòu)造函數(shù)定義形式如下:

派生類名::派生類構(gòu)造函數(shù)名(參數(shù)總表):基類1構(gòu)造函數(shù)名(參數(shù)表1),基類2構(gòu)造函數(shù)名(參數(shù)表2),… {

派生類構(gòu)造函數(shù)體 }多重繼承派生類的構(gòu)造函數(shù)10.3.2多重繼承派生類的構(gòu)造函數(shù)派生類中新增加的成員還可以是類對象。假如派生類是多重繼承,并且新增數(shù)據(jù)成員有一個或多個對象成員,那么派生類需要初始化的數(shù)據(jù)有三部分:基類構(gòu)造函數(shù)、新增類對象成員和新增普通成員。這種復(fù)雜派生類的構(gòu)造函數(shù)定義形式如下所示:

派生類名::派生類構(gòu)造函數(shù)名(參數(shù)列表):基類1構(gòu)造函數(shù)名(參數(shù)表1),基類2構(gòu)造函數(shù)名(參數(shù)表2),…子對象名1(參數(shù)表n),子對象名2(參數(shù)表n+1) {

派生類新增普通數(shù)據(jù)成員的初始化 }多重繼承派生類的構(gòu)造函數(shù)10.3.3多重繼承引起的二義性調(diào)用不同基類中的同名成員時產(chǎn)生二義性

多個基類出現(xiàn)同名成員時,派生類對象訪問該成員時會出現(xiàn)二義性。

解決辦法:派生類對象.基類::成員名()。派生類中訪問公有成員時產(chǎn)生二義性

多重繼承中派生類有多個基類,多個基類又由同一個基類派生,則在派生類中訪問公共基類成員時會出現(xiàn)二義性。二義性10.3.4虛基類在多重繼承中,若一個類聲明為虛基類,則能保證一個派生類間接地多次繼承該類時,派生類中只繼承該基類的一份成員,避免了派生類中訪問公共基類公有屬性多份拷貝的二義性。虛基類的定義形式是在派生類定義時基類名稱前加virtual關(guān)鍵字,具體形式如下所示: class派生類名:virtual繼承方式基類名 {

派生類成員 };虛基類10.3.4虛基類#include<iostream>usingnamespacestd;classAnimal//定義類Animal{public:

Animal(intage):m_nAge(age){cout<<"Animal constructor!"<<endl;}protected: intm_nAge;};classBird:virtualpublicAnimal{public:

Bird(intage,intfh):Animal(age)

{ cout<<"Birdconstructor!"<<endl; m_nFlightAltitude=fh; }

intget_flightaltitude(){returnm_nFlightAltitude;}private: intm_nFlightAltitude;};classFish:virtualpublicAnimal{public:

Fish(intage,intspeed):Animal(age)

{cout<<"Fishconstructor!"<<endl; m_nSwimSpeed=speed; }

intget_swimspeed(){returnm_nSwimSpeed;}private: intm_nSwimSpeed;};classWaterBird:publicBird,publicFish//定義水鳥類{public:WaterBird(intb_age,intf_age,intfh,intspeed):Bird(b_age,fh),Fish(f_age,speed) {cout<<"WaterBirdconstructor!"<<endl;} voidprint_animalage

{cout<<"age="<<Bird::m_nAge<<endl; cout<<"age="<<Fish::m_nAge<<endl;}};10.3.4虛基類intmain(){

WaterBirdwaterbird(5,6,20,30); //定義水鳥對象

cout<<"waterbirdflightaltitude:"<<waterbird.get_flightaltitude() <<",swimmingspeed:"<<waterbird.get_swimspeed()<<endl; waterbird.print_animalage(); system("pause"); return0;}程序的運行結(jié)果如下。Animalconstructor!Birdconstructor!Fishconstructor!WaterBirdconstructor!waterbirdflightaltitude:20,swimmingspeed:30age=5第四節(jié)多態(tài)多態(tài)性的概念虛函數(shù)純虛函數(shù)10.4.1多態(tài)性概念多態(tài)是面向?qū)ο蟪绦蛟O(shè)計的重要特征之一,它與封裝、繼承共同構(gòu)成了面向?qū)ο蟪绦蛟O(shè)計的三大特征。多態(tài)是指不同的對象接收到相同的操作指令時,產(chǎn)生不同的動作。在程序中表現(xiàn)為,不同功能的函數(shù)在不同的類中具有相同的函數(shù)名,相應(yīng)的類對象在調(diào)用同名函數(shù)時會執(zhí)行不同的動作。在面向?qū)ο蟪绦蛟O(shè)計中,多態(tài)性主要體現(xiàn)在:向不同的對象發(fā)送同一個消息,不同對象接收到消息時會產(chǎn)生不同的行為,即每個對象以自己的方式響應(yīng)同樣的消息。多態(tài)性概念多態(tài)實例10.4.1多態(tài)性概念C++程序設(shè)計中,消息即對類的成員函數(shù)的調(diào)用,不同的行為是指不同的實現(xiàn),也就是調(diào)用不同的函數(shù)。因此,多態(tài)的本質(zhì)是指同一個函數(shù)的多種形態(tài)。C++語言支持的多態(tài)可以按照實現(xiàn)的時機分為編譯時多態(tài)和運行時多態(tài)兩種:編譯時多態(tài)又稱靜態(tài)聯(lián)編,是指程序在編譯時就可確定的多態(tài)性,通過重載機制實現(xiàn)。運行時多態(tài)稱為動態(tài)聯(lián)編,是指必須在運行中才可確定的多態(tài)性,通過繼承和虛函數(shù)實現(xiàn)。多態(tài)性概念10.4.2虛函數(shù)虛函數(shù)是運行時多態(tài),若某個基類函數(shù)聲明為虛函數(shù),則其公有派生類將定義與其基類虛函數(shù)原型相同的函數(shù),這時,當(dāng)使用基類指針或基類引用操作派生類對象時,系統(tǒng)會自動用派生類中的同名函數(shù)代替基類虛函數(shù)。虛函數(shù)10.4.2虛函數(shù)對于普通成員函數(shù),派生類可以重新定義從基類繼承下來的虛函數(shù),從而形成該函數(shù)在派生類中的專門版本。派生類對基類虛函數(shù)重新定義后,仍作為虛函數(shù)可在更下層派生類中被重新定義。通常,在派生類中重新定義虛函數(shù)時,virtual可以不出現(xiàn),但最好保留,以增強程序的可讀性。有了虛函數(shù)后,通過基類指針或基類引用調(diào)用派生類對象的虛函數(shù)時,會實際調(diào)用指針或引用指向的派生類對象中那個重定義版本,即操作派生類的虛函數(shù)。一般虛函數(shù)成員10.4.2虛函數(shù)在C++中,不能聲明虛構(gòu)造函數(shù),因為構(gòu)造函數(shù)執(zhí)行時,對象還沒有構(gòu)造好,不可按虛函數(shù)方式進行調(diào)用,但可以聲明虛析構(gòu)函數(shù)。虛析構(gòu)函數(shù)是為了解決基類的指針指向派生類對象,并用基類的指針銷毀派生類對象存在的?;愇鰳?gòu)函數(shù)聲明為虛析構(gòu)函數(shù),則釋放基類指針指向的對象時會調(diào)用基類及派生類析構(gòu)函數(shù),派生類對象中的所有資源被回收。虛析構(gòu)函數(shù)聲明形式如下所示: virtual~類名();虛析構(gòu)函數(shù)10.4.3純虛函數(shù)在定義一個表示抽象概念的基類時,有時無法或者不需要給出某些成員函數(shù)的具體實現(xiàn),函數(shù)的實現(xiàn)在派生類中完成,基類中這樣的函數(shù)聲明為純虛函數(shù)。與虛函數(shù)相比,純虛函數(shù)沒有函數(shù)體,其作用是在基類中為派生類保留一個函數(shù)接口,方便派生類根據(jù)需要對它實現(xiàn),實現(xiàn)多態(tài)。C++中純虛函數(shù)的聲明形式如下所示: virtual函數(shù)返回值類型函數(shù)名(參數(shù)表)=0;若在一個類中聲明了純虛函數(shù),但是在其派生類中沒有實現(xiàn)該函數(shù),則該函數(shù)在派生類中仍為純虛函數(shù)。純虛函數(shù)10.4.3純虛函數(shù)#include<iostream>usingnamespacestd;classStaff

//職員類{public:

virtualvoidPay()=0; //聲明為純虛函數(shù)};classCommonWorker:publicStaff //普通員工類{public:

CommonWorker(doublew,doubleb):wage(w),bonus(b){}

virtualvoidPay()

//重新定義虛函數(shù)

{

cout<<"基本工資+獎金="<<wage+bonus<<endl; }protected: doublewage; doublebonus;};classManager:publicCommonWorker //經(jīng)理類{public:

Manager(doublew,doubleb,doublea):CommonWorker(w,b),allowance(a){}

virtualvoidPay()//重新定義虛函數(shù)

{

cout<<"基本工資+獎金+職務(wù)津貼

="<<wage+bonus+allowance<<endl; }protected: doubleallowance;};10.4.3純虛函數(shù)intmain(){

Staff*s; //基類指針 CommonWorkerc1(800,2000); Managerm1(1200,2000,500); s=&c1;//基類指針指向派生類對象 s->Pay();//調(diào)用CommonWorker類的Pay()函數(shù) s=&m1;//間接基類指針指向派生類對象 s->Pay();//調(diào)用Manager類的Pay()函數(shù)return0;}基本工資+獎金=2800基本工資+獎金+職務(wù)津貼=3700程序的運行結(jié)果為:第五節(jié)運算符重載什么是運算符重載運算符重載的方法運算符重載的規(guī)則重載為類的友元函數(shù)10.5.1什么是運算符重載C++語言語言的算術(shù)、關(guān)系等運算符能否直接應(yīng)用于對象呢?例如,在解決科學(xué)計算問題時經(jīng)常要用到復(fù)數(shù)的運算,定義復(fù)數(shù)類Complex,然后定義Complex類的兩個對象c1和c2,計算c1和c2之和,能否直接相加呢?

這樣的運算能不能運行呢?怎么解決呢?問題10.5.1什么是運算符重載運算符重載是對已有的運算符賦予多重含義,使同一個運算符作用于不同類型的數(shù)據(jù)時做出不同的行為。運算符重載的本質(zhì)是函數(shù)重載,它也是C++多態(tài)的一種體現(xiàn),為用戶提供了一個直觀的接口,調(diào)用運算符操作自定義數(shù)據(jù)類型其實就是調(diào)用運算符函數(shù)。運算符重載增強了C++的可擴充性,使得C++代碼更加直觀、易讀,且便于對對象進行各種運算操作。運算符重載10.5.3運算符重載的規(guī)則只能重載C++中已有的運算符,不能創(chuàng)建新的運算符。重載之后的運算符不能改變其優(yōu)先級和結(jié)合性,也不能改變其操作數(shù)的個數(shù)及語法結(jié)構(gòu)。避免沒有目的地使用重載運算符。類屬關(guān)系運算符“.”、成員指針運算符“*”、作用域運算符“::”、sizeof運算符和三目運算符“?:”不可以重載。重載的規(guī)則10.5.2運算符重載的方法重載的運算符是具有特殊名字的函數(shù):它們的名字由關(guān)鍵字operator和其后要重載的運算符共同組成。語法格式

返回類型operator運算符(參數(shù)列表) {

函數(shù)體; }重載的方法10.5.2運算符重載的方法#include<iostream>usingnamespacestd;classComplex //聲明Complex類{ public:

Complex(doubler=0,doublei=0)

//帶默認(rèn)值的構(gòu)造函數(shù){real=r;imag=i;}

voiddisplay() //輸出復(fù)數(shù){cout<<"("<<real;if(imag>0)cout<<"+"<<imag<<"i)";elseif(imag<0)cout<<imag<<"i)";

elsecout<<")";}重載為成員函數(shù):用成員函數(shù)重載算術(shù)運算符“+”10.5.2運算符重載的方法Complexoperator+(Complexc); //聲明重載算術(shù)運算符“+”private:doublereal,imag; };ComplexComplex::operator+(Complexc) //定義運算符“+”的重載函數(shù){Complexcc;cc.real=real+c.real;cc.imag=imag+c.imag;returncc;}intmain(){Complexc1(1.1,2.2),c2(3.3,4.4),c3;

c3=c1+c2;//調(diào)用運算符重載函數(shù)

函數(shù)調(diào)用形式:c3=c1.operrator+(c2)c3.display();return0;}程序運行結(jié)果為:

(4.4+6.6i)10.5.4重載為類的友元函數(shù)語法格式如下: friend返回類型operator運算符(參數(shù)列表) {

函數(shù)體; }重載為類的友元函數(shù)注意:當(dāng)運算符重載為類的成員函數(shù)時,函數(shù)的參數(shù)個數(shù)比原來操作數(shù)的個數(shù)要少一個(后置“++”“--”除外)。當(dāng)重載為類的友元函數(shù)時,參數(shù)個數(shù)與原操作數(shù)的個數(shù)相同。

10.5.4重載為類的友元函數(shù)【案例】用友元函數(shù)重載算術(shù)運算符“+”。#include<iostream>usingnamespacestd;classComplex //聲明Complex類{ public:

Complex(doubler=0,doublei=0){real=r;imag=i;}

voiddisplay() //輸出復(fù)數(shù){cout<<"("<<real;if(imag>0)cout<<"+"<<imag<<"i)";elseif(imag<0)cout<<imag<<"i)";

elsecout<<")";}10.5.4重載為類的友元函數(shù)friendComplexoperator+(Complexc1,Complexc2);//聲明運算符重載函數(shù)為Complex類的友元函數(shù)private:doublereal,imag; };Complexoperator+(Complexc1,Complexc2) //定義運算符“+”的重載函數(shù){Complexcc;cc.real=c1.real+c2.real;cc.imag=c1.imag+c2.imag;returncc;}intmain(){Complexc1(1.1,2.2),c2(3.3,4.4),c3;

c3=c1+c2;//調(diào)用運算符重載函數(shù)c3.display();return0;}

程序運行結(jié)果為:

(4.4+6.6i)10.5.4重載為類的友元函數(shù)1.重載流插入運算符重載運算符“<<”的一般格式為:ostream&operator<<(ostream&,自定義類&形參對象);流插入運算符“<<”的重載函數(shù),第一個參數(shù)必須是ostream&的類型,即ostream對象的引用;第二個參數(shù)是要進行輸出操作的類對象或?qū)ο蟮囊?;函?shù)的返回類型和第一個參數(shù)一樣,必須是ostream&的類型,返回ostream對象的引用。

從重載函數(shù)的原型可以看出,只能將運算符“<<”重載為類的友元函數(shù),不能將它重載為類的成員函數(shù),原因是運算符“<<”左側(cè)操作數(shù)必須是ostream類的對象,而如果重載為成員函數(shù),左側(cè)操作數(shù)則應(yīng)是一個我們自定義類的對象,所以必須重載為友元函數(shù)。

10.5.4重載為類的友元函數(shù)2.重載流提取運算符重載運算符“>>”的一般格式為:

istream&operator>>(istream&,自定義類&形參對象);流提取運算符“>>”的重載函數(shù),第一個參數(shù)必須是istream對象的引用;第二個參數(shù)是要進行輸入操作的類對象或?qū)ο蟮囊茫缓瘮?shù)的返回類型必須是istream對象的引用。和流插入運算符一樣,只能將運算符“>>”重載為類的友元函數(shù),不能將它重載為類的成員函數(shù)。

10.5.4重載為類的友元函數(shù)【例】重載流運算符“>>”和”<<”。#include<iostream>usingnamespacestd;classComplex //聲明Complex類{public:Complex(doubler=0,doublei=0)

{real=r;imag=i;} friendistream&operator>>(istream&input,Complex&c);

//運算符“>>”重載為友元函數(shù) friendostream&operator<<(ostream&output,Complex&c); //運算符“<<”重載為友元函數(shù)

private:doublereal,imag;};

10.5.4重載為類的友元函數(shù)istream&operator>>(istream&input,Complex&c)//流提取運算符重載函數(shù)的定義{ input>>c.real>>c.imag; returninput;

}ostream&operator<<(ostream&output,Complex&c)//流插入運算符重載函數(shù)的定義{ output<<'('<<c.real<<'+'<<c.imag<<"i)"<<endl; returnoutput;

}intmain(){ Complexc1;

cin>>c1;//調(diào)用流提取運算符“>>”重載函數(shù)

cout<<c1;//調(diào)用流插入運算符“<<”重載函數(shù) return0;}

第六節(jié)案例實戰(zhàn)與實訓(xùn)10.6.1案例實戰(zhàn)1.抽象基類、虛函數(shù)、純虛函數(shù)應(yīng)用銀行賬戶業(yè)務(wù)分了存款賬戶和結(jié)算賬戶,存款賬戶包括賬號,余額等信息,同時包含創(chuàng)建、存款、取款(可以由一定的透支額度)、查詢等操作。結(jié)算賬戶包括賬號、余額和異地取款的類型(電匯、信匯、其他,涉及手續(xù)費不同),同時包含創(chuàng)建、存款、取款、查詢等操作?!竟δ苄枨蟆拷y行賬戶、存款賬戶、結(jié)算賬戶的繼承關(guān)系。銀行賬戶為上層基類;存款賬戶和結(jié)算賬戶由基類派生。將共同的內(nèi)容和操作定義在基類,并部分操作如取款、查詢操作設(shè)置為虛函數(shù)或純虛函數(shù)。編寫函數(shù)對基類賬戶的對象數(shù)組操作,顯示出每個賬戶的信息及所有賬戶的存款余額和。#include"iostream"#include"string"usingnamespacestd;enumREMIT{remitByPost,remitByCable,other};//信匯,電匯,其他classAccount{protected: stringacntNum; doublebalance;public: Account(stringacnNo="",doublebalan=0); doubleget_balance()const{returnbalance;}//獲取余額 voiddeposit(doubleamount){//存款 balance+=amount; } booloperator==(constAccount&a){//重載==,判斷賬戶是否相等 returnacntNum==a.acntNum;} virtualvoidsetRemit(REMITre){ }//設(shè)置結(jié)算賬戶中匯款類型 virtualvoiddisplay()const;//顯示賬戶信息 virtualvoidwithdrawal(doubleamount)=0;//取款操作};Account::Account(stringacnNo,doublebalan){ acntNum=acnNo;balance=balan;}voidAccount::display()const{cout<<"Acount"+acntNum+"="<<balance<<endl;}//派生存款類classSaving:publicAccount{ private: static doubleminBalance;//存款賬戶有透支最小額度 public: Saving(stringacnNo="",doublebalan=0):Account(acnNo,balan) { } staticvoidsetminBalance(intamount){ minBalance=amount; } virtualvoiddisplay()const; virtualvoidwithdrawal(doubleamount);};doubleSaving::minBalance=0;voidSaving::display()const{ cout<<"Saving:"; Account::display();}voidSaving::withdrawal(doubleamount){ if(balance+minBalance<amount) cout<<"Insufficientfundswithdrawal:"<<amount<<endl; else balance-=amount;}//派生類CheckingclassChecking:publicAccount{ REMITremittance;public: Checking(stringacnNo="",doublebalan=0):Account(acnNo,balan),remittance(other){ } voiddisplay()const; voidwithdrawal(doubleamount); voidsetRemit(REMITre){ remittance=re; }};voidChecking::display()const{cout<<"Checking:";Account::display();}voidChecking::withdrawal(doubleamount){//異地取款手續(xù)費 if(remittance==remitByPost) amount+=30; if(remittance==remitByCable) amount+=60; if(balance<amount) cout<<"Insufficientfundswithdrawal:"<<amount<<endl; else balance-=amount;}voidshow_Balance_sum(Account*a[],intn){//普通函數(shù) doublesum=0; inti; for(i=0;i<n;i++) { sum+=a[i]->get_balance(); a[i]->display(); } cout<<"allAcount'sbalancesum=:"<<sum;}intmain(){ Account*a[3];//抽象基類只能定義指針類型,每個指針指向不同派生類對象 a[0]=newSaving("622000123",500); a[1]=newChecking("622111245",10000); a[2]=newSaving("62200167",2000); cout<<"操作前:\n"; show_Balance_sum(a,3); Saving::setminBalance(500); a[0]->withdrawal(800); a[1]->setRemit(remitByCable); a[1]->withdrawal(2000); a[2]->deposit(500); cout<<"\n操作后:\n"; show_Balance_sum(a,3); }在案例中,分析虛函數(shù)、純虛函數(shù)、靜態(tài)成員、const成員的使用,虛函數(shù)的調(diào)用規(guī)則,驗證使用了部分函數(shù)功能;對算法還可以補充和完善。10.6.1案例實戰(zhàn)2.運算符重載應(yīng)用封裝定義日期類并完善類中成員定義,從而簡化日期對象的操作。利用運算符函數(shù)重載,擴充運算符對對象的運算。【功能需求】重載插入符“<<”和提取符”>>”,簡化日期對象的輸入和輸出重載運算符”++”,”--”實現(xiàn)求日期的明天和昨天重載運算符“-”實現(xiàn)兩個日期相減,得到兩個日期相隔的天數(shù)重載運算符“+”實現(xiàn)日期加一個整數(shù)(正或負),得到一個新日期求出當(dāng)前日期是當(dāng)年的第幾天的功能其他功能函數(shù)因需定義#include"iostream"#include"math.h"#include"string.h"usingnamespacestd;classTdate{public:Tdate(int=2022,int=12,int=1);Tdate(constTdate&x);~Tdate();voidsetdate(int,int,int);Tdate&operator++();//求明天,前++ Tdateoperator++(int);//求明天,后++ Tdate&operator--();//求昨天,前-- Tdateoperator--(int);//求昨天,后--Tdateoperator+(intn);//加整數(shù)n天intoperator-(Tdatet);//日期相減inttotalday();//求總天數(shù) boolisleap();//是否是閏年friendostream&operator<<(ostream&ob,Tdatedate);friendistream&operator>>(istream&ob,Tdate&date);private:intyear,month,day;};Tdate::Tdate(inty,intm,intd){ year=y;month=m;day=d;//cout<<"constructurecalled\n";}Tdate::Tdate(constTdate&x){ year=x.year;month=x.month; day=x.day; //cout<<"copy_constructurecalled\n";}Tdate::~Tdate(){ //cout<<"destructurecalled\n";}voidTdate::setdate(inty1,intm1,intd1)//對象值修改{year=y1;month=m1;day=d1;}Tdate&Tdate::operator++(){//求明天,前++//涉及日期的跨月,跨年的判斷day++; switch(month) { case1: case3: case5: case7: case8: case10:if(day>31){month++;day=1;}break; case4: case6: case9: case11:if(day>30){month++;day=1;}break; case12:if(day>31){year++;month=day=1;}break; case2:if(isleap()&&day>29){month++;day=1;} elseif(!isleap()&&day>28){month++;day=1;} } return*this;}TdateTdate::operator++(inti){//求明天,后++//形參設(shè)置只是表示和前++函數(shù)定義的區(qū)別,無實際使用Tdatetemp=*this; day++; switch(month) { case1: case3: case5: case7: case8: case10:if(day>31){month++;day=1;}break; case4: case6: case9: case11:if(day>30){month++;day=1;}break; case12:if(day>31)

溫馨提示

  • 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)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論