版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
會(huì)計(jì)學(xué)1C應(yīng)用與開(kāi)發(fā)案例教程中實(shí)用6.1概述
繼承是C++語(yǔ)言的一種重要機(jī)制,該機(jī)制允許在既有類(lèi)的基礎(chǔ)上定義新的類(lèi),而不需要把既有類(lèi)的內(nèi)容重新書(shū)寫(xiě)一遍。繼承是通過(guò)派生方式實(shí)現(xiàn)的。一個(gè)基類(lèi)可以派生出多個(gè)派生類(lèi),這種從一個(gè)基類(lèi)中的繼承叫單繼承。如果一個(gè)派生類(lèi)由多個(gè)基類(lèi)派生出來(lái),稱(chēng)為多繼承。派生類(lèi)也可以作為基類(lèi)再派生出新的類(lèi),形成類(lèi)的層次結(jié)構(gòu)。類(lèi)有三種繼承方式:公有繼承(public)、私有繼承(private)和保護(hù)繼承(protected)。我們可以通過(guò)不同的繼承方式限定成員的訪問(wèn)權(quán)限,以達(dá)到修改已有成員的目的。我們還可以通過(guò)同名覆蓋、作用域限定符和虛基類(lèi)的方法達(dá)到此目的。派生類(lèi)繼承了基類(lèi)的所有成員,但構(gòu)造函數(shù)和析構(gòu)函數(shù)除外,因此,在派生類(lèi)中,想要進(jìn)行特別的初始化和清理工作時(shí),就要加入新的構(gòu)造函數(shù)和析構(gòu)函數(shù)。第1頁(yè)/共253頁(yè)6.1概述
多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計(jì)中的又一重要特征,多態(tài)的實(shí)現(xiàn)可分為編譯時(shí)的多態(tài)和運(yùn)行時(shí)的多態(tài)。編譯時(shí)的多態(tài),指程序在編譯過(guò)程中確定函數(shù)操作的具體對(duì)象,通過(guò)函數(shù)重載來(lái)實(shí)現(xiàn);運(yùn)行時(shí)的多態(tài),是指程序在運(yùn)行過(guò)程中才能確定函數(shù)操作的具體對(duì)象,通過(guò)虛函數(shù)來(lái)實(shí)現(xiàn)。虛函數(shù)為我們提供了一種更為靈活的多態(tài)性機(jī)制,它體現(xiàn)的是運(yùn)行時(shí)的多態(tài),它允許函數(shù)調(diào)用與函數(shù)體之間的關(guān)系在運(yùn)行時(shí)才建立。虛函數(shù)是用virtual關(guān)鍵字聲明的非靜態(tài)成員函數(shù)。當(dāng)將基類(lèi)的同名函數(shù)定義為虛函數(shù)時(shí),我們就可利用基類(lèi)類(lèi)型的指針訪問(wèn)該指針指向的派生類(lèi)對(duì)象的同名原型函數(shù),從而實(shí)現(xiàn)運(yùn)行過(guò)程的多態(tài)。第2頁(yè)/共253頁(yè)6.2派生類(lèi)的概念6.2.1基類(lèi)和派生類(lèi)繼承性在客觀世界中是一種常見(jiàn)的現(xiàn)象。例如:當(dāng)一個(gè)小孩出生時(shí),就從父親和母親那里繼承了一定的特征。隨著時(shí)間的推移和環(huán)境的變化,這個(gè)小孩逐漸有了自己的性格特征,因此這個(gè)小孩就具備了從父母那里繼承來(lái)的以及自己所獨(dú)有的特征的組合。從面向?qū)ο蟪绦蛟O(shè)計(jì)的觀點(diǎn)看,繼承所表達(dá)的正是這樣一種類(lèi)與類(lèi)之間的關(guān)系,這種關(guān)系允許在既有類(lèi)的基礎(chǔ)上創(chuàng)建新的類(lèi)。在最簡(jiǎn)單的情況下,一個(gè)類(lèi)B繼承類(lèi)A或者從類(lèi)A派生出類(lèi)B,通常類(lèi)A稱(chēng)為基類(lèi)(父類(lèi)),類(lèi)B稱(chēng)為派生類(lèi)(子類(lèi))。這時(shí),類(lèi)B的對(duì)象具有類(lèi)A對(duì)象的所有特性。也可以這樣說(shuō),類(lèi)B從類(lèi)A派生出來(lái)。這意味著類(lèi)B至少描述了與類(lèi)A同樣的接口,至少包含了同類(lèi)A一樣的數(shù)據(jù),可以共享類(lèi)A的成員函數(shù)。第3頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
類(lèi)B繼承了類(lèi)A(或稱(chēng)類(lèi)A派生了類(lèi)B),那么類(lèi)A的公有段成員可以傳遞給派生類(lèi)B,當(dāng)作類(lèi)B自己的成員。在創(chuàng)建派生類(lèi)對(duì)象時(shí),先要調(diào)用基類(lèi)的構(gòu)造函數(shù),以便分配基類(lèi)的公有段成員存儲(chǔ)空間;實(shí)際上,由于調(diào)用了構(gòu)造函數(shù),一個(gè)派生類(lèi)對(duì)象包含有一個(gè)基類(lèi)對(duì)象,只是對(duì)于派生類(lèi)而言,不能訪問(wèn)基類(lèi)的私有段成員。派生類(lèi)的定義格式如下:
class派生類(lèi)名:繼承方式基類(lèi)名
{//派生類(lèi)成員定義
}第4頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
在上面的格式中,派生類(lèi)名是新定義的一個(gè)類(lèi)名,它是從由“基類(lèi)名”所標(biāo)識(shí)的類(lèi)派生而來(lái)的。這樣定義的派生類(lèi)繼承了基類(lèi)的除了構(gòu)造函數(shù)和析夠函數(shù)之外的所有成員,因此派生類(lèi)對(duì)象由兩部分組成:一部分是由基類(lèi)繼承的成員,另一部分是派生類(lèi)新增加的自己特有的成員?!袄^承方式”用于規(guī)定派生類(lèi)中從基類(lèi)繼承到的那部分成員在派生類(lèi)中的訪問(wèn)控制權(quán)限。繼承方式可用下列3個(gè)關(guān)鍵字之一來(lái)指定:public:公有繼承;protected:保護(hù)繼承;private:私有繼承。第5頁(yè)/共253頁(yè)6.2派生類(lèi)的概念【例6-1】#include<iostream.h>classA{private:intpri;public:intpub;voidset_pri(inta){pri=a;}voidset_pub(inta){pub=a;}
voidout_pri(){cout<<pri<<””<<endl;}};classB:publicA{private:intpri1;public:intpub1;voidset_pri1(inta){pri1=a;}第6頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
voidset_pub1(inta){pub1=a;}voidout(){cout<<pub<<””<<pri1<<””<<pub1<<endl;}};voidmain(){AobjA;objA.set_pri(1);objA.set_pub(2);objA.out_pri();BobjB;objB.set_pri(3);objB.set_pub(4);objB.set_pri1(5);objB.set_pub1(6);objB.out_pri();objB.out();}第7頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
程序的輸出結(jié)果為:
13456
該程序創(chuàng)建了兩個(gè)對(duì)象objA(A類(lèi)對(duì)象)和objB(B類(lèi)對(duì)象)。盡管B類(lèi)沒(méi)有包含像out_pri()這樣的成員函數(shù)說(shuō)明,對(duì)象objB仍可以調(diào)用基類(lèi)的成員函數(shù)。使用了繼承,B類(lèi)的任何對(duì)象也是A類(lèi)的對(duì)象。該程序創(chuàng)建了兩個(gè)對(duì)象objA(A類(lèi)對(duì)象)和objB(B類(lèi)對(duì)象)。盡管B類(lèi)沒(méi)有包含out_pri()函數(shù)聲明,但對(duì)象objB仍然可以調(diào)用out_pri(),因?yàn)锽類(lèi)繼承了A類(lèi)的公有成員函數(shù)out_pri()。第8頁(yè)/共253頁(yè)6.2派生類(lèi)的概念6.2.2繼承方式
1.公有繼承在上一小節(jié)中,我們用了公有繼承的例子,這種類(lèi)型的繼承是最常用的。對(duì)于公有繼承來(lái)說(shuō),基類(lèi)的公有段成員和保護(hù)段成員在派生類(lèi)中的訪問(wèn)屬性不變。派生類(lèi)的其他成員可以直接訪問(wèn)繼承來(lái)的這些成員。而外部使用者只能通過(guò)對(duì)象來(lái)訪問(wèn)其公有成員?;?lèi)的私有成員是不可訪問(wèn)的(友員除外),就是說(shuō),無(wú)論是派生類(lèi)的成員還是派生類(lèi)的對(duì)象都無(wú)法訪問(wèn)基類(lèi)的私有成員。應(yīng)注意的是:基類(lèi)的保護(hù)成員只能被派生類(lèi)的成員訪問(wèn),不能被派生類(lèi)的對(duì)象訪問(wèn)。第9頁(yè)/共253頁(yè)6.2派生類(lèi)的概念【例6-2】#include<iostream.h>classA{protected:inti,j;public:voidget_ij();voidshow_ij()};classB:publicA{intk;public:intget_k();voidmake_k();};classC:publicB{public:voidf();};第10頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
這里,類(lèi)B由基類(lèi)A公有派生而來(lái),則A公有段和保護(hù)段的成員在B中也是公有段和保護(hù)段的成員。同時(shí),B又公有派生出類(lèi)C,這時(shí),B的公有段和保護(hù)段的成員(包括從A繼承過(guò)來(lái)的成員)在C中也是公有段和保護(hù)段的成員。下面是各成員函數(shù)的實(shí)現(xiàn)代碼:
第11頁(yè)/共253頁(yè)6.2派生類(lèi)的概念voidA::get_ij(){cout<<“Enterthetwonumbers:”;cin>>i>>j;}voidA::show_ij(){cout<<i<<””<<j<<”\n”;}
intB::get_k(){returnk;}voidB::make_k(){k=I+j;}voidC::f(){i=5;j=6;}第12頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
可見(jiàn),在B和C中都可以直接使用基類(lèi)A的保護(hù)段中的成員i和j。下面的實(shí)現(xiàn)說(shuō)明了怎樣使用這些派生類(lèi)。
voidmain(){BobjB;CobjC;objB.get_ij();objB.show_ij();objB.make_k();cout<<objB.get_k()<<”\n”;objC.f();objC.show_ij();}第13頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
程序的輸出結(jié)果為:
Enterthetwonumbers:1212356
總之,一個(gè)派生類(lèi)如果從基類(lèi)公有派生出來(lái),則基類(lèi)成員的訪問(wèn)權(quán)限在派生類(lèi)中保持不變。第14頁(yè)/共253頁(yè)6.2派生類(lèi)的概念2.私有繼承當(dāng)類(lèi)的繼承方式為私有繼承時(shí),基類(lèi)公有段成員和保護(hù)段成員在派生類(lèi)中作為私有成員,派生類(lèi)的其他成員可以直接訪問(wèn)它們。但是,在外部通過(guò)派生類(lèi)的對(duì)象無(wú)法訪問(wèn)?;?lèi)的私有成員仍然不可訪問(wèn)(友員除外),即不允許派生類(lèi)的成員函數(shù)訪問(wèn)基類(lèi)的私有成員。經(jīng)過(guò)私有繼承之后,所有基類(lèi)的成員都成為派生類(lèi)的私有成員,如果進(jìn)一步派生的話,新的派生類(lèi)的成員函數(shù)就不能訪問(wèn)已變成私有的基類(lèi)的成員。修改前面公有派生的例子如下:第15頁(yè)/共253頁(yè)6.2派生類(lèi)的概念【例6-3】#include<iostream.h>classA{protected:inti,j;public:voidget_ij();voidshow_ij();};classB:privateA{intk;public:intget_k();voidmake_k();};classC:publicB{public:voidf();};第16頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
這里,派生類(lèi)B由基類(lèi)A私有派生,A的公有段和保護(hù)段的成員在B中變成了私有段的成員。同時(shí),C又由類(lèi)B公有派生,則B的公有段和保護(hù)段的成員(注意:并不包括從A繼承過(guò)來(lái)的成員,它們屬于B的私有段)成為C的公有段和保護(hù)段的成員。下面是各成員函數(shù)的實(shí)現(xiàn):第17頁(yè)/共253頁(yè)6.2派生類(lèi)的概念voidA::get_ij(){cout<<“Enterthetwonumbers:”;cin>>i>>j;}voidA::show_ij(){cout<<i<<””<<j<<”\n”;}intB::get_k()
{returnk;}voidB::make_k(){k=i+j;}voidC::f(){i=5;//錯(cuò)誤
j=6;//錯(cuò)誤
}第18頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
類(lèi)A的保護(hù)段成員i和j已經(jīng)成了類(lèi)B的私有段成員,因此,B的派生類(lèi)C以及B的對(duì)象都不能訪問(wèn)A的保護(hù)段成員i和j、以及A的公有段成員函數(shù)get_ij()和show_ij()。下面的實(shí)現(xiàn)說(shuō)明了這些派生類(lèi)的使用。
voidmain(){BobjB;CobjC;objB.get_ij();//錯(cuò)誤,外部對(duì)象無(wú)法訪問(wèn)私有成員
objB.show_ij();//錯(cuò)誤,外部對(duì)象無(wú)法訪問(wèn)私有成員
objB.make_k();cout<<objB.get_k()<<”\n”;objC.show_ij();//錯(cuò)誤,外部對(duì)象無(wú)法訪問(wèn)私有成員
}第19頁(yè)/共253頁(yè)6.2派生類(lèi)的概念3.保護(hù)繼承無(wú)論是公有繼承,還是私有繼承,派生類(lèi)都不能直接訪問(wèn)基類(lèi)的私有成員,要想訪問(wèn)某些私有成員只能通過(guò)調(diào)用基類(lèi)的成員函數(shù),這樣顯得很不方便。要想使用這些私有成員既便于派生類(lèi)訪問(wèn),又禁止外界對(duì)它的操作,可以把這些私有成員定義為保護(hù)成員。當(dāng)派生類(lèi)使用保護(hù)繼承方式派生時(shí),基類(lèi)的公有成員和保護(hù)成員在派生類(lèi)中具有保護(hù)成員訪問(wèn)屬性。這樣,派生類(lèi)的其他成員函數(shù)就可以直接訪問(wèn)它們,但在類(lèi)外部通過(guò)派生類(lèi)的對(duì)象無(wú)法訪問(wèn)。而基類(lèi)的私有成員仍然是不可訪問(wèn)的(友員除外)。第20頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
對(duì)于私有繼承和保護(hù)繼承的直接派生類(lèi)中,基類(lèi)私有成員都是不可見(jiàn)的,基類(lèi)保護(hù)成員和公有成員是可見(jiàn)的;對(duì)派生類(lèi)對(duì)象而言,所有成員都不可見(jiàn)。兩種繼承方式的效果似乎完全相同。但是,如果派生類(lèi)作為新的基類(lèi),繼續(xù)派生時(shí),二者的區(qū)別就出現(xiàn)了。假設(shè)A類(lèi)以私有繼承方式派生B類(lèi),B類(lèi)又派生C類(lèi),那么C類(lèi)的成員函數(shù)和對(duì)象都不能訪問(wèn)間接從A類(lèi)中繼承來(lái)的成員。如果A類(lèi)以保護(hù)繼承方式派生B類(lèi),那么A類(lèi)中的公有和保護(hù)成員在B類(lèi)中都是保護(hù)成員。B類(lèi)再派生出C類(lèi)后,A類(lèi)中的公有和保護(hù)成員被C類(lèi)繼承后,有可能是保護(hù)的或者是私有的(由類(lèi)C對(duì)類(lèi)B的繼承方式?jīng)Q定)。因此,C類(lèi)的成員有可能可以訪問(wèn)間接從類(lèi)A中繼承來(lái)的成員。第21頁(yè)/共253頁(yè)6.2派生類(lèi)的概念【例6-4】#include<iostream.h>classA{inti;protected:intj;public:voidget_ij();{cout<<“Enterthetwonumbers:”;cin>>i>>j;}
voidshow_ij();{cout<<i<<“”<<j<<”\n”;}
};
classB:protectedA{intk;public:intget_k();{returnk;}第22頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
voidmake_k();{k=i+j;//錯(cuò)誤,不能訪問(wèn)基類(lèi)私有成員
}};classC:publicB{public:voidf();{i=5;//錯(cuò)誤,不能訪問(wèn)基類(lèi)私有成員
j=6;//正確,在派生類(lèi)中是保護(hù)成員
}};第23頁(yè)/共253頁(yè)6.2派生類(lèi)的概念6.2.3調(diào)整訪問(wèn)聲明基類(lèi)成員被派生類(lèi)繼承后,在派生類(lèi)中的訪問(wèn)聲明主要由派生類(lèi)定義時(shí)的繼承方式來(lái)決定。但是,在C++程序設(shè)計(jì)過(guò)程中,我們希望基類(lèi)某些成員的訪問(wèn)聲明以私有或保護(hù)繼承方式時(shí),在派生類(lèi)中的訪問(wèn)聲明不變,保持在基類(lèi)中的訪問(wèn)聲明,這可通過(guò)調(diào)整訪問(wèn)聲明來(lái)實(shí)現(xiàn)。格式如下:基類(lèi)類(lèi)名::基類(lèi)保護(hù)段或公有段數(shù)據(jù)成員基類(lèi)類(lèi)名::基類(lèi)保護(hù)段或公有段成員函數(shù)名第24頁(yè)/共253頁(yè)6.2派生類(lèi)的概念例如:classA{inta;public:intb,c;intaf();};classD:privateA{intd;public:A::c;//調(diào)整對(duì)A::c的訪問(wèn)聲明
inte;intdf();};第25頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
類(lèi)D從基類(lèi)A私有派生,類(lèi)A的所有公有段和保護(hù)段的成員都為類(lèi)D的私有段,使用訪問(wèn)聲明
A::c
可以將類(lèi)B的公有段成員c在私有派生類(lèi)D中顯式聲明為公有的,D的派生類(lèi)可以訪問(wèn)它。由此可見(jiàn),調(diào)整訪問(wèn)聲明是私有派生的一種補(bǔ)充。對(duì)訪問(wèn)聲明的調(diào)整要注意以下幾點(diǎn):第26頁(yè)/共253頁(yè)6.2派生類(lèi)的概念1.調(diào)整時(shí)不能說(shuō)明任何類(lèi)型。
classA{inta;public:intb,c;intaf();};classD:privateA{intd;public:intA::c;//錯(cuò)誤
…};第27頁(yè)/共253頁(yè)6.2派生類(lèi)的概念2.對(duì)訪問(wèn)聲明的調(diào)整僅用于派生類(lèi)中恢復(fù)名字的訪問(wèn)權(quán)限,不允許在派生類(lèi)中降低或提升基類(lèi)成員的可訪問(wèn)性。即:基類(lèi)中的公有段成員或保護(hù)段成員在派生類(lèi)中僅能說(shuō)明為相對(duì)應(yīng)的公有段成員或保護(hù)段成員;基類(lèi)的私有成員不能用于訪問(wèn)聲明的調(diào)整。
從類(lèi)的封裝性的角度來(lái)看,這一限制是可以理解的。因?yàn)槿绻缮?lèi)能將其基類(lèi)的私有成員用訪問(wèn)聲明提高其訪問(wèn)權(quán)限而成為公有段的成員,這就破壞了類(lèi)的封裝性。第28頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
classA{inta;protected:intb;public:intc;};classD:privateA{protected:A::b;A::c//錯(cuò)誤,不能降低基類(lèi)成員的可訪問(wèn)性
public:A::c;A::a;//私有成員不能用于訪問(wèn)聲明的調(diào)整
};第29頁(yè)/共253頁(yè)6.2派生類(lèi)的概念3.對(duì)重載函數(shù)名的訪問(wèn)聲明將調(diào)整基類(lèi)中具有該名的所有函數(shù)的訪問(wèn)權(quán)限。由于調(diào)整訪問(wèn)聲明僅僅是恢復(fù)名字的訪問(wèn),對(duì)于重載函數(shù)名,它的訪問(wèn)聲明將使所有同名的重載函數(shù)的訪問(wèn)權(quán)限都得到調(diào)整。
classA{public:f();f(int);};classB:privateA{public:A::f;//使A::f()和A::f(int)在B中都是公有的
};第30頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
其中,A::f()表示訪問(wèn)聲明僅僅調(diào)整名字,該函數(shù)名不帶任何參數(shù)和類(lèi)型;同時(shí),它使得A::f()和A::f(int)在類(lèi)B中都處于公有段。
classA{private:f(int);public:f();};classB:privateA{public:A::f;//錯(cuò)誤,訪問(wèn)聲明具有二義性,不能調(diào)整其訪問(wèn)
};第31頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
同時(shí)也意味著,如果派生類(lèi)和基類(lèi)有同名的成員,則不可調(diào)整基類(lèi)成員的訪問(wèn)。
classA{public:voidf();};classB:privateA{public:voidf();A::f;//錯(cuò)誤,f的二次聲明,不能調(diào)整訪問(wèn)
};第32頁(yè)/共253頁(yè)6.2派生類(lèi)的概念6.2.4類(lèi)層次中的訪問(wèn)規(guī)則
1.在派生類(lèi)中對(duì)基類(lèi)成員的覆蓋
C++允許派生類(lèi)重新定義基類(lèi)的成員。如果派生類(lèi)定義了與基類(lèi)同名的成員,稱(chēng)派生類(lèi)的成員覆蓋了基類(lèi)的同名成員。如果要在派生類(lèi)中使用基類(lèi)的同名成員,可以顯式地使用如下格式:類(lèi)名::成員來(lái)調(diào)用基類(lèi)的成員。第33頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
引用被覆蓋的基類(lèi)同名成員時(shí),應(yīng)使用類(lèi)名限定符加以限定。當(dāng)派生類(lèi)與基類(lèi)有同名成員時(shí),引用不同的派生類(lèi)對(duì)象中的這些成員時(shí),還需指明對(duì)象名。應(yīng)當(dāng)注意的是,不管是公有派生還是私有派生都不影響派生類(lèi)對(duì)基類(lèi)的靜態(tài)成員的訪問(wèn),但訪問(wèn)靜態(tài)成員時(shí),必須用“類(lèi)名::成員”顯式地說(shuō)明。例如:第34頁(yè)/共253頁(yè)6.2派生類(lèi)的概念classA{public:staticvoidsa();//靜態(tài)成員
voidfa();};classB:privateA{};//全私有派生classC:publicB{voidfc(){A::sa();//正確
fa();//錯(cuò)誤
sa();//錯(cuò)誤
}};第35頁(yè)/共253頁(yè)6.2派生類(lèi)的概念2.基類(lèi)和派生類(lèi)的賦值兼容規(guī)則對(duì)于基類(lèi)對(duì)象和派生類(lèi)對(duì)象,在公有派生條件下,C++語(yǔ)言允許派生類(lèi)對(duì)象到基類(lèi)對(duì)象的自動(dòng)轉(zhuǎn)換,這通常稱(chēng)為賦值兼容規(guī)則。賦值兼容規(guī)則:當(dāng)派生類(lèi)從基類(lèi)公有繼承時(shí),允許以下4種派生類(lèi)對(duì)象到基類(lèi)對(duì)象的自動(dòng)轉(zhuǎn)換。規(guī)則1:可以用派生類(lèi)對(duì)象為基類(lèi)對(duì)象賦值;規(guī)則2:可以用派生類(lèi)對(duì)象初始化基類(lèi)引用對(duì)象;規(guī)則3:可以把指向派生類(lèi)對(duì)象指針賦給基類(lèi)對(duì)象的指針;規(guī)則4:可以把派生類(lèi)對(duì)象的地址賦給基類(lèi)對(duì)象的指針。每個(gè)派生類(lèi)對(duì)象包含有一個(gè)基類(lèi)對(duì)象,因此,上述規(guī)則不難理解。第36頁(yè)/共253頁(yè)6.2派生類(lèi)的概念6.2.5派生類(lèi)的構(gòu)造函數(shù)和析構(gòu)函數(shù)派生類(lèi)繼承了基類(lèi)的所有成員,但基類(lèi)中的構(gòu)造函數(shù)和析構(gòu)函數(shù)是不能被繼承的。當(dāng)我們想對(duì)派生類(lèi)中新添加的成員進(jìn)行初始化時(shí),就必須按實(shí)際需要添加新的構(gòu)造函數(shù)。如果要對(duì)從基類(lèi)繼承下來(lái)的成員進(jìn)行初始化,則還要由基類(lèi)的構(gòu)造函數(shù)完成。當(dāng)需要傳遞參數(shù)給基類(lèi)構(gòu)造函數(shù)時(shí),必須為派生類(lèi)建立一個(gè)構(gòu)造函數(shù),并由它來(lái)傳遞基類(lèi)的構(gòu)造函數(shù)所需的參數(shù)。在C++中,派生類(lèi)構(gòu)造函數(shù)的聲明為:派生類(lèi)構(gòu)造函數(shù)(參數(shù)總表):基類(lèi)名(參數(shù)表),對(duì)象成員1(對(duì)象成員參數(shù)表),
…對(duì)象成員n(對(duì)象成員參數(shù)表)
{…}第37頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
若基類(lèi)使用缺省的構(gòu)造函數(shù)或不帶參數(shù)的構(gòu)造函數(shù),則在派生類(lèi)中定義構(gòu)造函數(shù)時(shí)可省略該基類(lèi)名。如果基類(lèi)定義了帶有參數(shù)的構(gòu)造函數(shù),則派生類(lèi)就應(yīng)該定義構(gòu)造函數(shù),以保證對(duì)調(diào)用基類(lèi)構(gòu)造函數(shù)的對(duì)象進(jìn)行初始化。派生類(lèi)構(gòu)造函數(shù)的調(diào)用順序是:
1.先祖先(基類(lèi)),調(diào)用基類(lèi)的構(gòu)造函數(shù)。
2.再客人(對(duì)象成員),調(diào)用成員對(duì)象的構(gòu)造函數(shù)。
3.后自己(派生類(lèi)本身)。調(diào)用派生類(lèi)的構(gòu)造函數(shù)。第38頁(yè)/共253頁(yè)6.2派生類(lèi)的概念【例6-5】#include<iostream.h>classBase{public:Base(){cout<<”Basecreated”<<endl;}classDerived:publicBase{public:Derived(){cout<<”Derivedcreated”<<endl;}};voidmain(){Deriveda;}第39頁(yè)/共253頁(yè)6.2派生類(lèi)的概念
程序的輸出結(jié)果為:
BasecreatedDerivedcreated
從輸出結(jié)果可以看出:先執(zhí)行基類(lèi)的構(gòu)造函數(shù),再執(zhí)行派生類(lèi)的構(gòu)造函數(shù)。另一方面,執(zhí)行析構(gòu)函數(shù)時(shí),先執(zhí)行派生類(lèi)的析構(gòu)函數(shù)再執(zhí)行基類(lèi)的析構(gòu)函數(shù)。原因是顯而易見(jiàn)的,對(duì)基類(lèi)的破壞隱含了對(duì)派生類(lèi)的破壞,所以派生類(lèi)的析構(gòu)函數(shù)必須先執(zhí)行。第40頁(yè)/共253頁(yè)6.3多繼承
6.3.1多繼承的概念
前面介紹的派生類(lèi)只有一個(gè)直接基類(lèi),這種繼承稱(chēng)為單繼承。C++允許一個(gè)派生類(lèi)具有兩個(gè)或兩個(gè)以上基類(lèi),這種繼承稱(chēng)為多繼承。多繼承在實(shí)際中也是非常有用的。常見(jiàn)的例子是軟件系統(tǒng)的windows風(fēng)格用戶界面設(shè)計(jì)。軟件系統(tǒng)的windows風(fēng)格用戶界面包括窗口、尺寸框、橫向滾動(dòng)條、縱向滾動(dòng)條以及各種類(lèi)型的按鈕。我們可以先分別設(shè)計(jì)出窗口類(lèi)、尺寸框類(lèi)、橫向滾動(dòng)條類(lèi)、縱向滾動(dòng)條類(lèi)以及各種按鈕類(lèi),然后設(shè)計(jì)用戶界面類(lèi)。用戶界面類(lèi)把窗口類(lèi)、尺寸框類(lèi)、橫向滾動(dòng)條類(lèi)、縱向滾動(dòng)條類(lèi)以及各種按鈕類(lèi)作為基類(lèi),通過(guò)多繼承產(chǎn)生。第41頁(yè)/共253頁(yè)6.3多繼承
定義具有兩個(gè)以上基類(lèi)的派生類(lèi)與定義單基類(lèi)的派生類(lèi)的形式相似,只要將繼承的多個(gè)基類(lèi)用逗號(hào)分隔即可。多繼承方式派生類(lèi)的定義格式為:
class派生類(lèi)名:繼承方式1基類(lèi)名1,繼承方式2基類(lèi)名2,…{//派生類(lèi)成員的定義
};
對(duì)每個(gè)基類(lèi)可以用不同的繼承方式,缺省繼承方式為private。例如:
classC:publicA,B//類(lèi)C公有繼承類(lèi)A,私有繼承類(lèi)B{…};第42頁(yè)/共253頁(yè)6.3多繼承
在多重繼承中,公有派生、私有派生和保護(hù)派生對(duì)于基類(lèi)成員在派生類(lèi)中的可訪問(wèn)性與單繼承的規(guī)則相同。在多重繼承中,派生類(lèi)的構(gòu)造函數(shù)與單繼承下派生類(lèi)構(gòu)造函數(shù)相似,它必須負(fù)責(zé)該派生類(lèi)所有基類(lèi)構(gòu)造函數(shù)以及對(duì)象成員(如果有的話)構(gòu)造函數(shù)的調(diào)用。同時(shí),派生類(lèi)的參數(shù)必須包含完成所有基類(lèi)、對(duì)象成員以及派生類(lèi)中新增數(shù)據(jù)成員初始化所需的參數(shù)。派生類(lèi)構(gòu)造函數(shù)執(zhí)行順序是:
1.所有基類(lèi)的構(gòu)造函數(shù);多個(gè)基類(lèi)構(gòu)造函數(shù)的執(zhí)行順序取決于定義派生類(lèi)時(shí)所指定的順序,與派生類(lèi)構(gòu)造函數(shù)中所定義的成員初始化列表的參數(shù)順序無(wú)關(guān);
2.對(duì)象成員的構(gòu)造函數(shù);
3.派生類(lèi)本身的構(gòu)造函數(shù)。多繼承的析構(gòu)函數(shù)與單繼承的一樣,析構(gòu)函數(shù)的調(diào)用順序正好與構(gòu)造函數(shù)的相反。第43頁(yè)/共253頁(yè)6.3多繼承6.3.2虛基類(lèi)
1.虛基類(lèi)的概念派生類(lèi)及其基類(lèi)可用一有向無(wú)環(huán)圖表示,其中的箭頭表示“由派生而來(lái)”。考慮下面的例子:第44頁(yè)/共253頁(yè)6.3多繼承classA{…public:inta;…};classB:publicA{…};
classC:publicA{…
};classD:publicB,publicC{public:voidf(inti){a=i;}};第45頁(yè)/共253頁(yè)6.3多繼承
這里,類(lèi)A兩次成為類(lèi)D的間接基類(lèi)。這就意味著D類(lèi)對(duì)象中有兩個(gè)A對(duì)象:由類(lèi)B繼承的A和類(lèi)C繼承的A。如下圖所示:
AA↑↑BC↖↗D
圖6-1
在D::f(inti)函數(shù)中,a有兩個(gè)拷貝(B路徑繼承來(lái)的a和C路徑繼承來(lái)的a),因此語(yǔ)句a=0具有二義性,它是將B::a置為0,還是將C::a置為0?第46頁(yè)/共253頁(yè)6.3多繼承
如果希望間接基類(lèi)A與其派生類(lèi)的關(guān)系是如下圖所示:
A↗↖BC↖↗D
顯然,目前多繼承的方法不能描述這種情況,需要新的描述方法--顯式地指明間接基類(lèi)與其派生類(lèi)的這種單拷貝關(guān)系。C++提供了這種描述手段:它將A說(shuō)明為B和C的虛基類(lèi);當(dāng)在多條繼承路徑上有一個(gè)公共的基類(lèi)(如本例的A),在這些路徑中的某幾條路徑匯合處(如本例的D),這個(gè)公共基類(lèi)就會(huì)產(chǎn)生多個(gè)實(shí)例,可以將這個(gè)公共基類(lèi)說(shuō)明為虛基類(lèi)。它僅是簡(jiǎn)單地在繼承的基類(lèi)前加關(guān)鍵字virtual,例如把上例改為:第47頁(yè)/共253頁(yè)6.3多繼承classA{…public:inta;…};classB:virtualpublicA{…};
classC:virtualpublicA{…
};classD:publicB,publicC{public:voidf(inti){a=i;}};第48頁(yè)/共253頁(yè)6.3多繼承
這時(shí)D類(lèi)對(duì)象中只有A的一個(gè)拷貝,因而函數(shù)D::f(int)中的語(yǔ)句a=i,沒(méi)有二義性。此例中,對(duì)于類(lèi)D而言,類(lèi)A是類(lèi)C的虛基類(lèi),而是類(lèi)B的真基類(lèi);但對(duì)于類(lèi)C而言,類(lèi)A仍是類(lèi)C的真基類(lèi),虛基類(lèi)只是個(gè)相對(duì)的概念。如果把上例稍稍修改一下:
…classD:publicC,publicB…
則對(duì)于類(lèi)D而言,類(lèi)A是類(lèi)B的虛基類(lèi),而是類(lèi)C的真基類(lèi)。第49頁(yè)/共253頁(yè)6.3多繼承
一個(gè)派生類(lèi)的對(duì)象的地址可以直接賦給虛基類(lèi)的指針。例如:
Dobj1;A*ptr=&obj1;
這時(shí)不需要強(qiáng)制類(lèi)型轉(zhuǎn)換。并且,一個(gè)虛基類(lèi)的引用可以引用一個(gè)派生類(lèi)的對(duì)象。例如:
Dobj2;A&ref=obj2;
反之則不行,無(wú)論在強(qiáng)制類(lèi)型轉(zhuǎn)換中指定什么路徑,一個(gè)虛基類(lèi)的指針或引用不能轉(zhuǎn)換為派生類(lèi)的指針或引用。例如:
D*p=(D*)(B*)ptr;
將產(chǎn)生編譯錯(cuò)誤。第50頁(yè)/共253頁(yè)6.3多繼承2.虛基類(lèi)對(duì)象的初始化加入虛基類(lèi)后,它的初始化在語(yǔ)法上與一般多繼承的初始化是一樣的,但在調(diào)用構(gòu)造函數(shù)的順序上有點(diǎn)差別。(1)先調(diào)用虛基類(lèi)構(gòu)造函數(shù),然后調(diào)用非虛基類(lèi)的構(gòu)造函數(shù);(2)當(dāng)同一層有多個(gè)虛基類(lèi),按照它們的說(shuō)明順序調(diào)用它們的構(gòu)造函數(shù);(3)當(dāng)虛基類(lèi)是由非虛基類(lèi)派生時(shí),則先調(diào)用基類(lèi)構(gòu)造函數(shù),再調(diào)用派生類(lèi)構(gòu)造函數(shù)。第51頁(yè)/共253頁(yè)6.3多繼承classX:publicY,virtualpublicZ{}Xone;將產(chǎn)生如下調(diào)用次序:Z()Y()X()這里Z是X的虛基類(lèi),故先調(diào)用Z的構(gòu)造函數(shù),再調(diào)用Y的構(gòu)造函數(shù),最后才調(diào)用派生類(lèi)X自己的構(gòu)造函數(shù)。第52頁(yè)/共253頁(yè)6.3多繼承
classbase1{};classbase2{};classlevel1:publicbase2,virtualpublicbase1{};classlevel2:publicbase2,virtualpublicbase1{};classtoplevel:publiclevel1,virtualpubliclevel2{};toplevelview;第53頁(yè)/共253頁(yè)6.3多繼承
類(lèi)等級(jí)關(guān)系如下圖所示:
base2base1base2↖↗↖↗level1level2↖↗toplevel1
圖6-2第54頁(yè)/共253頁(yè)6.3多繼承當(dāng)建立對(duì)象view時(shí),將產(chǎn)生如下調(diào)用次序:
level2():base1()base2()level2()level1():base2()level1()toplevel():toplevel()第55頁(yè)/共253頁(yè)6.3多繼承toplevel有兩個(gè)基類(lèi):一個(gè)是虛基類(lèi)level2,另一個(gè)是非虛基類(lèi)level1。根據(jù)規(guī)定:應(yīng)先執(zhí)行l(wèi)evel2的構(gòu)造函數(shù);level2也有兩個(gè)基類(lèi),一個(gè)是虛基類(lèi)base1,另一個(gè)是非虛基類(lèi)base2。應(yīng)先執(zhí)行base1的構(gòu)造函數(shù),再執(zhí)行base2的構(gòu)造函數(shù),最后執(zhí)行l(wèi)evel2的構(gòu)造函數(shù)。toplevel然后執(zhí)行l(wèi)evel1的構(gòu)造函數(shù),而level1又有兩個(gè)基類(lèi),base1是虛基類(lèi),無(wú)需再執(zhí)行其構(gòu)造函數(shù),base2是非虛基類(lèi),因此要先執(zhí)行base2的構(gòu)造函數(shù),然后執(zhí)行l(wèi)evel1的構(gòu)造函數(shù)。最后執(zhí)行toplevel的構(gòu)造函數(shù)。上例中,對(duì)于toplevel的對(duì)象而言,base1是level1的虛基類(lèi)。注意:虛基類(lèi)和非虛基類(lèi)在使用上是不同的。
第56頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
在講述多態(tài)性和虛函數(shù)之前,先介紹基類(lèi)的指針。
6.4.1指向基類(lèi)對(duì)象的指針指向派生類(lèi)對(duì)象
指向基類(lèi)和指向派生類(lèi)的指針變量是相關(guān)的。假設(shè)A是基類(lèi),B是從A公有派生出來(lái)的派生類(lèi),在C++中,任何被說(shuō)明為指向A的指針也可以指向B。例如:
A*p;//指向類(lèi)型A的對(duì)象的指針
AobjA;//類(lèi)型A的對(duì)象
BobjB;//類(lèi)型B的對(duì)象
p=&objA;//p指向類(lèi)型A的對(duì)象
p=&objB;//p指向類(lèi)型B的對(duì)象利用指針p,可以訪問(wèn)從基類(lèi)A繼承的成員,但B自己定義的成員不能用p訪問(wèn)(除非用了顯式類(lèi)型轉(zhuǎn)換把A轉(zhuǎn)換成B)。第57頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
任何聲明為指向基類(lèi)的指針,它可以指向它的公有派生類(lèi)對(duì)象,這種指針只能直接訪問(wèn)那些從基類(lèi)繼承來(lái)的成員,不能直接訪問(wèn)公有派生類(lèi)中的新添成員。如果派生類(lèi)是以私有方式派生的,則基類(lèi)的指針不能指向派生類(lèi)對(duì)象。如果我們想用基類(lèi)的指針調(diào)用派生類(lèi)的特定成員,則可以將基類(lèi)指針顯式轉(zhuǎn)換為派生類(lèi)指針來(lái)實(shí)現(xiàn)。一個(gè)指向基類(lèi)的指針可用來(lái)指向從基類(lèi)公有派生的任何對(duì)象,這一事實(shí)是非常重要的,是C++實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)性的關(guān)鍵途徑。第58頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)6.4.2多態(tài)性多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計(jì)的重要特征。它是指同樣的消息被不同類(lèi)型的對(duì)象接收時(shí)會(huì)產(chǎn)生不同行為。多態(tài)的實(shí)現(xiàn)可分為編譯時(shí)的多態(tài)和運(yùn)行時(shí)的多態(tài):編譯時(shí)的多態(tài),指程序在編譯過(guò)程中確定函數(shù)操作的具體對(duì)象,通過(guò)函數(shù)重載來(lái)實(shí)現(xiàn),重載是多態(tài)性的一種簡(jiǎn)單形式;運(yùn)行時(shí)的多態(tài),是指程序在運(yùn)行過(guò)程中才能確定函數(shù)操作的具體對(duì)象,通過(guò)虛函數(shù)實(shí)現(xiàn)(下節(jié)介紹)。第59頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)1.編譯時(shí)的多態(tài)性編譯時(shí)的多態(tài)是在程序編譯過(guò)程中就確定同名操作的具體操作對(duì)象,即決定調(diào)用哪個(gè)同名函數(shù)。編譯時(shí)的多態(tài)性可以通過(guò)重載函數(shù)來(lái)實(shí)現(xiàn)。有關(guān)一個(gè)類(lèi)中函數(shù)重載的問(wèn)題,我們已經(jīng)在前面介紹過(guò)。下面是一個(gè)基類(lèi)成員函數(shù)在派生類(lèi)中重載(也就是實(shí)現(xiàn)編譯時(shí)的多態(tài)性)的例子。
2.運(yùn)行時(shí)的多態(tài)性在實(shí)際情況中,許多對(duì)象以及對(duì)對(duì)象的操作往往不能再編譯時(shí)就確定下來(lái),它們需要在程序的運(yùn)行過(guò)程中確定,這就是運(yùn)行時(shí)的多態(tài)性。解決這個(gè)問(wèn)題就是采用動(dòng)態(tài)綁定,具體是用虛函數(shù)來(lái)實(shí)現(xiàn)。在下一節(jié)中具體地介紹虛函數(shù)。第60頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)6.4.3虛函數(shù)
虛函數(shù)是在基類(lèi)中被冠以virtual的成員函數(shù),它提供了一種接口界面,虛函數(shù)可以在一個(gè)或多個(gè)派生類(lèi)中被重新定義,但要求在派生類(lèi)中重新定義時(shí),虛函數(shù)的函數(shù)原型,包括返回類(lèi)型、函數(shù)名、參數(shù)個(gè)數(shù)、參數(shù)類(lèi)型的順序,必須完全相同。第61頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)【例6-6】#include<iostream.h>classbase{protected:intx;public:base(inta){x=a;}voidwho(){cout<<”base”<<x<<endl;}};classfirst_d:publicbase{public:first_d(inta):base(a){}voidwho(){cout<<”firstderivation”<<endl<<x<<endl;}};第62頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)classsecond_d:publicbase{public:second_d(inta):base(a){}voidwho(){cout<<secondderivation<<endl<<x<<endl;}};
建立了一個(gè)類(lèi)等級(jí),兩個(gè)派生類(lèi)中都重新定義了基類(lèi)的成員函數(shù)who()。下面是這個(gè)類(lèi)等級(jí)的使用:第63頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)voidmain(){base*p;basebase_obj(1);first_dfirst_obj(2);second_dsecond_obj(3);p=&base_obj;p->who();p=&first_obj;p->who();p=&second_obj;p->who();}第64頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
程序的輸出結(jié)果為:
base1base2base3
指向基類(lèi)的指針p,不管是指向基類(lèi)的對(duì)象base_obj還是指向派生類(lèi)的對(duì)象first_obj和second_obj,p->who()調(diào)用的都是基類(lèi)定義的who()版本。這說(shuō)明,通過(guò)指針引起的普通成員函數(shù)的調(diào)用,僅僅與指針(或引用)的類(lèi)型有關(guān),而與此刻正在指向什么對(duì)象無(wú)關(guān)。因?yàn)榛?lèi)的指針僅能訪問(wèn)派生類(lèi)中繼承的基類(lèi)成員,而不能訪問(wèn)派生類(lèi)自己的成員。在這種情況下,必須顯示地用
first_obj.who();和second_obj();第65頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
這樣才能調(diào)用類(lèi)first_d和類(lèi)second_d中定義的who()的版本。其本質(zhì)的原因在于普通成員函數(shù)的調(diào)用是在編譯時(shí)靜態(tài)區(qū)分的。如果隨著p所指向的對(duì)象的不同,p->who()能調(diào)用不同類(lèi)中who()的版本,這樣就可以用一個(gè)界面p->who()訪問(wèn)多個(gè)實(shí)現(xiàn)版本:base中的who()、first_d中的who()和second_d中的who(),這在編程時(shí)非常有用。實(shí)際上,這表達(dá)了一種動(dòng)態(tài)的性質(zhì),函數(shù)調(diào)用p->who()依賴(lài)于運(yùn)行時(shí)p所指向的對(duì)象,虛函數(shù)提供的就是這種解釋機(jī)制,如果在base中將成員函數(shù)who()說(shuō)明為虛函數(shù),則修改上述程序?yàn)椋?/p>
第66頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)【例6-7】#include<iostream.h>classbase{protected:intx;public:base(inta){x=a;}virtualvoidwho()//說(shuō)明為虛函數(shù)
{cout<<”base”<<x<<endl;}};
classfirst_d:publicbase{public:first_d(inta):base(a){}voidwho(){cout<<”firstderivation”<<endl<<x<<endl;}};第67頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
classsecond_d:publicbase{public:second_d(inta):base(a){}voidwho(){cout<<secondderivation<<endl<<x<<endl;}};voidmain(){base*p;basebase_obj(1);first_dfirst_obj(2);second_dsecond_obj(3);p=&base_obj;p->who();p=&first_obj;p->who();p=&second_obj;p->who();}第68頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
程序的輸出結(jié)果為:
base1firstderivation2secondderivation3
這里,語(yǔ)句p->who()出現(xiàn)了3次,由于p所指向的對(duì)象不同,每次出現(xiàn)都執(zhí)行了who()的不同實(shí)現(xiàn)版本?;?lèi)的虛函數(shù)who()定義了一種接口,在派生類(lèi)中為此接口定義了不同的實(shí)現(xiàn)版本,由于虛函數(shù)的解釋機(jī)制,實(shí)現(xiàn)了“單界面、多實(shí)現(xiàn)版本”的思想。這種在運(yùn)行時(shí)刻將函數(shù)界面與函數(shù)的不同實(shí)現(xiàn)版本進(jìn)行匹配的過(guò)程,稱(chēng)為動(dòng)態(tài)綁定,也稱(chēng)為運(yùn)行時(shí)的多態(tài)性。第69頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
用虛函數(shù)實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)性的關(guān)鍵之處是:必須用指向基類(lèi)的指針訪問(wèn)虛函數(shù)。盡管可以像調(diào)用其他成員函數(shù)那樣顯示地用對(duì)象名來(lái)調(diào)用一個(gè)虛函數(shù),但只有在同一個(gè)指向基類(lèi)的指針訪問(wèn)虛函數(shù)時(shí),運(yùn)行時(shí)多態(tài)性才能實(shí)現(xiàn)。由于p指向的對(duì)象不同,因此調(diào)用了who()的3個(gè)不同實(shí)現(xiàn)版本,這時(shí),稱(chēng)為函數(shù)who()具有虛特性。第70頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
基類(lèi)函數(shù)具有虛特性的條件是:
1.在基類(lèi)中,將該函數(shù)說(shuō)明為虛(virtual)函數(shù);
2.定義基類(lèi)的公有派生類(lèi);
3.在基類(lèi)的公有派生類(lèi)中一模一樣地重載該虛函數(shù);
4.定義指向基類(lèi)的指針變量,它指向基類(lèi)的公有派生類(lèi)的對(duì)象。
第71頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
注意:在一個(gè)派生類(lèi)中重新定義基類(lèi)的虛函數(shù)是函數(shù)重載的另一種形式。但它不同于一般的函數(shù)重載。當(dāng)重載一般的函數(shù)時(shí),函數(shù)的返回類(lèi)型和參數(shù)表可能是不相同的,僅函數(shù)名要求相同。但重載一個(gè)虛函數(shù)時(shí),要求函數(shù)名、返回類(lèi)型、參數(shù)個(gè)數(shù)。參數(shù)類(lèi)型和順序是完全相同的。如果函數(shù)原型不同,僅函數(shù)名相同,C++認(rèn)為這是一般的函數(shù)重載,此時(shí)虛特性丟失。例如:第72頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)classbase{public:virtualvoidvf1();virtualvoidvf2();virtualvoidvf3();voidf();};classderived:publicbase{public:voidvf1();//具有虛特性
voidvf2(int);//一般函數(shù)重載,
參數(shù)不同,虛特性丟失
charvf3();//錯(cuò)誤:僅返回類(lèi)型不同
voidf();//一般的函數(shù)重載非虛函數(shù)的重載};第73頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
voidg(){derivedd;base*bp->&d;//基類(lèi)指針指向派生類(lèi)
bp->vf1();//調(diào)用derived::vf1()bp->vf2();//調(diào)用base::vf2()bp->f();//調(diào)用base::f()}第74頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
在派生類(lèi)derived中的函數(shù)vf1()與基類(lèi)base中的虛函數(shù)vf1()具有完全相同的函數(shù)原型,故保持了虛特性;而函數(shù)vf2(int)與基類(lèi)中的虛函數(shù)vf2()參數(shù)不同,僅函數(shù)名相同,這只是一般函數(shù)的重載,其虛特性丟失;函數(shù)charvf3()同基類(lèi)的虛函數(shù)voidvf3()相比較,僅返回類(lèi)型不同,目前的C++實(shí)現(xiàn)認(rèn)為這是錯(cuò)誤的;函數(shù)f()僅僅是基類(lèi)非虛函數(shù)f()的重載。由于vf1()保持了虛特性,vf2()丟失了虛特性,因此,在進(jìn)行函數(shù)調(diào)用時(shí),結(jié)果不一樣。第75頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
在函數(shù)g()中,語(yǔ)句bp->vf1(),調(diào)用的derived::vf1()。在派生類(lèi)derived中,函數(shù)vf1定義為虛函數(shù),虛函數(shù)調(diào)用的解釋依賴(lài)于調(diào)用它的對(duì)象類(lèi)型,bp雖然是指向基類(lèi)的指針,但此刻指向的是派生類(lèi)對(duì)象d,因此,該語(yǔ)句等價(jià)d.vf1();另外一條語(yǔ)句bp->f(),調(diào)用的卻是base::f()。函數(shù)f()在基類(lèi)和派生類(lèi)中均已定義,且函數(shù)原型相同,但它是一個(gè)非虛函數(shù),非虛函數(shù)調(diào)用的解釋僅依賴(lài)于表示調(diào)用它的對(duì)象的指針或引用類(lèi)型。bp被聲明為指向基類(lèi)的指針,非虛函數(shù)的調(diào)用僅僅依賴(lài)bp是指向基類(lèi)的指針,而不在乎bp此刻是否正在指向派生類(lèi)的對(duì)象,它調(diào)用的是base();同樣地,語(yǔ)句bp->vf2()調(diào)用的是base::vf2()。第76頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
虛函數(shù)的這種特性使派生類(lèi)和虛函數(shù)成為許多C++程序設(shè)計(jì)的關(guān)鍵,因?yàn)榛?lèi)可以使用虛函數(shù)提供一個(gè)界面,這是該類(lèi)的所有公有派生類(lèi)都具有的共同界面,但派生類(lèi)可以定義自己的實(shí)現(xiàn)版本,而且虛函數(shù)調(diào)用的解釋依賴(lài)于調(diào)用它的對(duì)象類(lèi)型,指向基類(lèi)對(duì)象的指針指向不同派生類(lèi)的對(duì)象,就能訪問(wèn)虛函數(shù)的不同實(shí)現(xiàn)版本。
第77頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
虛函數(shù)必須是類(lèi)的成員函數(shù)。不能將虛函數(shù)說(shuō)明為全局(非成員的)函數(shù),也不能說(shuō)明為靜態(tài)成員函數(shù)。不能將友員說(shuō)明為虛函數(shù),但虛函數(shù)可以是另一個(gè)類(lèi)的友員。一旦一個(gè)函數(shù)被說(shuō)明為虛函數(shù),不管經(jīng)歷了多少派生類(lèi)層,都將保持其虛特性。當(dāng)一個(gè)派生類(lèi)沒(méi)有重新定義虛函數(shù)時(shí),則使用其基類(lèi)的虛函數(shù)版本。因此,在使用時(shí)要記住繼承的層次性。
第78頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)6.4.4純虛函數(shù)及抽象類(lèi)
基類(lèi)往往表示一些抽象的概念。例如,shape是一個(gè)基類(lèi),它表示具有形狀的東西,從shape可以派生出封閉圖形和非封閉圖形兩個(gè)派生類(lèi),封閉圖形又可以派生出橢圓形、多邊形等。這個(gè)類(lèi)等級(jí)的基類(lèi)shape體現(xiàn)了一個(gè)抽象的概念,在shape中定義一個(gè)求面積的函數(shù)顯然是無(wú)意義的,但可以將其說(shuō)明為虛函數(shù),提供各派生類(lèi)一個(gè)公共的界面,并由各派生類(lèi)提供求面積函數(shù)的各自版本。在這種情況下,基類(lèi)的有些虛函數(shù)沒(méi)有定義是很正常的,但是要求派生類(lèi)必須重新定義這些虛函數(shù),以使派生類(lèi)有意義。為此,C++引入了純虛函數(shù)的概念。第79頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
純虛函數(shù)是一個(gè)在基類(lèi)中說(shuō)明的虛函數(shù),它在該基類(lèi)中沒(méi)有定義,要求任何派生類(lèi)都必須定義自己的版本。為說(shuō)明一純虛函數(shù),應(yīng)使用如下格式:
virtualtypefunc_name(參數(shù)表)=0;這里,type是函數(shù)的返回類(lèi)型,func_name是函數(shù)名。第80頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
如果一個(gè)類(lèi)至少有一個(gè)純虛函數(shù),那么就稱(chēng)該類(lèi)為抽象類(lèi)。抽象類(lèi)機(jī)制支持一般概念的表示。例如上面談到的形狀類(lèi)shape是一般的概念,可以表達(dá)為抽象類(lèi),它有許多具體的變種,如圓形和方形才是具體可用的類(lèi)。抽象類(lèi)也可用于定義接口,由派生類(lèi)提供各種實(shí)現(xiàn)。抽象類(lèi)只能用作其他類(lèi)的基類(lèi),抽象類(lèi)不能建立對(duì)象。抽象類(lèi)不能用作參數(shù)類(lèi)型、函數(shù)返回類(lèi)型或顯式轉(zhuǎn)換的類(lèi)型。但可以聲明抽象類(lèi)的指針和引用。例如:第81頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)classpoint{…};classshape{pointcenter;…public:pointwhere(){returncenter;}
voidmove(pointp){center=p;draw();}virtualvoidrotate(int)=0;//純虛函數(shù)的定義
virtualvoiddraw()=0;//純虛函數(shù)的定義
};第82頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)shapex;//錯(cuò)誤:抽象類(lèi)不能建立對(duì)象shape*p;//可以聲明抽象類(lèi)的指針shapef();//錯(cuò)誤:抽象類(lèi)不能作為返回類(lèi)型voidg(shape);//錯(cuò)誤:抽象類(lèi)不能作為參數(shù)類(lèi)型shape&h(shape&);//可以聲明抽象類(lèi)的引用第83頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
從基類(lèi)繼承來(lái)的純虛函數(shù),在派生類(lèi)中仍是純虛函數(shù)。例如:classab_circle:publicshape{intradius;public:voidrotate(int){…}};第84頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
由于shape::draw()是一個(gè)純虛函數(shù),缺省的ab_circle::draw()也是一個(gè)純虛函數(shù),這時(shí)ab_circle仍為抽象類(lèi)。要使ab_circle類(lèi)為非抽象的,必須如下說(shuō)明:
classab_circle:publicshape{intradius;public:voidrotate(int){…}voiddraw(){}};第85頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)6.4.5構(gòu)造函數(shù)與虛析構(gòu)函數(shù)
1.構(gòu)造函數(shù)因?yàn)樵谂缮?lèi)中構(gòu)造函數(shù)是不能繼承的,也沒(méi)有重定義的必要。在構(gòu)造函數(shù)中調(diào)用虛函數(shù)將破壞動(dòng)態(tài)綁定邏輯。下面的例子說(shuō)明了這樣動(dòng)態(tài)綁定邏輯。第86頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)【例6-8】#include<iostream.h>classbase{protected:intx;public:base(intm){x=m+1;print();}
virtualvoidprint(){cout<<”Thevirtualfunctioninbaseiscalled!”<<endl;
cout<<x<<endl;}};classderive:publicbase{private:inty;public:derive(intm):base(m){y=m;print();}第87頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
virtualvoidprint(){cout<<”Thevirtualfunctioninderiveiscalled!”<<endl;cout<<y<<endl;}};voidmain(){deriveobj(10);}第88頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)
程序的輸出結(jié)果為:
Thevirtualfunctioninbaseiscalled!11Thevirtualfunctioninderiveiscalled!10
程序從創(chuàng)建派生類(lèi)的對(duì)象開(kāi)始執(zhí)行,在執(zhí)行中,先要調(diào)用基類(lèi)的構(gòu)造函數(shù)。此時(shí)派生類(lèi)的創(chuàng)建過(guò)程尚未完成,只能按靜態(tài)綁定調(diào)用基類(lèi)的虛函數(shù),無(wú)法實(shí)現(xiàn)預(yù)期的多態(tài)邏輯。第89頁(yè)/共253頁(yè)6.4多態(tài)性與虛函數(shù)2.虛析構(gòu)函數(shù)析構(gòu)函數(shù)可以通過(guò)virtual修飾而聲明為虛析構(gòu)函數(shù)。虛析構(gòu)函數(shù)與一般虛函數(shù)的不同之處在于:
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- CCAA - 2022年12月建筑施工領(lǐng)域?qū)I(yè)答案及解析 - 詳解版(65題)
- 河北省石家莊市辛集市2025-2026學(xué)年七年級(jí)上學(xué)期期末生物學(xué)試題(含解析)
- 養(yǎng)老院志愿服務(wù)制度
- 養(yǎng)老院護(hù)理服務(wù)質(zhì)量規(guī)范制度
- 企業(yè)危廢管理制度
- 煙花爆竹倉(cāng)庫(kù)建設(shè)項(xiàng)目環(huán)評(píng)報(bào)告
- CCAA - 考前沖刺練習(xí)二答案及解析 - 詳解版(62題)
- 向上安全教育課件
- 2025年北海市殘疾人康復(fù)培訓(xùn)中心招聘筆試真題
- 苯酚丙酮裝置操作工操作水平強(qiáng)化考核試卷含答案
- 危險(xiǎn)化學(xué)品安全法解讀
- 2026元旦主題班會(huì):馬年猜猜樂(lè)新春祝福版 教學(xué)課件
- 110kV旗潘線π接入社旗陌陂110kV輸電線路施工方案(OPGW光纜)解析
- 第5章 PowerPoint 2016演示文稿制作軟件
- 王洪圖黃帝內(nèi)經(jīng)80課時(shí)講稿
- 鼎甲異構(gòu)數(shù)據(jù)同步軟件用戶手冊(cè)
- 個(gè)人借條電子版模板
- 新版FMEA(AIAG-VDA)完整版PPT可編輯FMEA課件
- 廣州自來(lái)水公司招聘筆試題
- GB/T 5023.7-2008額定電壓450/750 V及以下聚氯乙烯絕緣電纜第7部分:二芯或多芯屏蔽和非屏蔽軟電纜
- GB/T 17766-1999固體礦產(chǎn)資源/儲(chǔ)量分類(lèi)
評(píng)論
0/150
提交評(píng)論