




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
會計(jì)學(xué)1C應(yīng)用與開發(fā)案例教程中實(shí)用6.1概述
繼承是C++語言的一種重要機(jī)制,該機(jī)制允許在既有類的基礎(chǔ)上定義新的類,而不需要把既有類的內(nèi)容重新書寫一遍。繼承是通過派生方式實(shí)現(xiàn)的。一個基類可以派生出多個派生類,這種從一個基類中的繼承叫單繼承。如果一個派生類由多個基類派生出來,稱為多繼承。派生類也可以作為基類再派生出新的類,形成類的層次結(jié)構(gòu)。類有三種繼承方式:公有繼承(public)、私有繼承(private)和保護(hù)繼承(protected)。我們可以通過不同的繼承方式限定成員的訪問權(quán)限,以達(dá)到修改已有成員的目的。我們還可以通過同名覆蓋、作用域限定符和虛基類的方法達(dá)到此目的。派生類繼承了基類的所有成員,但構(gòu)造函數(shù)和析構(gòu)函數(shù)除外,因此,在派生類中,想要進(jìn)行特別的初始化和清理工作時,就要加入新的構(gòu)造函數(shù)和析構(gòu)函數(shù)。第1頁/共253頁6.1概述
多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計(jì)中的又一重要特征,多態(tài)的實(shí)現(xiàn)可分為編譯時的多態(tài)和運(yùn)行時的多態(tài)。編譯時的多態(tài),指程序在編譯過程中確定函數(shù)操作的具體對象,通過函數(shù)重載來實(shí)現(xiàn);運(yùn)行時的多態(tài),是指程序在運(yùn)行過程中才能確定函數(shù)操作的具體對象,通過虛函數(shù)來實(shí)現(xiàn)。虛函數(shù)為我們提供了一種更為靈活的多態(tài)性機(jī)制,它體現(xiàn)的是運(yùn)行時的多態(tài),它允許函數(shù)調(diào)用與函數(shù)體之間的關(guān)系在運(yùn)行時才建立。虛函數(shù)是用virtual關(guān)鍵字聲明的非靜態(tài)成員函數(shù)。當(dāng)將基類的同名函數(shù)定義為虛函數(shù)時,我們就可利用基類類型的指針訪問該指針指向的派生類對象的同名原型函數(shù),從而實(shí)現(xiàn)運(yùn)行過程的多態(tài)。第2頁/共253頁6.2派生類的概念6.2.1基類和派生類繼承性在客觀世界中是一種常見的現(xiàn)象。例如:當(dāng)一個小孩出生時,就從父親和母親那里繼承了一定的特征。隨著時間的推移和環(huán)境的變化,這個小孩逐漸有了自己的性格特征,因此這個小孩就具備了從父母那里繼承來的以及自己所獨(dú)有的特征的組合。從面向?qū)ο蟪绦蛟O(shè)計(jì)的觀點(diǎn)看,繼承所表達(dá)的正是這樣一種類與類之間的關(guān)系,這種關(guān)系允許在既有類的基礎(chǔ)上創(chuàng)建新的類。在最簡單的情況下,一個類B繼承類A或者從類A派生出類B,通常類A稱為基類(父類),類B稱為派生類(子類)。這時,類B的對象具有類A對象的所有特性。也可以這樣說,類B從類A派生出來。這意味著類B至少描述了與類A同樣的接口,至少包含了同類A一樣的數(shù)據(jù),可以共享類A的成員函數(shù)。第3頁/共253頁6.2派生類的概念
類B繼承了類A(或稱類A派生了類B),那么類A的公有段成員可以傳遞給派生類B,當(dāng)作類B自己的成員。在創(chuàng)建派生類對象時,先要調(diào)用基類的構(gòu)造函數(shù),以便分配基類的公有段成員存儲空間;實(shí)際上,由于調(diào)用了構(gòu)造函數(shù),一個派生類對象包含有一個基類對象,只是對于派生類而言,不能訪問基類的私有段成員。派生類的定義格式如下:
class派生類名:繼承方式基類名
{//派生類成員定義
}第4頁/共253頁6.2派生類的概念
在上面的格式中,派生類名是新定義的一個類名,它是從由“基類名”所標(biāo)識的類派生而來的。這樣定義的派生類繼承了基類的除了構(gòu)造函數(shù)和析夠函數(shù)之外的所有成員,因此派生類對象由兩部分組成:一部分是由基類繼承的成員,另一部分是派生類新增加的自己特有的成員?!袄^承方式”用于規(guī)定派生類中從基類繼承到的那部分成員在派生類中的訪問控制權(quán)限。繼承方式可用下列3個關(guān)鍵字之一來指定:public:公有繼承;protected:保護(hù)繼承;private:私有繼承。第5頁/共253頁6.2派生類的概念【例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頁/共253頁6.2派生類的概念
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頁/共253頁6.2派生類的概念
程序的輸出結(jié)果為:
13456
該程序創(chuàng)建了兩個對象objA(A類對象)和objB(B類對象)。盡管B類沒有包含像out_pri()這樣的成員函數(shù)說明,對象objB仍可以調(diào)用基類的成員函數(shù)。使用了繼承,B類的任何對象也是A類的對象。該程序創(chuàng)建了兩個對象objA(A類對象)和objB(B類對象)。盡管B類沒有包含out_pri()函數(shù)聲明,但對象objB仍然可以調(diào)用out_pri(),因?yàn)锽類繼承了A類的公有成員函數(shù)out_pri()。第8頁/共253頁6.2派生類的概念6.2.2繼承方式
1.公有繼承在上一小節(jié)中,我們用了公有繼承的例子,這種類型的繼承是最常用的。對于公有繼承來說,基類的公有段成員和保護(hù)段成員在派生類中的訪問屬性不變。派生類的其他成員可以直接訪問繼承來的這些成員。而外部使用者只能通過對象來訪問其公有成員?;惖乃接谐蓡T是不可訪問的(友員除外),就是說,無論是派生類的成員還是派生類的對象都無法訪問基類的私有成員。應(yīng)注意的是:基類的保護(hù)成員只能被派生類的成員訪問,不能被派生類的對象訪問。第9頁/共253頁6.2派生類的概念【例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頁/共253頁6.2派生類的概念
這里,類B由基類A公有派生而來,則A公有段和保護(hù)段的成員在B中也是公有段和保護(hù)段的成員。同時,B又公有派生出類C,這時,B的公有段和保護(hù)段的成員(包括從A繼承過來的成員)在C中也是公有段和保護(hù)段的成員。下面是各成員函數(shù)的實(shí)現(xiàn)代碼:
第11頁/共253頁6.2派生類的概念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頁/共253頁6.2派生類的概念
可見,在B和C中都可以直接使用基類A的保護(hù)段中的成員i和j。下面的實(shí)現(xiàn)說明了怎樣使用這些派生類。
voidmain(){BobjB;CobjC;objB.get_ij();objB.show_ij();objB.make_k();cout<<objB.get_k()<<”\n”;objC.f();objC.show_ij();}第13頁/共253頁6.2派生類的概念
程序的輸出結(jié)果為:
Enterthetwonumbers:1212356
總之,一個派生類如果從基類公有派生出來,則基類成員的訪問權(quán)限在派生類中保持不變。第14頁/共253頁6.2派生類的概念2.私有繼承當(dāng)類的繼承方式為私有繼承時,基類公有段成員和保護(hù)段成員在派生類中作為私有成員,派生類的其他成員可以直接訪問它們。但是,在外部通過派生類的對象無法訪問。基類的私有成員仍然不可訪問(友員除外),即不允許派生類的成員函數(shù)訪問基類的私有成員。經(jīng)過私有繼承之后,所有基類的成員都成為派生類的私有成員,如果進(jìn)一步派生的話,新的派生類的成員函數(shù)就不能訪問已變成私有的基類的成員。修改前面公有派生的例子如下:第15頁/共253頁6.2派生類的概念【例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頁/共253頁6.2派生類的概念
這里,派生類B由基類A私有派生,A的公有段和保護(hù)段的成員在B中變成了私有段的成員。同時,C又由類B公有派生,則B的公有段和保護(hù)段的成員(注意:并不包括從A繼承過來的成員,它們屬于B的私有段)成為C的公有段和保護(hù)段的成員。下面是各成員函數(shù)的實(shí)現(xiàn):第17頁/共253頁6.2派生類的概念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;//錯誤
}第18頁/共253頁6.2派生類的概念
類A的保護(hù)段成員i和j已經(jīng)成了類B的私有段成員,因此,B的派生類C以及B的對象都不能訪問A的保護(hù)段成員i和j、以及A的公有段成員函數(shù)get_ij()和show_ij()。下面的實(shí)現(xiàn)說明了這些派生類的使用。
voidmain(){BobjB;CobjC;objB.get_ij();//錯誤,外部對象無法訪問私有成員
objB.show_ij();//錯誤,外部對象無法訪問私有成員
objB.make_k();cout<<objB.get_k()<<”\n”;objC.show_ij();//錯誤,外部對象無法訪問私有成員
}第19頁/共253頁6.2派生類的概念3.保護(hù)繼承無論是公有繼承,還是私有繼承,派生類都不能直接訪問基類的私有成員,要想訪問某些私有成員只能通過調(diào)用基類的成員函數(shù),這樣顯得很不方便。要想使用這些私有成員既便于派生類訪問,又禁止外界對它的操作,可以把這些私有成員定義為保護(hù)成員。當(dāng)派生類使用保護(hù)繼承方式派生時,基類的公有成員和保護(hù)成員在派生類中具有保護(hù)成員訪問屬性。這樣,派生類的其他成員函數(shù)就可以直接訪問它們,但在類外部通過派生類的對象無法訪問。而基類的私有成員仍然是不可訪問的(友員除外)。第20頁/共253頁6.2派生類的概念
對于私有繼承和保護(hù)繼承的直接派生類中,基類私有成員都是不可見的,基類保護(hù)成員和公有成員是可見的;對派生類對象而言,所有成員都不可見。兩種繼承方式的效果似乎完全相同。但是,如果派生類作為新的基類,繼續(xù)派生時,二者的區(qū)別就出現(xiàn)了。假設(shè)A類以私有繼承方式派生B類,B類又派生C類,那么C類的成員函數(shù)和對象都不能訪問間接從A類中繼承來的成員。如果A類以保護(hù)繼承方式派生B類,那么A類中的公有和保護(hù)成員在B類中都是保護(hù)成員。B類再派生出C類后,A類中的公有和保護(hù)成員被C類繼承后,有可能是保護(hù)的或者是私有的(由類C對類B的繼承方式?jīng)Q定)。因此,C類的成員有可能可以訪問間接從類A中繼承來的成員。第21頁/共253頁6.2派生類的概念【例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頁/共253頁6.2派生類的概念
voidmake_k();{k=i+j;//錯誤,不能訪問基類私有成員
}};classC:publicB{public:voidf();{i=5;//錯誤,不能訪問基類私有成員
j=6;//正確,在派生類中是保護(hù)成員
}};第23頁/共253頁6.2派生類的概念6.2.3調(diào)整訪問聲明基類成員被派生類繼承后,在派生類中的訪問聲明主要由派生類定義時的繼承方式來決定。但是,在C++程序設(shè)計(jì)過程中,我們希望基類某些成員的訪問聲明以私有或保護(hù)繼承方式時,在派生類中的訪問聲明不變,保持在基類中的訪問聲明,這可通過調(diào)整訪問聲明來實(shí)現(xiàn)。格式如下:基類類名::基類保護(hù)段或公有段數(shù)據(jù)成員基類類名::基類保護(hù)段或公有段成員函數(shù)名第24頁/共253頁6.2派生類的概念例如:classA{inta;public:intb,c;intaf();};classD:privateA{intd;public:A::c;//調(diào)整對A::c的訪問聲明
inte;intdf();};第25頁/共253頁6.2派生類的概念
類D從基類A私有派生,類A的所有公有段和保護(hù)段的成員都為類D的私有段,使用訪問聲明
A::c
可以將類B的公有段成員c在私有派生類D中顯式聲明為公有的,D的派生類可以訪問它。由此可見,調(diào)整訪問聲明是私有派生的一種補(bǔ)充。對訪問聲明的調(diào)整要注意以下幾點(diǎn):第26頁/共253頁6.2派生類的概念1.調(diào)整時不能說明任何類型。
classA{inta;public:intb,c;intaf();};classD:privateA{intd;public:intA::c;//錯誤
…};第27頁/共253頁6.2派生類的概念2.對訪問聲明的調(diào)整僅用于派生類中恢復(fù)名字的訪問權(quán)限,不允許在派生類中降低或提升基類成員的可訪問性。即:基類中的公有段成員或保護(hù)段成員在派生類中僅能說明為相對應(yīng)的公有段成員或保護(hù)段成員;基類的私有成員不能用于訪問聲明的調(diào)整。
從類的封裝性的角度來看,這一限制是可以理解的。因?yàn)槿绻缮惸軐⑵浠惖乃接谐蓡T用訪問聲明提高其訪問權(quán)限而成為公有段的成員,這就破壞了類的封裝性。第28頁/共253頁6.2派生類的概念
classA{inta;protected:intb;public:intc;};classD:privateA{protected:A::b;A::c//錯誤,不能降低基類成員的可訪問性
public:A::c;A::a;//私有成員不能用于訪問聲明的調(diào)整
};第29頁/共253頁6.2派生類的概念3.對重載函數(shù)名的訪問聲明將調(diào)整基類中具有該名的所有函數(shù)的訪問權(quán)限。由于調(diào)整訪問聲明僅僅是恢復(fù)名字的訪問,對于重載函數(shù)名,它的訪問聲明將使所有同名的重載函數(shù)的訪問權(quán)限都得到調(diào)整。
classA{public:f();f(int);};classB:privateA{public:A::f;//使A::f()和A::f(int)在B中都是公有的
};第30頁/共253頁6.2派生類的概念
其中,A::f()表示訪問聲明僅僅調(diào)整名字,該函數(shù)名不帶任何參數(shù)和類型;同時,它使得A::f()和A::f(int)在類B中都處于公有段。
classA{private:f(int);public:f();};classB:privateA{public:A::f;//錯誤,訪問聲明具有二義性,不能調(diào)整其訪問
};第31頁/共253頁6.2派生類的概念
同時也意味著,如果派生類和基類有同名的成員,則不可調(diào)整基類成員的訪問。
classA{public:voidf();};classB:privateA{public:voidf();A::f;//錯誤,f的二次聲明,不能調(diào)整訪問
};第32頁/共253頁6.2派生類的概念6.2.4類層次中的訪問規(guī)則
1.在派生類中對基類成員的覆蓋
C++允許派生類重新定義基類的成員。如果派生類定義了與基類同名的成員,稱派生類的成員覆蓋了基類的同名成員。如果要在派生類中使用基類的同名成員,可以顯式地使用如下格式:類名::成員來調(diào)用基類的成員。第33頁/共253頁6.2派生類的概念
引用被覆蓋的基類同名成員時,應(yīng)使用類名限定符加以限定。當(dāng)派生類與基類有同名成員時,引用不同的派生類對象中的這些成員時,還需指明對象名。應(yīng)當(dāng)注意的是,不管是公有派生還是私有派生都不影響派生類對基類的靜態(tài)成員的訪問,但訪問靜態(tài)成員時,必須用“類名::成員”顯式地說明。例如:第34頁/共253頁6.2派生類的概念classA{public:staticvoidsa();//靜態(tài)成員
voidfa();};classB:privateA{};//全私有派生classC:publicB{voidfc(){A::sa();//正確
fa();//錯誤
sa();//錯誤
}};第35頁/共253頁6.2派生類的概念2.基類和派生類的賦值兼容規(guī)則對于基類對象和派生類對象,在公有派生條件下,C++語言允許派生類對象到基類對象的自動轉(zhuǎn)換,這通常稱為賦值兼容規(guī)則。賦值兼容規(guī)則:當(dāng)派生類從基類公有繼承時,允許以下4種派生類對象到基類對象的自動轉(zhuǎn)換。規(guī)則1:可以用派生類對象為基類對象賦值;規(guī)則2:可以用派生類對象初始化基類引用對象;規(guī)則3:可以把指向派生類對象指針賦給基類對象的指針;規(guī)則4:可以把派生類對象的地址賦給基類對象的指針。每個派生類對象包含有一個基類對象,因此,上述規(guī)則不難理解。第36頁/共253頁6.2派生類的概念6.2.5派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù)派生類繼承了基類的所有成員,但基類中的構(gòu)造函數(shù)和析構(gòu)函數(shù)是不能被繼承的。當(dāng)我們想對派生類中新添加的成員進(jìn)行初始化時,就必須按實(shí)際需要添加新的構(gòu)造函數(shù)。如果要對從基類繼承下來的成員進(jìn)行初始化,則還要由基類的構(gòu)造函數(shù)完成。當(dāng)需要傳遞參數(shù)給基類構(gòu)造函數(shù)時,必須為派生類建立一個構(gòu)造函數(shù),并由它來傳遞基類的構(gòu)造函數(shù)所需的參數(shù)。在C++中,派生類構(gòu)造函數(shù)的聲明為:派生類構(gòu)造函數(shù)(參數(shù)總表):基類名(參數(shù)表),對象成員1(對象成員參數(shù)表),
…對象成員n(對象成員參數(shù)表)
{…}第37頁/共253頁6.2派生類的概念
若基類使用缺省的構(gòu)造函數(shù)或不帶參數(shù)的構(gòu)造函數(shù),則在派生類中定義構(gòu)造函數(shù)時可省略該基類名。如果基類定義了帶有參數(shù)的構(gòu)造函數(shù),則派生類就應(yīng)該定義構(gòu)造函數(shù),以保證對調(diào)用基類構(gòu)造函數(shù)的對象進(jìn)行初始化。派生類構(gòu)造函數(shù)的調(diào)用順序是:
1.先祖先(基類),調(diào)用基類的構(gòu)造函數(shù)。
2.再客人(對象成員),調(diào)用成員對象的構(gòu)造函數(shù)。
3.后自己(派生類本身)。調(diào)用派生類的構(gòu)造函數(shù)。第38頁/共253頁6.2派生類的概念【例6-5】#include<iostream.h>classBase{public:Base(){cout<<”Basecreated”<<endl;}classDerived:publicBase{public:Derived(){cout<<”Derivedcreated”<<endl;}};voidmain(){Deriveda;}第39頁/共253頁6.2派生類的概念
程序的輸出結(jié)果為:
BasecreatedDerivedcreated
從輸出結(jié)果可以看出:先執(zhí)行基類的構(gòu)造函數(shù),再執(zhí)行派生類的構(gòu)造函數(shù)。另一方面,執(zhí)行析構(gòu)函數(shù)時,先執(zhí)行派生類的析構(gòu)函數(shù)再執(zhí)行基類的析構(gòu)函數(shù)。原因是顯而易見的,對基類的破壞隱含了對派生類的破壞,所以派生類的析構(gòu)函數(shù)必須先執(zhí)行。第40頁/共253頁6.3多繼承
6.3.1多繼承的概念
前面介紹的派生類只有一個直接基類,這種繼承稱為單繼承。C++允許一個派生類具有兩個或兩個以上基類,這種繼承稱為多繼承。多繼承在實(shí)際中也是非常有用的。常見的例子是軟件系統(tǒng)的windows風(fēng)格用戶界面設(shè)計(jì)。軟件系統(tǒng)的windows風(fēng)格用戶界面包括窗口、尺寸框、橫向滾動條、縱向滾動條以及各種類型的按鈕。我們可以先分別設(shè)計(jì)出窗口類、尺寸框類、橫向滾動條類、縱向滾動條類以及各種按鈕類,然后設(shè)計(jì)用戶界面類。用戶界面類把窗口類、尺寸框類、橫向滾動條類、縱向滾動條類以及各種按鈕類作為基類,通過多繼承產(chǎn)生。第41頁/共253頁6.3多繼承
定義具有兩個以上基類的派生類與定義單基類的派生類的形式相似,只要將繼承的多個基類用逗號分隔即可。多繼承方式派生類的定義格式為:
class派生類名:繼承方式1基類名1,繼承方式2基類名2,…{//派生類成員的定義
};
對每個基類可以用不同的繼承方式,缺省繼承方式為private。例如:
classC:publicA,B//類C公有繼承類A,私有繼承類B{…};第42頁/共253頁6.3多繼承
在多重繼承中,公有派生、私有派生和保護(hù)派生對于基類成員在派生類中的可訪問性與單繼承的規(guī)則相同。在多重繼承中,派生類的構(gòu)造函數(shù)與單繼承下派生類構(gòu)造函數(shù)相似,它必須負(fù)責(zé)該派生類所有基類構(gòu)造函數(shù)以及對象成員(如果有的話)構(gòu)造函數(shù)的調(diào)用。同時,派生類的參數(shù)必須包含完成所有基類、對象成員以及派生類中新增數(shù)據(jù)成員初始化所需的參數(shù)。派生類構(gòu)造函數(shù)執(zhí)行順序是:
1.所有基類的構(gòu)造函數(shù);多個基類構(gòu)造函數(shù)的執(zhí)行順序取決于定義派生類時所指定的順序,與派生類構(gòu)造函數(shù)中所定義的成員初始化列表的參數(shù)順序無關(guān);
2.對象成員的構(gòu)造函數(shù);
3.派生類本身的構(gòu)造函數(shù)。多繼承的析構(gòu)函數(shù)與單繼承的一樣,析構(gòu)函數(shù)的調(diào)用順序正好與構(gòu)造函數(shù)的相反。第43頁/共253頁6.3多繼承6.3.2虛基類
1.虛基類的概念派生類及其基類可用一有向無環(huán)圖表示,其中的箭頭表示“由派生而來”。考慮下面的例子:第44頁/共253頁6.3多繼承classA{…public:inta;…};classB:publicA{…};
classC:publicA{…
};classD:publicB,publicC{public:voidf(inti){a=i;}};第45頁/共253頁6.3多繼承
這里,類A兩次成為類D的間接基類。這就意味著D類對象中有兩個A對象:由類B繼承的A和類C繼承的A。如下圖所示:
AA↑↑BC↖↗D
圖6-1
在D::f(inti)函數(shù)中,a有兩個拷貝(B路徑繼承來的a和C路徑繼承來的a),因此語句a=0具有二義性,它是將B::a置為0,還是將C::a置為0?第46頁/共253頁6.3多繼承
如果希望間接基類A與其派生類的關(guān)系是如下圖所示:
A↗↖BC↖↗D
顯然,目前多繼承的方法不能描述這種情況,需要新的描述方法--顯式地指明間接基類與其派生類的這種單拷貝關(guān)系。C++提供了這種描述手段:它將A說明為B和C的虛基類;當(dāng)在多條繼承路徑上有一個公共的基類(如本例的A),在這些路徑中的某幾條路徑匯合處(如本例的D),這個公共基類就會產(chǎn)生多個實(shí)例,可以將這個公共基類說明為虛基類。它僅是簡單地在繼承的基類前加關(guān)鍵字virtual,例如把上例改為:第47頁/共253頁6.3多繼承classA{…public:inta;…};classB:virtualpublicA{…};
classC:virtualpublicA{…
};classD:publicB,publicC{public:voidf(inti){a=i;}};第48頁/共253頁6.3多繼承
這時D類對象中只有A的一個拷貝,因而函數(shù)D::f(int)中的語句a=i,沒有二義性。此例中,對于類D而言,類A是類C的虛基類,而是類B的真基類;但對于類C而言,類A仍是類C的真基類,虛基類只是個相對的概念。如果把上例稍稍修改一下:
…classD:publicC,publicB…
則對于類D而言,類A是類B的虛基類,而是類C的真基類。第49頁/共253頁6.3多繼承
一個派生類的對象的地址可以直接賦給虛基類的指針。例如:
Dobj1;A*ptr=&obj1;
這時不需要強(qiáng)制類型轉(zhuǎn)換。并且,一個虛基類的引用可以引用一個派生類的對象。例如:
Dobj2;A&ref=obj2;
反之則不行,無論在強(qiáng)制類型轉(zhuǎn)換中指定什么路徑,一個虛基類的指針或引用不能轉(zhuǎn)換為派生類的指針或引用。例如:
D*p=(D*)(B*)ptr;
將產(chǎn)生編譯錯誤。第50頁/共253頁6.3多繼承2.虛基類對象的初始化加入虛基類后,它的初始化在語法上與一般多繼承的初始化是一樣的,但在調(diào)用構(gòu)造函數(shù)的順序上有點(diǎn)差別。(1)先調(diào)用虛基類構(gòu)造函數(shù),然后調(diào)用非虛基類的構(gòu)造函數(shù);(2)當(dāng)同一層有多個虛基類,按照它們的說明順序調(diào)用它們的構(gòu)造函數(shù);(3)當(dāng)虛基類是由非虛基類派生時,則先調(diào)用基類構(gòu)造函數(shù),再調(diào)用派生類構(gòu)造函數(shù)。第51頁/共253頁6.3多繼承classX:publicY,virtualpublicZ{}Xone;將產(chǎn)生如下調(diào)用次序:Z()Y()X()這里Z是X的虛基類,故先調(diào)用Z的構(gòu)造函數(shù),再調(diào)用Y的構(gòu)造函數(shù),最后才調(diào)用派生類X自己的構(gòu)造函數(shù)。第52頁/共253頁6.3多繼承
classbase1{};classbase2{};classlevel1:publicbase2,virtualpublicbase1{};classlevel2:publicbase2,virtualpublicbase1{};classtoplevel:publiclevel1,virtualpubliclevel2{};toplevelview;第53頁/共253頁6.3多繼承
類等級關(guān)系如下圖所示:
base2base1base2↖↗↖↗level1level2↖↗toplevel1
圖6-2第54頁/共253頁6.3多繼承當(dāng)建立對象view時,將產(chǎn)生如下調(diào)用次序:
level2():base1()base2()level2()level1():base2()level1()toplevel():toplevel()第55頁/共253頁6.3多繼承toplevel有兩個基類:一個是虛基類level2,另一個是非虛基類level1。根據(jù)規(guī)定:應(yīng)先執(zhí)行l(wèi)evel2的構(gòu)造函數(shù);level2也有兩個基類,一個是虛基類base1,另一個是非虛基類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又有兩個基類,base1是虛基類,無需再執(zhí)行其構(gòu)造函數(shù),base2是非虛基類,因此要先執(zhí)行base2的構(gòu)造函數(shù),然后執(zhí)行l(wèi)evel1的構(gòu)造函數(shù)。最后執(zhí)行toplevel的構(gòu)造函數(shù)。上例中,對于toplevel的對象而言,base1是level1的虛基類。注意:虛基類和非虛基類在使用上是不同的。
第56頁/共253頁6.4多態(tài)性與虛函數(shù)
在講述多態(tài)性和虛函數(shù)之前,先介紹基類的指針。
6.4.1指向基類對象的指針指向派生類對象
指向基類和指向派生類的指針變量是相關(guān)的。假設(shè)A是基類,B是從A公有派生出來的派生類,在C++中,任何被說明為指向A的指針也可以指向B。例如:
A*p;//指向類型A的對象的指針
AobjA;//類型A的對象
BobjB;//類型B的對象
p=&objA;//p指向類型A的對象
p=&objB;//p指向類型B的對象利用指針p,可以訪問從基類A繼承的成員,但B自己定義的成員不能用p訪問(除非用了顯式類型轉(zhuǎn)換把A轉(zhuǎn)換成B)。第57頁/共253頁6.4多態(tài)性與虛函數(shù)
任何聲明為指向基類的指針,它可以指向它的公有派生類對象,這種指針只能直接訪問那些從基類繼承來的成員,不能直接訪問公有派生類中的新添成員。如果派生類是以私有方式派生的,則基類的指針不能指向派生類對象。如果我們想用基類的指針調(diào)用派生類的特定成員,則可以將基類指針顯式轉(zhuǎn)換為派生類指針來實(shí)現(xiàn)。一個指向基類的指針可用來指向從基類公有派生的任何對象,這一事實(shí)是非常重要的,是C++實(shí)現(xiàn)運(yùn)行時多態(tài)性的關(guān)鍵途徑。第58頁/共253頁6.4多態(tài)性與虛函數(shù)6.4.2多態(tài)性多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計(jì)的重要特征。它是指同樣的消息被不同類型的對象接收時會產(chǎn)生不同行為。多態(tài)的實(shí)現(xiàn)可分為編譯時的多態(tài)和運(yùn)行時的多態(tài):編譯時的多態(tài),指程序在編譯過程中確定函數(shù)操作的具體對象,通過函數(shù)重載來實(shí)現(xiàn),重載是多態(tài)性的一種簡單形式;運(yùn)行時的多態(tài),是指程序在運(yùn)行過程中才能確定函數(shù)操作的具體對象,通過虛函數(shù)實(shí)現(xiàn)(下節(jié)介紹)。第59頁/共253頁6.4多態(tài)性與虛函數(shù)1.編譯時的多態(tài)性編譯時的多態(tài)是在程序編譯過程中就確定同名操作的具體操作對象,即決定調(diào)用哪個同名函數(shù)。編譯時的多態(tài)性可以通過重載函數(shù)來實(shí)現(xiàn)。有關(guān)一個類中函數(shù)重載的問題,我們已經(jīng)在前面介紹過。下面是一個基類成員函數(shù)在派生類中重載(也就是實(shí)現(xiàn)編譯時的多態(tài)性)的例子。
2.運(yùn)行時的多態(tài)性在實(shí)際情況中,許多對象以及對對象的操作往往不能再編譯時就確定下來,它們需要在程序的運(yùn)行過程中確定,這就是運(yùn)行時的多態(tài)性。解決這個問題就是采用動態(tài)綁定,具體是用虛函數(shù)來實(shí)現(xiàn)。在下一節(jié)中具體地介紹虛函數(shù)。第60頁/共253頁6.4多態(tài)性與虛函數(shù)6.4.3虛函數(shù)
虛函數(shù)是在基類中被冠以virtual的成員函數(shù),它提供了一種接口界面,虛函數(shù)可以在一個或多個派生類中被重新定義,但要求在派生類中重新定義時,虛函數(shù)的函數(shù)原型,包括返回類型、函數(shù)名、參數(shù)個數(shù)、參數(shù)類型的順序,必須完全相同。第61頁/共253頁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頁/共253頁6.4多態(tài)性與虛函數(shù)classsecond_d:publicbase{public:second_d(inta):base(a){}voidwho(){cout<<secondderivation<<endl<<x<<endl;}};
建立了一個類等級,兩個派生類中都重新定義了基類的成員函數(shù)who()。下面是這個類等級的使用:第63頁/共253頁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頁/共253頁6.4多態(tài)性與虛函數(shù)
程序的輸出結(jié)果為:
base1base2base3
指向基類的指針p,不管是指向基類的對象base_obj還是指向派生類的對象first_obj和second_obj,p->who()調(diào)用的都是基類定義的who()版本。這說明,通過指針引起的普通成員函數(shù)的調(diào)用,僅僅與指針(或引用)的類型有關(guān),而與此刻正在指向什么對象無關(guān)。因?yàn)榛惖闹羔槂H能訪問派生類中繼承的基類成員,而不能訪問派生類自己的成員。在這種情況下,必須顯示地用
first_obj.who();和second_obj();第65頁/共253頁6.4多態(tài)性與虛函數(shù)
這樣才能調(diào)用類first_d和類second_d中定義的who()的版本。其本質(zhì)的原因在于普通成員函數(shù)的調(diào)用是在編譯時靜態(tài)區(qū)分的。如果隨著p所指向的對象的不同,p->who()能調(diào)用不同類中who()的版本,這樣就可以用一個界面p->who()訪問多個實(shí)現(xiàn)版本:base中的who()、first_d中的who()和second_d中的who(),這在編程時非常有用。實(shí)際上,這表達(dá)了一種動態(tài)的性質(zhì),函數(shù)調(diào)用p->who()依賴于運(yùn)行時p所指向的對象,虛函數(shù)提供的就是這種解釋機(jī)制,如果在base中將成員函數(shù)who()說明為虛函數(shù),則修改上述程序?yàn)椋?/p>
第66頁/共253頁6.4多態(tài)性與虛函數(shù)【例6-7】#include<iostream.h>classbase{protected:intx;public:base(inta){x=a;}virtualvoidwho()//說明為虛函數(shù)
{cout<<”base”<<x<<endl;}};
classfirst_d:publicbase{public:first_d(inta):base(a){}voidwho(){cout<<”firstderivation”<<endl<<x<<endl;}};第67頁/共253頁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頁/共253頁6.4多態(tài)性與虛函數(shù)
程序的輸出結(jié)果為:
base1firstderivation2secondderivation3
這里,語句p->who()出現(xiàn)了3次,由于p所指向的對象不同,每次出現(xiàn)都執(zhí)行了who()的不同實(shí)現(xiàn)版本?;惖奶摵瘮?shù)who()定義了一種接口,在派生類中為此接口定義了不同的實(shí)現(xiàn)版本,由于虛函數(shù)的解釋機(jī)制,實(shí)現(xiàn)了“單界面、多實(shí)現(xiàn)版本”的思想。這種在運(yùn)行時刻將函數(shù)界面與函數(shù)的不同實(shí)現(xiàn)版本進(jìn)行匹配的過程,稱為動態(tài)綁定,也稱為運(yùn)行時的多態(tài)性。第69頁/共253頁6.4多態(tài)性與虛函數(shù)
用虛函數(shù)實(shí)現(xiàn)運(yùn)行時多態(tài)性的關(guān)鍵之處是:必須用指向基類的指針訪問虛函數(shù)。盡管可以像調(diào)用其他成員函數(shù)那樣顯示地用對象名來調(diào)用一個虛函數(shù),但只有在同一個指向基類的指針訪問虛函數(shù)時,運(yùn)行時多態(tài)性才能實(shí)現(xiàn)。由于p指向的對象不同,因此調(diào)用了who()的3個不同實(shí)現(xiàn)版本,這時,稱為函數(shù)who()具有虛特性。第70頁/共253頁6.4多態(tài)性與虛函數(shù)
基類函數(shù)具有虛特性的條件是:
1.在基類中,將該函數(shù)說明為虛(virtual)函數(shù);
2.定義基類的公有派生類;
3.在基類的公有派生類中一模一樣地重載該虛函數(shù);
4.定義指向基類的指針變量,它指向基類的公有派生類的對象。
第71頁/共253頁6.4多態(tài)性與虛函數(shù)
注意:在一個派生類中重新定義基類的虛函數(shù)是函數(shù)重載的另一種形式。但它不同于一般的函數(shù)重載。當(dāng)重載一般的函數(shù)時,函數(shù)的返回類型和參數(shù)表可能是不相同的,僅函數(shù)名要求相同。但重載一個虛函數(shù)時,要求函數(shù)名、返回類型、參數(shù)個數(shù)。參數(shù)類型和順序是完全相同的。如果函數(shù)原型不同,僅函數(shù)名相同,C++認(rèn)為這是一般的函數(shù)重載,此時虛特性丟失。例如:第72頁/共253頁6.4多態(tài)性與虛函數(shù)classbase{public:virtualvoidvf1();virtualvoidvf2();virtualvoidvf3();voidf();};classderived:publicbase{public:voidvf1();//具有虛特性
voidvf2(int);//一般函數(shù)重載,
參數(shù)不同,虛特性丟失
charvf3();//錯誤:僅返回類型不同
voidf();//一般的函數(shù)重載非虛函數(shù)的重載};第73頁/共253頁6.4多態(tài)性與虛函數(shù)
voidg(){derivedd;base*bp->&d;//基類指針指向派生類
bp->vf1();//調(diào)用derived::vf1()bp->vf2();//調(diào)用base::vf2()bp->f();//調(diào)用base::f()}第74頁/共253頁6.4多態(tài)性與虛函數(shù)
在派生類derived中的函數(shù)vf1()與基類base中的虛函數(shù)vf1()具有完全相同的函數(shù)原型,故保持了虛特性;而函數(shù)vf2(int)與基類中的虛函數(shù)vf2()參數(shù)不同,僅函數(shù)名相同,這只是一般函數(shù)的重載,其虛特性丟失;函數(shù)charvf3()同基類的虛函數(shù)voidvf3()相比較,僅返回類型不同,目前的C++實(shí)現(xiàn)認(rèn)為這是錯誤的;函數(shù)f()僅僅是基類非虛函數(shù)f()的重載。由于vf1()保持了虛特性,vf2()丟失了虛特性,因此,在進(jìn)行函數(shù)調(diào)用時,結(jié)果不一樣。第75頁/共253頁6.4多態(tài)性與虛函數(shù)
在函數(shù)g()中,語句bp->vf1(),調(diào)用的derived::vf1()。在派生類derived中,函數(shù)vf1定義為虛函數(shù),虛函數(shù)調(diào)用的解釋依賴于調(diào)用它的對象類型,bp雖然是指向基類的指針,但此刻指向的是派生類對象d,因此,該語句等價d.vf1();另外一條語句bp->f(),調(diào)用的卻是base::f()。函數(shù)f()在基類和派生類中均已定義,且函數(shù)原型相同,但它是一個非虛函數(shù),非虛函數(shù)調(diào)用的解釋僅依賴于表示調(diào)用它的對象的指針或引用類型。bp被聲明為指向基類的指針,非虛函數(shù)的調(diào)用僅僅依賴bp是指向基類的指針,而不在乎bp此刻是否正在指向派生類的對象,它調(diào)用的是base();同樣地,語句bp->vf2()調(diào)用的是base::vf2()。第76頁/共253頁6.4多態(tài)性與虛函數(shù)
虛函數(shù)的這種特性使派生類和虛函數(shù)成為許多C++程序設(shè)計(jì)的關(guān)鍵,因?yàn)榛惪梢允褂锰摵瘮?shù)提供一個界面,這是該類的所有公有派生類都具有的共同界面,但派生類可以定義自己的實(shí)現(xiàn)版本,而且虛函數(shù)調(diào)用的解釋依賴于調(diào)用它的對象類型,指向基類對象的指針指向不同派生類的對象,就能訪問虛函數(shù)的不同實(shí)現(xiàn)版本。
第77頁/共253頁6.4多態(tài)性與虛函數(shù)
虛函數(shù)必須是類的成員函數(shù)。不能將虛函數(shù)說明為全局(非成員的)函數(shù),也不能說明為靜態(tài)成員函數(shù)。不能將友員說明為虛函數(shù),但虛函數(shù)可以是另一個類的友員。一旦一個函數(shù)被說明為虛函數(shù),不管經(jīng)歷了多少派生類層,都將保持其虛特性。當(dāng)一個派生類沒有重新定義虛函數(shù)時,則使用其基類的虛函數(shù)版本。因此,在使用時要記住繼承的層次性。
第78頁/共253頁6.4多態(tài)性與虛函數(shù)6.4.4純虛函數(shù)及抽象類
基類往往表示一些抽象的概念。例如,shape是一個基類,它表示具有形狀的東西,從shape可以派生出封閉圖形和非封閉圖形兩個派生類,封閉圖形又可以派生出橢圓形、多邊形等。這個類等級的基類shape體現(xiàn)了一個抽象的概念,在shape中定義一個求面積的函數(shù)顯然是無意義的,但可以將其說明為虛函數(shù),提供各派生類一個公共的界面,并由各派生類提供求面積函數(shù)的各自版本。在這種情況下,基類的有些虛函數(shù)沒有定義是很正常的,但是要求派生類必須重新定義這些虛函數(shù),以使派生類有意義。為此,C++引入了純虛函數(shù)的概念。第79頁/共253頁6.4多態(tài)性與虛函數(shù)
純虛函數(shù)是一個在基類中說明的虛函數(shù),它在該基類中沒有定義,要求任何派生類都必須定義自己的版本。為說明一純虛函數(shù),應(yīng)使用如下格式:
virtualtypefunc_name(參數(shù)表)=0;這里,type是函數(shù)的返回類型,func_name是函數(shù)名。第80頁/共253頁6.4多態(tài)性與虛函數(shù)
如果一個類至少有一個純虛函數(shù),那么就稱該類為抽象類。抽象類機(jī)制支持一般概念的表示。例如上面談到的形狀類shape是一般的概念,可以表達(dá)為抽象類,它有許多具體的變種,如圓形和方形才是具體可用的類。抽象類也可用于定義接口,由派生類提供各種實(shí)現(xiàn)。抽象類只能用作其他類的基類,抽象類不能建立對象。抽象類不能用作參數(shù)類型、函數(shù)返回類型或顯式轉(zhuǎn)換的類型。但可以聲明抽象類的指針和引用。例如:第81頁/共253頁6.4多態(tài)性與虛函數(shù)classpoint{…};classshape{pointcenter;…public:pointwhere(){returncenter;}
voidmove(pointp){center=p;draw();}virtualvoidrotate(int)=0;//純虛函數(shù)的定義
virtualvoiddraw()=0;//純虛函數(shù)的定義
};第82頁/共253頁6.4多態(tài)性與虛函數(shù)shapex;//錯誤:抽象類不能建立對象shape*p;//可以聲明抽象類的指針shapef();//錯誤:抽象類不能作為返回類型voidg(shape);//錯誤:抽象類不能作為參數(shù)類型shape&h(shape&);//可以聲明抽象類的引用第83頁/共253頁6.4多態(tài)性與虛函數(shù)
從基類繼承來的純虛函數(shù),在派生類中仍是純虛函數(shù)。例如:classab_circle:publicshape{intradius;public:voidrotate(int){…}};第84頁/共253頁6.4多態(tài)性與虛函數(shù)
由于shape::draw()是一個純虛函數(shù),缺省的ab_circle::draw()也是一個純虛函數(shù),這時ab_circle仍為抽象類。要使ab_circle類為非抽象的,必須如下說明:
classab_circle:publicshape{intradius;public:voidrotate(int){…}voiddraw(){}};第85頁/共253頁6.4多態(tài)性與虛函數(shù)6.4.5構(gòu)造函數(shù)與虛析構(gòu)函數(shù)
1.構(gòu)造函數(shù)因?yàn)樵谂缮愔袠?gòu)造函數(shù)是不能繼承的,也沒有重定義的必要。在構(gòu)造函數(shù)中調(diào)用虛函數(shù)將破壞動態(tài)綁定邏輯。下面的例子說明了這樣動態(tài)綁定邏輯。第86頁/共253頁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頁/共253頁6.4多態(tài)性與虛函數(shù)
virtualvoidprint(){cout<<”Thevirtualfunctioninderiveiscalled!”<<endl;cout<<y<<endl;}};voidmain(){deriveobj(10);}第88頁/共253頁6.4多態(tài)性與虛函數(shù)
程序的輸出結(jié)果為:
Thevirtualfunctioninbaseiscalled!11Thevirtualfunctioninderiveiscalled!10
程序從創(chuàng)建派生類的對象開始執(zhí)行,在執(zhí)行中,先要調(diào)用基類的構(gòu)造函數(shù)。此時派生類的創(chuàng)建過程尚未完成,只能按靜態(tài)綁定調(diào)用基類的虛函數(shù),無法實(shí)現(xiàn)預(yù)期的多態(tài)邏輯。第89頁/共253頁6.4多態(tài)性與虛函數(shù)2.虛析構(gòu)函數(shù)析構(gòu)函數(shù)可以通過virtual修飾而聲明為虛析構(gòu)函數(shù)。虛析構(gòu)函數(shù)與一般虛函數(shù)的不同之處在于:
溫馨提示
- 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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 建筑工程項(xiàng)目委托管理合同
- 工作流程標(biāo)準(zhǔn)化操作指南說明
- 中介業(yè)務(wù)合作協(xié)議合同
- 夫妻離婚協(xié)議書年
- 醫(yī)院治療流程規(guī)范
- 混凝土運(yùn)輸承包合同
- 2025年武漢貨運(yùn)資格證考試答題20題
- 三農(nóng)品牌塑造與推廣策略手冊
- 2025年哈爾濱貨運(yùn)從業(yè)資格證模擬考試
- 2025年自貢貨運(yùn)從業(yè)資格證考試模擬考試題庫下載
- 主體結(jié)構(gòu)實(shí)體檢測方案
- 電焊工培訓(xùn)課件
- 考研考博-英語-天津職業(yè)技術(shù)師范大學(xué)考試押題三合一+答案詳解4
- 原型范疇理論(精選)課件
- 網(wǎng)絡(luò)賬號信息登記表
- 鋼樓梯計(jì)算(自動版)
- 部編版八年級歷史(下)全冊教案
- 水土保持各種分級標(biāo)準(zhǔn)表與指標(biāo)
- (完整版)學(xué)生課堂學(xué)習(xí)自我評價表
- Unit 1 How tall are you B Read and write(課堂PPT)
- 最新部編版九年級語文下冊 12《詞四首》課后習(xí)題參考答案
評論
0/150
提交評論