




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、版權(quán)所有版權(quán)所有 復(fù)制必究復(fù)制必究本章主要內(nèi)容本章主要內(nèi)容:l類和對象l類繼承l(wèi)多態(tài)性:虛函數(shù),重載,模板 與傳統(tǒng)的面向過程的程序設(shè)計語言相比,C+語言的最大特征是支持面向?qū)ο蟪绦蛟O(shè)計OOP(Object Oriented Programming),它引入了類、繼承、多態(tài)和重載等面向?qū)ο蟮男聶C制。通過本章的學(xué)習(xí),使我們系統(tǒng)地介紹C+面向?qū)ο笤O(shè)計的基本方法。結(jié)構(gòu)化程序設(shè)計的特點:結(jié)構(gòu)化程序設(shè)計的特點:l是一種自上而下、逐步細化的模塊化程序設(shè)計方法。lWirth N的觀點:算法 + 數(shù)據(jù)結(jié)構(gòu) = 程序l是一種面向過程程序設(shè)計方法,即一個程序是由多個過程(在C+中為函數(shù))模塊組成,過程之間通過函數(shù)參
2、數(shù)和全局變量進行相互聯(lián)系。l與非結(jié)構(gòu)化程序相比,結(jié)構(gòu)化程序在調(diào)試、可讀性和可維護性等方面都有很大的改進。l代碼重用性不高:以過程為中心設(shè)計新系統(tǒng),除了一些標準函數(shù),大部分代碼都必須重新編寫。l由于軟、硬件技術(shù)的不斷發(fā)展和用戶需求的變化,按照功能劃分設(shè)計的系統(tǒng)模塊容易發(fā)生變化,使得開發(fā)出來的模塊的可維護性欠佳。l面向過程模式將數(shù)據(jù)與過程分離,若對某一數(shù)據(jù)結(jié)構(gòu)做了修改,所有處理數(shù)據(jù)的過程都必須重新修訂,這樣就增加了很多的編程工作量。什么是對象:什么是對象:l現(xiàn)實世界是由各種各樣的事物組成,包括真實的事物和抽象的事物。例如,人、動物、汽車(真實的事物)和程序、直線(抽象的事物)等。l每一類事物都有自
3、己特定的屬性(如大小、形狀、重量等)和行為(如生長、行走、轉(zhuǎn)彎、運算等),人們通過研究事物的屬性和行為而認識事物。l在計算機科學(xué)中將這些現(xiàn)實世界中的事物稱之為對對象象。對象是包含現(xiàn)實世界中事物特征的抽象實體,它反映了系統(tǒng)為之保存信息和與之交互的方法。l在程序設(shè)計領(lǐng)域,可以用如下公式表示: 對象 = 數(shù)據(jù) + 作用于這些數(shù)據(jù)上的操作l為了描述屬性和行為相同的一類對象,引入了類(class)的概念。l類是具有相同數(shù)據(jù)結(jié)構(gòu)(屬性)和相同操作功能(行為)的對象的集合,它規(guī)定了這些對象的公共屬性和行為方法。l對象是類的一個實例,例如,汽車是一個類,而行駛在公路上的一輛汽車則是一個對象。l對象和類的關(guān)系相
4、當于程序設(shè)計語言中變量和變量類型的關(guān)系。lOOP圍繞現(xiàn)實世界的概念來組織模塊,采用對象描述問題空間的實體,用程序代碼模擬現(xiàn)實世界中的對象,使程序設(shè)計過程更自然、更直觀。lSP是以功能為中心來描述系統(tǒng),而OOP是以數(shù)據(jù)為中心來描述系統(tǒng)。相對于功能而言,數(shù)據(jù)具有更強的穩(wěn)定性。lOOP模擬了對象之間的通信。就象人們之間互通信息一樣,對象之間也可以通過消息進行通信。這樣,我們不必知道一個對象是怎樣實現(xiàn)其行為的,只需通過對象提供的接口進行通信并使用對象所具有的行為功能。lOOP把一個復(fù)雜的問題分解成多個能夠完成獨立功能的對象(類),然后把這些對象組合起來去完成這個復(fù)雜的問題。l一個對象可由多個更小的對象
5、組成,如汽車由發(fā)動機、傳送系統(tǒng)和排氣系統(tǒng)等組成。這些對象(類)可由不同的程序員來設(shè)計,可在不同程序中使用,就象一個汽車制造商使用許多零部件去組裝一輛汽車,而這些零部件可能不是自己生產(chǎn)的。l采用面向?qū)ο竽J骄拖笤诹魉€上工作,我們最終只需將多個零部件(已設(shè)計好的對象)按照一定關(guān)系組合成一個完整的系統(tǒng)。class Time private: int hour;/ 數(shù)據(jù)成員,表示小時 int minute;/ 數(shù)據(jù)成員,表示分鐘 int second;/ 數(shù)據(jù)成員,表示秒public: void setTime(int h, int m, int s) / 成員函數(shù),設(shè)置時間 hour=(h=0 &
6、amp; h=0 & m=0 & s60) ? s:0; void showTime()/ 成員函數(shù),輸出時間 couthour:minute:secondendl; ;main() Time EndTime;/ 聲明對象EndTime / 設(shè)置對象EndTime的時間(屬性,數(shù)據(jù)成員) EndTime.setTime(12, 23, 36); coutThe time is:; / 顯示對象EndTime的時間 EndTime.showTime();運行結(jié)果:運行結(jié)果:The time is:12 : 23 : 36面向?qū)ο蟪绦蛟O(shè)計方法的基本特征面向?qū)ο蟪绦蛟O(shè)計方法的基本特征
7、面向?qū)ο蟪绦蛟O(shè)計方法具有四個基本特征:l抽象l封裝l繼承l(wèi)多態(tài)性1. 抽象抽象 抽象是人類認識問題的最基本手段之一。抽象是指對具體問題(對象)進行概括,抽出一類類對象的公共屬性和行為并加以描述的過程。抽象包括數(shù)據(jù)抽象和代碼抽象(或行為抽象)。2. 封裝封裝 封裝是把每個對象的數(shù)據(jù)(屬性)和操作(行為)包裝在一個類中。一旦定義了對象的屬性和行為,則必須決定哪些屬性和行為只用于表示內(nèi)部狀態(tài),哪些屬性和行為在外部是可見的。 一般限制直接訪問對象的屬性,而應(yīng)通過操作接口訪問,這樣使程序中模塊之間關(guān)系更簡單、數(shù)據(jù)更安全。對程序的修改也僅限于類的內(nèi)部,使得由于修改程序所帶來的影響局部化。3. 繼承繼承 繼
8、承是指一個新類可以從現(xiàn)有的類派生而來。新類繼承了現(xiàn)有類的特性,包括一些屬性和行為,并且可以修改或增加新的屬性和行為,使之適合具體的需要。 例如,所有的Windows應(yīng)用程序都有一個窗口,它們可以看作都是從一個窗口類派生出來的,但有的應(yīng)用程序用于文字處理,有的應(yīng)用程序用于繪圖,這是由于派生出了不同的類,它們增加了不同的屬性和行為。 繼承很好地解決了軟件的可重用性問題。4. 多態(tài)性多態(tài)性 多態(tài)性是指類中具有相似功能的不同函數(shù)使用同一個名稱來實現(xiàn),并允許不同類的對象對同一消息作出的響應(yīng)不相同。 例如,同樣的“編輯|粘貼”操作,在字處理程序和繪圖程序中有不同的結(jié)果;同樣的加法,把兩個時間值相加和把兩個
9、整數(shù)相加的要求肯定不同。 多態(tài)性使程序設(shè)計靈活、抽象,具有行為共享和代碼共享的優(yōu)點,很好地解決了程序的函數(shù)同名問題。l為了支持面向?qū)ο蟪绦蛟O(shè)計,C+在C語言結(jié)構(gòu)(struct)數(shù)據(jù)類型的基礎(chǔ)上引入了類類這種抽象數(shù)據(jù)類型。lC+面向?qū)ο缶幊虒嵸|(zhì)上就是面向類類編程,只有定義和實現(xiàn)了類,才能聲明屬于這個類的對象,才能通過對象使用定義的成員。l傳統(tǒng)C程序員把編程重點放在函數(shù)的編寫上,而C+程序員把重點放在類類的定義和實現(xiàn)上。lC+類將對象的屬性抽象為數(shù)據(jù)成員,將對象的行為抽象為成員函數(shù),并對它們進行封裝。數(shù)據(jù)成員又稱成員變量,成員函數(shù)又稱為方法。lC+類在形式上類似于C語言中用戶自定義的結(jié)構(gòu)類型,但定
10、義類時規(guī)定了成員的訪問控制權(quán)限。對象只能訪問所屬類的公有成員,而類的私有成員只能在類的成員函數(shù)中被訪問。 C+類定義的基本形式class private:;public:;protected:;l類的定義由關(guān)鍵字class開始,其后為用戶定義的類名,花括號括起來的部分稱為類體。l關(guān)鍵字private、public和protected稱為訪問權(quán)限控制符,用來設(shè)置數(shù)據(jù)成員和成員函數(shù)的訪問屬性,其默認值為private。lprivate屬性表示數(shù)據(jù)成員和成員函數(shù)是類的私有成員,它們只允許被本類的成員函數(shù)訪問或調(diào)用,數(shù)據(jù)成員一般定義為private屬性;lpublic屬性表示數(shù)據(jù)成員和成員函數(shù)是類的公
11、有成員,它們允許被本類或其它類的成員函數(shù)(通過對象)訪問或調(diào)用,是類的外部接口,成員函數(shù)一般定義為public屬性;lprotected屬性表示數(shù)據(jù)成員和成員函數(shù)是類的保護成員,它們允許被本類的成員函數(shù)和派生類的成員函數(shù)訪問或調(diào)用。例:例:class Timeprivate:/ 最好不要省略private int hour;/ 數(shù)據(jù)成員,表示小時 int minute;/ 數(shù)據(jù)成員,表示分鐘 int second;/ 數(shù)據(jù)成員,表示秒public: void setTime(int, int, int); / 成員函數(shù),設(shè)置時間 void showTime();/ 成員函數(shù),輸出時間;私有數(shù)據(jù)
12、成員hour、minute和second只能在類的成員函數(shù)中被訪問或賦值;公有成員函數(shù)setTime、showTime可在外部被調(diào)用,但必須通過一個對象作為對象的成員使用。l利用C+類進行面向?qū)ο缶幊蹋x類的成員只是完成了工作的第一步,最重要的工作是實現(xiàn)定義的類。l類的實現(xiàn)實質(zhì)上是類的成員函數(shù)的實現(xiàn),即定義類的成員函數(shù)。l成員函數(shù)的定義形式與一般函數(shù)的定義形式基本相同,但必須在成員函數(shù)名前加上類名和作用域限定符(:)。l成員函數(shù)的定義也可放在類體內(nèi)(該函數(shù)聲明之處),這時成員函數(shù)將變成內(nèi)聯(lián)函數(shù)。 例:例:void Time:setTime(int h, int m, int s) hour=
13、(h=0 & h=0 & m=0 & s60) ? s:0;void Time:showTime() couthour:minute:second”訪問對象的公有成員,但不能訪問對象的私有成員。 例如,公有成員函數(shù)調(diào)用:t1.setTime();start.showTime();pt1-setTime(); 而任何形如t1.hour、t1.minute、start.second等私有成員變量的直接訪問都是非法的。main() Time EndTime;/ 聲明對象EndTime EndTime.setTime(12, 23, 36);/ 設(shè)置對象EndTime的時間 c
14、outThe time is:; EndTime.showTime();/ 顯示對象EndTime的時間l在定義類時不能對成員變量進行初始化,因為無法確定成員變量屬于哪一個對象。l成員變量一般都定義為私有屬性,也不能在聲明對象后利用賦值運算對成員變量進行初始化。l成員變量的初始化一般是利用一個名為構(gòu)造函數(shù)構(gòu)造函數(shù)的成員函數(shù)來完成。如何進行成員變量的初始化? 構(gòu)造函數(shù)是一種特殊的成員函數(shù),它是在創(chuàng)建對象時(聲明或new動態(tài)創(chuàng)建)系統(tǒng)自動調(diào)用的成員函數(shù)。 析構(gòu)函數(shù)也是一種特殊的成員函數(shù),它是在對象生存期結(jié)束時系統(tǒng)自動調(diào)用的成員函數(shù)。 構(gòu)造函數(shù)的名稱與類名相同,析構(gòu)函數(shù)的名稱必須在類名前加上“”符號
15、。注意,構(gòu)造函數(shù)和析構(gòu)函數(shù)不能指定任何返回值類型,包括void返回類型。#include class Timeprivate: int hour; int minute; int second;public: Time(int, int, int);/ 構(gòu)造函數(shù) Time();/ 析構(gòu)函數(shù) . . . . . . ; Time:Time(int h, int m, int s) hour=h;/ 對私有成員變量初始化 minute=m; second=s; coutThe constructor be called: hour: minute:secondendl; 功能與成員函數(shù)Time:s
16、etTime()類似Time:Time() coutThe destructor be called: hour: minute:secondendl;void main(void) Time t1(10, 35, 55) ; / 自動調(diào)用構(gòu)造函數(shù)自動調(diào)用構(gòu)造函數(shù) Time t2(16, 53, 9) ; / 自動調(diào)用構(gòu)造函數(shù)自動調(diào)用構(gòu)造函數(shù)/ 退出退出main()主函數(shù)時自動調(diào)用析構(gòu)函數(shù)主函數(shù)時自動調(diào)用析構(gòu)函數(shù)程序運行結(jié)果為:程序運行結(jié)果為: The constructor be called:10:35:55The constructor be called:16:53:9The dest
17、ructor be called:16:53:9The destructor be called:10:35:55為什么是這個結(jié)果? 當創(chuàng)建一個對象時,系統(tǒng)先根據(jù)類定義的成員變量為對象分配內(nèi)存空間,然后自動調(diào)用對象的構(gòu)造函數(shù)對這段內(nèi)存空間進行初始化處理,從而完成對象的初始化。 當撤消一個對象時,系統(tǒng)先自動調(diào)用對象的析構(gòu)函數(shù),然后釋放對象所占內(nèi)存空間。 從程序的運行結(jié)果可以看出,析構(gòu)函數(shù)的調(diào)用順序一般與構(gòu)造函數(shù)的調(diào)用順序相反。 棧棧:后進先出表l與一般數(shù)據(jù)類型的變量相比,對象在它的生存期會有大量的操作,有時這些操作的結(jié)果必須在對象的生存期結(jié)束時加以清理。因此可以在析構(gòu)函數(shù)中進行動態(tài)分配的內(nèi)存清理
18、工作。l如果定義類時沒有提供構(gòu)造函數(shù)和析構(gòu)函數(shù),編譯系統(tǒng)將會自動為類分別添加一個缺省的構(gòu)造函數(shù)和析構(gòu)函數(shù)。如果用戶加上自定義的構(gòu)造函數(shù)和析構(gòu)函數(shù),編譯系統(tǒng)將不會再添加缺省的構(gòu)造函數(shù)和析構(gòu)函數(shù)。l若構(gòu)造函數(shù)無參數(shù),則聲明對象時也不能給出參數(shù)。lthis指針是一個特殊的隱藏在對象中的指針,每一個處于生存期的對象都有一個this指針,用于指向?qū)ο蟊旧?。l當類的某個非靜態(tài)成員函數(shù)被調(diào)用時,系統(tǒng)通過this指針確定是哪一個對象的該成員函數(shù)被調(diào)用。實際上,this指針總是作為一個隱含參數(shù)傳遞給類的每一個成員函數(shù)。 例:例:void Time:showTime() couthour:minute:secon
19、dendl;void Time:showTime(Time* * this); couthour:minute: secondendl; 當程序中調(diào)用某個成員函數(shù)時,編譯器會把該對象的地址賦值給this指針,并將該地址值加入到參數(shù)表中,如下所示:EndTime.showTime(&EndTime); 在一個成員函數(shù)中經(jīng)常需要調(diào)用其它函數(shù)(非本類的成員函數(shù)),而有時需要把對象本身(即對象的地址)作為參數(shù)傳遞給被調(diào)用函數(shù),這時必須使用this指針。 例:例:#include #include class Personpublic: / 可在外部直接訪問public屬性的數(shù)據(jù)成員 char
20、m_strName20; char m_ID18;public: Person(char* strName, char* ID) / 內(nèi)聯(lián)構(gòu)造函數(shù) strcpy(m_strName, strName); strcpy(m_ID, ID); void Show();void Display(Person* pObj)/ 非成員函數(shù) coutName:m_strNameendlID:m_IDShow(); / 通過調(diào)用Show調(diào)用Display靜態(tài)成員的概念:靜態(tài)成員的概念: 一般情況下,同一個類不同對象的數(shù)據(jù)成員所占用的內(nèi)存空間是不同的(體現(xiàn)了不同對象具有不同的屬性值)。在有些情況下,類的數(shù)據(jù)成
21、員的值對每個對象都是相同的,如當前已創(chuàng)建對象的數(shù)量,這時可以將該數(shù)據(jù)成員聲明為靜態(tài)數(shù)據(jù)成員(占有相同的存儲單元)。靜態(tài)成員的聲明:靜態(tài)成員的聲明: 在聲明成員時以關(guān)鍵字static開頭,例如: public: static int m_nCount;l靜態(tài)成員分為靜態(tài)數(shù)據(jù)成員和靜態(tài)成員函數(shù)。l靜態(tài)數(shù)據(jù)成員類似于一般的static靜態(tài)變量,它具有全局性。靜態(tài)數(shù)據(jù)成員屬于整個類,為類的所有對象共享。l無論類的對象有多少,類的靜態(tài)數(shù)據(jù)成員只有一份,存儲在同一個內(nèi)存空間。即使沒有創(chuàng)建類的一個對象,類的靜態(tài)數(shù)據(jù)成員也是存在的。l使用靜態(tài)數(shù)據(jù)成員保證了該數(shù)據(jù)成員值的唯一性。靜態(tài)成員的初始化:靜態(tài)成員的初始
22、化:放在類定義的外部 int Person : m_nCount=0; l公有靜態(tài)成員:三種方式(1)通過對象訪問,如: person1.m_nCount=100;(2)利用類名和作用域限定符(:)訪問,如:int Person:m_nCount=100; / 初始化(3)在成員函數(shù)中訪問,如:m_nCount+;l私有和保護靜態(tài)成員:只能在成員函數(shù)中訪問l成員函數(shù)也可以是靜態(tài)的,其聲明方式與靜態(tài)成員變量類似。如:public: static int GetCount();/ 獲取靜態(tài)數(shù)據(jù)成員l靜態(tài)成員函數(shù)也與一個類相關(guān)聯(lián),而不只與一個特定的對象相關(guān)聯(lián)。l區(qū)別非靜態(tài)成員函數(shù),靜態(tài)成員函數(shù)沒有t
23、his指針,因為類的靜態(tài)成員函數(shù)只有一個運行實例。l成員函數(shù)一般是公有屬性,可以通過對象、類名和作用域限定符、在成員函數(shù)中三種方式調(diào)用靜態(tài)成員函數(shù)。 靜態(tài)成員函數(shù)只能訪問類的靜態(tài)成員(成員變量和成員函數(shù)),而不能訪問類的非靜態(tài)成員。因為當通過類名和運算符“:”調(diào)用一個靜態(tài)成員函數(shù)時,不能確定函數(shù)中所訪問的非靜態(tài)成員屬于哪一個對象。 解決方法:解決方法: 將對象作為靜態(tài)成員函數(shù)的參數(shù),然后在靜態(tài)成員函數(shù)中通過對象訪問它的非靜態(tài)成員。注注 意意例例#include #include class Personpublic: char m_strName20; long m_ID; static in
24、t m_nCount; / 靜態(tài)成員變量,表示已創(chuàng)建對象的數(shù)量public: Person(char*, long);/ 構(gòu)造函數(shù) static int GetCount();/ 靜態(tài)成員函數(shù) static long GetID(Person);/ 對象作為靜態(tài)成員函數(shù)的參數(shù);Person:Person(char* strName, long ID) strcpy(m_strName, strName); m_ID=ID; m_nCount+;/ 對象數(shù)目加1int Person:GetCount() return m_nCount;/ 訪問靜態(tài)成員變量long Person:GetID(Pe
25、rson x) return x.m_ID; / 不能直接訪問非靜態(tài)成員m_IDint Person:m_nCount=0;/ 初始化靜態(tài)成員變量void main() Person e1(LiuJun,1101051); coutPerson:m_nCount , e1.m_nCount n; / 通過類或?qū)ο笤L問靜態(tài)成員變量 coutPerson:GetCount() , ” Person:GetID(e1)n; / 通過類調(diào)用靜態(tài)成員函數(shù) coute1.GetCount() , e1.GetID(e1)n; / 通過對象調(diào)用靜態(tài)成員函數(shù) Person e2(WangXiaogang,11
26、01058); coutPerson:GetCount() , ” Person:GetID(e2)n; coute2.GetCount() , e2.GetID(e2)n; coute1.GetCount() , e1.GetID(e1)n; / e1和e2共享靜態(tài)成員變量m_nCount程序運行結(jié)果為:程序運行結(jié)果為: 1,11,11010511,11010512,11010582,11010582,1101051 類具有封裝性,類的私有成員一般只能通過該類的成員函數(shù)訪問,這種封裝性隱藏了對象的數(shù)據(jù)成員,保證了對象的安全,但有時帶來了編程的不方便。友元函數(shù):友元函數(shù): C+提供了一種函數(shù),
27、它雖然不是一個類的成員函數(shù),但可以象成員函數(shù)一樣訪問該類的所有成員,包括私有成員和保護成員。這種函數(shù)稱為友元(friend)函數(shù)。 一個函數(shù)要成為一個類的友員函數(shù),需要在類的定義中聲明該函數(shù),并在函數(shù)聲明的前面加上關(guān)鍵字friend。 友元函數(shù)本身的定義沒有什么特殊要求,可以是一般函數(shù),也可以是另一個類的成員函數(shù)。 為了能夠在友元函數(shù)中訪問并設(shè)置類的私有數(shù)據(jù)成員,一個類的友元函數(shù)一般將該類的引用作為函數(shù)參數(shù)。 例例class A friend void display(A);/ 友元函數(shù)是一個一般函數(shù) friend void B:BMemberFun(A&); / 友元函數(shù)是另一個類B
28、的成員函數(shù)public:. . . 友元類:友元類: 友元的另一種類型是友元類,一個類可以聲明另一個類為其友元類,這個友元類的所有成員函數(shù)都可以訪問聲明其為友元的類的所有成員。 由于訪問權(quán)限控制符不影響友元聲明,友元聲明可放在類體中任何地方,建議把友元聲明放在類體的開始位置。例例 友元(一般友元函數(shù)、友元成員函數(shù)和友元類)的聲明和使用。 P78-79,例3-7。說明:說明: l友元關(guān)系是單方向的,不具有交換性和傳遞性。l使用友元雖然簡化了編程,并可避免調(diào)用成員函數(shù)的開銷,但破壞了類的封裝性,建議謹慎使用。l繼承繼承是面向?qū)ο蟪绦蛟O(shè)計方法的四個基本特征之一,是程序代碼可重用性的具體體現(xiàn)。l在C+
29、面向?qū)ο蟪绦蛟O(shè)計中,所謂類的繼承就是利用現(xiàn)有的類創(chuàng)建一個新的類。新類繼承了現(xiàn)有類的屬性和行為。l為了使新類具有自己所需的功能,它可以擴充和完善現(xiàn)有類的屬性和行為,使之更具體。l微軟基礎(chǔ)類MFC就是通過類的繼承來體現(xiàn)類的可重用性和可擴充性。 繼承繼承發(fā)揚發(fā)揚l在現(xiàn)實世界中,一類事物的對象常常也屬于另一類事物。l在面向?qū)ο蟪绦蛟O(shè)計方法中,一個類的對象也常常是另一個類的對象,即一個類具有了另一個類的屬性和方法。l在定義一個類時,根據(jù)類的繼承性,我們能夠且應(yīng)盡可能地利用現(xiàn)有的類來定制新的類,而不必重新設(shè)計新的類。 在繼承關(guān)系中,新定義的類稱為被繼承類的派派生類生類或子類子類,而被繼承的類稱為新定義類的
30、基類基類或父類父類。派生類繼承了基類的所有成員。 一個派生類也可以作為另一個派生類的基類。class : . . . / 派生類新增加的成員聲明列表;l派生方式?jīng)Q定了基類的成員在派生類中的訪問權(quán)限。派生方式共有三種:public、private和protected(缺省值為private)。l雖然派生類繼承了基類的所有成員,但為了不破壞基類的封裝性,無論采用哪種派生方式,基類的私有成員在派生類中都是不可見的,即不允許在派生類的成員函數(shù)中訪問基類的私有成員。l采用public派生,基類成員的訪問權(quán)限在派生類中保持不變,即基類所有的公有或保護成員在派生類中仍為公有或保護成員。public派生最常用
31、。 (1) 可以在派生類的成員函數(shù)中訪問基類的非私有成員; (2) 可通過派生類的對象直接訪問基類的公有成員。l采用private私有派生,基類所有的公有和保護成員在派生類中都成為私有成員,只允許在派生類的成員函數(shù)中訪問基類的非私有成員。private派生很少使用。l采用protected保護派生,基類所有的公有和保護成員在派生類中都成為保護成員,只允許在派生類的成員函數(shù)和該派生類的派生類的成員函數(shù)中訪問基類的非私有成員。 #include class Point/ 定義基類,表示點private: int x; int y;public: void setPoint(int a, int b
32、) x=a; y=b; ; / 設(shè)置坐標 int getX() return x; ;/ 取得X坐標 int getY() return y; ;/ 取得Y坐標;class Circle : public Point / 定義派生類,表示圓private: int radius;public: void setRadius(int r) radius=r; ; / 設(shè)置半徑 int getRadius() return radius; ; / 取得半徑 int getUpperLeftX() return getX()radius; ;/ 取得外接正方形左上角的X坐標 int getUpper
33、LeftY() return getY() + radius; ;/ 取得外接正方形左上角的Y坐標;main() Circle c; c.setPoint(200, 250); c.setRadius(100); coutX=c.getX(), Y=c.getY() , Radius=c.getRadius()endl; coutUpperLeft X=c.getUpperLeftX() , UpperLeft Y=c.getUpperLeftY()endl; 公有派生類的對象可以直接訪問基類Point的公有成員l派生類Circle通過public派生方式繼承了基類Point的所有成員(除私有
34、成員外所有成員的訪問權(quán)限不變),同時還定義了自己的成員變量和成員函數(shù)。l若將類Circle的派生方式改為private或protected,則下述語句是非法的:c.setPoint(200, 250); 容易混淆l無論哪種派生方式,派生類都繼承了基類的所有成員,包括私有成員。我們雖然不能在派生類Circle中直接訪問私有數(shù)據(jù)成員x和y,但可以通過繼承的公有成員函數(shù)getX()、getY()和setPoint()訪問或設(shè)置它們。 利用類繼承定義類可能帶來一個問題問題:派生類會繼承它不需要的基類中的數(shù)據(jù)成員和成員函數(shù),這時,基類中不適合于派生類的成員可以在派生類中重新加以定義。 #include
35、class Apublic:void Show( ) coutA:Shown; ;class B : public Apublic: void Show( ) coutB:Shown; ;/ 在派生類中重新定義成員函數(shù) void Display() Show( ); ;/ 調(diào)用派生類B的成員函數(shù)Show(); void main() A a; B b; a.Show();/ 調(diào)用基類A的成員函數(shù)Show() b.Show();/ 調(diào)用派生類B的成員函數(shù)Show() b.Display();如果想調(diào)用基類A的成員函數(shù)Show(),可以使用作用域限 定 符 “ : : ” : A : : Show
36、(); 從本例可以看出,雖然派生類繼承了基類的所有成員函數(shù),但如果派生類某個成員函數(shù)的名稱和參數(shù)與基類成員函數(shù)一致(即在派生類中對該成員函數(shù)重新進行了定義),則在派生類中調(diào)用的成員函數(shù)是派生類的成員函數(shù)。 請問:如果在派生類B中沒有對成員函數(shù)Show()重新進行定義,程序運行結(jié)果如何? 程序運行結(jié)果:程序運行結(jié)果:A:ShowB:ShowB:Show 為什么我們經(jīng)常在現(xiàn)有類的基礎(chǔ)上采用繼承的方法來定制新類,而不通過直接修改現(xiàn)有類來設(shè)計自己的類?除了代碼重用的優(yōu)越性,其主要原因是可能得不到基類的實現(xiàn)源碼。繼承的重要性! 在利用微軟基礎(chǔ)類MFC派生自己的類時,我們只需要MFC類聲明的頭文件(利用#
37、include指令將頭文件包含)和含有成員函數(shù)目標代碼的OBJ文件,并不需要整個MFC類庫的實現(xiàn)源碼。 l一個派生類對象也屬于其基類,因此當程序創(chuàng)建一個派生類對象時,系統(tǒng)首先自動創(chuàng)建一個基類對象。l在調(diào)用派生類的構(gòu)造函數(shù)構(gòu)建派生類對象時,系統(tǒng)首先調(diào)用基類的構(gòu)造函數(shù)構(gòu)建基類對象。當派生類對象的生存期結(jié)束時,首先調(diào)用派生類的析構(gòu)函數(shù),然后調(diào)用基類的析構(gòu)函數(shù)。 編譯器在對程序編譯時,首先生成基類構(gòu)造函數(shù)的調(diào)用代碼,然后生成派生類構(gòu)造函數(shù)的調(diào)用代碼。 隱式調(diào)用和顯式調(diào)用兩種方式: 注意:注意:除非基類有默認的構(gòu)造函數(shù),否則必須采用顯式調(diào)用方式。(1)隱式方式隱式方式是指在派生類的構(gòu)造函數(shù)中不指定對應(yīng)的
38、基類的構(gòu)造函數(shù),調(diào)用的是基類的默認構(gòu)造函數(shù)(即含有缺省參數(shù)值或不帶參數(shù)的構(gòu)造函數(shù))。 (2)顯式方式顯式方式是指在派生類的構(gòu)造函數(shù)中指定要調(diào)用的基類構(gòu)造函數(shù),并將派生類構(gòu)造函數(shù)的部分參數(shù)值傳遞給基類構(gòu)造函數(shù)。 設(shè)類B是類A的派生類,則派生類B顯式方式構(gòu)造函數(shù)的定義形式如下: B:B( ) : A( ). . . / 類B構(gòu)造函數(shù)的實現(xiàn)代碼 形參聲明中的部分參數(shù),傳遞給基類構(gòu)造函數(shù) 派生類構(gòu)造函數(shù)形參的名稱和類型 l派生類構(gòu)造函數(shù)既初始化派生類的數(shù)據(jù)成員,又通過基類構(gòu)造函數(shù)初始化其基類的數(shù)據(jù)成員。l參數(shù)表中參數(shù)的個數(shù)和類型要與基類某個構(gòu)造函數(shù)的形參聲明一致。 PointCircle Cylind
39、er 注意:注意: 當基類有多個構(gòu)造函數(shù)時,編譯器根據(jù)派生類構(gòu)造函數(shù)為基類構(gòu)造函數(shù)提供的參數(shù)表來確定調(diào)用基類的哪一個構(gòu)造函數(shù)。例例 首先定義類Point,然后定義類Point的派生類Circle,再定義類Circle的派生類Cylinder。 (x, y)(x, y)r(x, y)hr#include class Point/ 定義基類Pointprotected: int x, y;public: Point(int a=0, int b=0) / 含有缺省參數(shù)值的構(gòu)造函數(shù)也是默認的構(gòu)造函數(shù)x=a; y=b;coutPoint constructor:x,yendl; ; Point() c
40、outPoint destructor:x,yendl; ;class Circle : public Point/ 定義類Point的派生類protected: int radius;public:/ 顯式調(diào)用基類的構(gòu)造函數(shù) Circle(int a=0, int b=0, int r=0) : Point(a, b) radius=r;coutCircle constructor:radiusx,yendl; ; Circle() coutCircle destructor:radiusx,yendl; ;class Cylinder : public Circle/ 定義類Circle的
41、派生類protected: int height;public: / 顯式調(diào)用基類的構(gòu)造函數(shù) Cylinder(int a=0, int b=0, int r=0, int h=0) : Circle(a, b, r) height=h; coutCylinder constructor:heightradiusx,yendl; ; Cylinder() coutCylinder destructor:heightradiusx,yendl; ;main() Cylinder cylinder(200, 300, 100, 400); / 調(diào)用了類Point、Circle和Cylinder的構(gòu)
42、造函數(shù)Point constructor:200,300Circle constructor:100 200,300Cylinder constructor:400 100 200,300Cylinder destructor:400 100 200,300Circle destructor:100 200,300Point destructor:200,300程序運行結(jié)果:程序運行結(jié)果:構(gòu)造函數(shù)的執(zhí)行順序:構(gòu)造函數(shù)的執(zhí)行順序:析構(gòu)函數(shù)的執(zhí)行順序:析構(gòu)函數(shù)的執(zhí)行順序:Point( )Circle( )Cylinder( )當聲明Cylinder對象時 Cylinder( ) Circle( )
43、 Point( )當程序結(jié)束時class Aclass Bclass Cclass Aclass Bclass C每個派生類只有一個直接基類 單繼承一個派生類同時從多個基類派生而來,即有多個直接基類 多重繼承 設(shè)類B是類A1、A2、An的派生類,多重繼承的派生類的定義形式為: class : , , , . . . / 派生類新增加的成員聲明列表 ;多 重 繼 承 的 派 生 方 式 也 有private、public和protected三種,各基類的派生方式可以不同 class BaseA / 定義基類protected: int a;public: void setA(int);class
44、 BaseB / 定義基類protected: int b;public: void setB(int); 定義兩個基類void BaseA:setA(int x) a=x;void BaseB:setB(int x) b=x;int MultiDerived:getAB() return a+b;可以直接訪問基類中protected屬性成員成員函數(shù)的實現(xiàn)程序運行結(jié)果:a+b=100二義性錯誤:二義性錯誤:編譯器無法確定數(shù)據(jù)成員a是哪一個副本 class Bclass Cclass Dclass ADBACA派生類D的對象中存在間接基類A的兩份副本 利用作用域限定符(:)把基類的成員與下一層基
45、類關(guān)聯(lián)起來: d1.B:a=100;或: d1.C:a=100 從路徑DBA繼承而來 從路徑DCA繼承而來 缺點:缺點: 浪費了存儲空間; 在訪問基類的成員時,要求指明訪問路徑。 大部分情況下不需要保存基類多個相同的副本。 虛基類并不是一種新的類型的類,而是一種派生方式。采用虛基類方式定義派生類,在創(chuàng)建派生類的對象時,類層次結(jié)構(gòu)中虛基類的成員只出現(xiàn)一次,即基類的一個副本被所有派生類對象所共享。 class Bclass Cclass Dclass AD A B C主函數(shù)中:d1.a=100; 采用虛基類方式定義派生類的方法是在基類的前面加上關(guān)鍵字virtual,而定義基類時與一般基類完全一樣。
46、 節(jié)約內(nèi)存空間; 避免在多重派生類中類成員的不明確性。 下節(jié)講授的內(nèi)容 聲明一個派生類的對象的同時也自動聲明了一個基類的對象。 3.3小節(jié)內(nèi)容派生類的對象可以認為是其基類的對象。C+允許一個基類對象的指針指向其派生類的對象 這是實現(xiàn)虛函數(shù)的關(guān)鍵不允許派生類對象的指針指向其基類的對象。即使將一個基類對象的指針指向其派生類的對象,通過該指針也只能訪問派生類中從基類繼承的公有成員,不能訪問派生類自定義的成員,除非通過強制類型轉(zhuǎn)換將基類指針轉(zhuǎn)換為派生類指針。 例例 程序運行結(jié)果為:程序運行結(jié)果為:a=100b=200 pb=&a pa-setB()pa-showB() class Apubli
47、c: void Show( ) coutShow();class B : public Apublic: void Show( ) coutB:Shown; ; 調(diào)用哪一個Show()如果想通過基類指針調(diào)用派生類中覆蓋的成員函數(shù),只有使用虛函數(shù)虛函數(shù)。 要將一個成員函數(shù)聲明為虛函數(shù),只需在定義基類時在成員函數(shù)聲明的開始位置加上關(guān)鍵字virtual。class Apublic: virtual void Show() coutA:shown; ;class B : public Apublic: void Show() coutDraw()”可能是繪制矩形,也可能是繪制三角形或圓。具體繪制什么圖
48、形,取決于pShape所指的對象。即將函數(shù)調(diào)用語句與函數(shù)代碼相關(guān)聯(lián)。兩種聯(lián)編方式:靜態(tài)聯(lián)編和動態(tài)聯(lián)編。靜態(tài)聯(lián)編是指編譯器在編譯階段就確定了要調(diào)用的函數(shù),即早期綁定。動態(tài)聯(lián)編是指在程序執(zhí)行過程中根據(jù)具體情況再確定要調(diào)用的函數(shù),即后期綁定。重載重載采用靜態(tài)聯(lián)編方式:雖然函數(shù)名相同,但編譯器能夠根據(jù)函數(shù)參數(shù)類型的不同確定要調(diào)用的函數(shù)。重載體現(xiàn)出一種靜態(tài)多態(tài)性或編譯時多態(tài)性。當通過基類指針調(diào)用虛函數(shù)虛函數(shù)時,C+采用動態(tài)聯(lián)編方式。虛函數(shù)體現(xiàn)出一種動態(tài)多態(tài)性或運行時多態(tài)性。 基于構(gòu)造函數(shù)的特點,不能將構(gòu)造函數(shù)定義為虛函數(shù)。聲明派生類對象時自動調(diào)用基類的構(gòu)造函數(shù)例例 l當撤消派生類的對象時,先調(diào)用派生類析
49、構(gòu)函數(shù),然后自動調(diào)用基類析構(gòu)函數(shù),如此看來析構(gòu)函數(shù)沒必要定義為虛函數(shù)。但是,假如使用基類指針指向其派生類的對象,而這個派生類對象是用new運算創(chuàng)建的。當程序使用delete運算撤消派生類對象時,這時只調(diào)用了基類的析構(gòu)函數(shù),而沒有調(diào)用派生類的析構(gòu)函數(shù)。l如果使用虛析構(gòu)函數(shù),無論指針所指的對象是基類對象還是派生類對象,程序執(zhí)行時都會調(diào)用對應(yīng)的析構(gòu)函數(shù)。 class Apublic:A() ;/ 構(gòu)造函數(shù)不能是虛函數(shù)virtual A() coutA:destructorn; ; / 析構(gòu)函數(shù)是虛函數(shù);class B : public Apublic:B() ;B() coutB:destructo
50、rn; ; / 虛析構(gòu)函數(shù);void main()A *pA=new B;/ . . . . . . delete pA;/ 先調(diào)用派生類B的構(gòu)造函數(shù),再調(diào)用基類A的構(gòu)造函數(shù) 程序運行結(jié)果:程序運行結(jié)果: B:destructor A:destructor 總結(jié)總結(jié):由于使用了虛析構(gòu)函數(shù),當撤消pA所指派生類B的對象時,首先調(diào)用派生類B的析構(gòu)函數(shù),然后再調(diào)用基類A的析構(gòu)函數(shù)。如果析構(gòu)函數(shù)不是虛函數(shù),則得不到下面的運行結(jié)果。請讀者思考會是什么結(jié)果 抽象類是抽象類是類的一些行為(成員函數(shù))沒有給出具體定義的類,即純粹的一種抽象。抽象類抽象類只能用于類的繼承,其本身不能用來創(chuàng)建對象,抽象類又稱為抽象
51、基類。抽象基類只提供了一個框架,僅僅起著一個統(tǒng)一接口的作用,而很多具體的功能由派生出來的類去實現(xiàn)。雖然不能聲明抽象類的對象,但可以聲明指向抽象類的指針。 在一般的類庫中都使用了抽象基類,如類CObject就是微軟基礎(chǔ)類庫MFC的抽象基類。不定義具體實現(xiàn)的成員函數(shù)稱為純虛函數(shù)純虛函數(shù)。純虛函數(shù)不能被調(diào)用,僅起提供一個統(tǒng)一接口的作用。純虛函數(shù)的聲明聲明:virtual ()= 0 ;當基類是抽象類時,只有在派生類中重新定義基類中的所有純虛函數(shù),該派生類才不會再成為抽象類。 一個類如果滿足以下兩個條件之一就是抽象類:l至少有一個成員函數(shù)不定義具體的實現(xiàn);l定義了一個protected屬性的構(gòu)造函數(shù)或
52、析構(gòu)函數(shù)。純虛函數(shù)純虛函數(shù)純虛函數(shù)/ 定義抽象基類定義抽象基類class CShapepublic:double r ;double s ;public:CShape(double x) r=x; / 聲明純虛函數(shù) virtual void Area()=0;/ 定義具體的派生類定義具體的派生類class CCircle : public CShapepublic:CCircle(double x):CShape(x) ; / 重新定義虛函數(shù)void Area() s=3.14159*r*r; ;main() CCircle circle(48.52); circle.Area(); cout
53、Area=circle.sendl;3.5 重載重載l重載重載是C+提供的一個新特性。C+重載分為函數(shù)重載和運算符重載,這兩種重載的實質(zhì)是一樣的,因為進行運算可以理解為是調(diào)用一個函數(shù)。l通過使用重載機制,可以對一個函數(shù)名(或運算符)定義多個函數(shù)(或運算功能),只不過要求這些函數(shù)的參數(shù)(或參加運算的操作數(shù))的類型有所不同。l重載使C+程序具有更好的可擴充性。x + yX + Y Add(x, y)Add(x, y, z)l函數(shù)重載:函數(shù)重載:指一組功能類似但函數(shù)參數(shù)類型(個數(shù))不同的函數(shù)可以共用一個函數(shù)名。l當C+編譯器遇到重載函數(shù)的調(diào)用語句時,它能夠根據(jù)不同的參數(shù)類型或不同的參數(shù)個數(shù)選擇一個合
54、適的函數(shù)。int abs(int val) return val0 ? val : val;float abs(float val) return (val0) ? val : val;main() int i=100; coutabs(i)endl; / int型 float f=-125.78F; coutabs(f)endl; / float型 在程序中,求絕對值函數(shù)的名稱相同,但參數(shù)類型不同,這時C+編譯器自動按參數(shù)表的不同來分別聯(lián)編不同的求絕對值函數(shù)。 l不能利用函數(shù)返回類型的不同進行函數(shù)重載。因為在沒有確定調(diào)用的是哪個函數(shù)之前,不知道函數(shù)的返回類型。 long abc(int);
55、float abc(int);l 同樣,不能利用引用進行函數(shù)重載: void fun(int&); void fun(int); 因為對于下面的調(diào)用語句,編譯器無法決定調(diào)用哪一個函數(shù): fun(i);/ i是一個整型變量 從上面可以看出,一般函數(shù)的重載重載使C+程序具有更好的可擴充性。此外,類的成員函數(shù)也可以重載,特別是構(gòu)造函數(shù)的重載給C+程序設(shè)計帶來很大的靈活性。例例 構(gòu)造函數(shù)的重載。class Boxprivate: int height, width, depth;public: Box() height=0; width=0; depth=0; / 避免給成員變量賦不安全的值
56、Box(int ht, int wd, int dp) / 重載構(gòu)造函數(shù) height=ht; width=wd; depth=dp; int Volume() return height*width*depth; ; void main() Box box1; Box box2(10, 15, 20); coutVolume1=box1.Volume() , Volume2=box2.Volume()endl;程序運行結(jié)果:程序運行結(jié)果:Volume1=0,Volume2=3000類Box有兩個構(gòu)造函數(shù)。第一個構(gòu)造函數(shù)不帶參數(shù),把默認值0賦給對象;第二個構(gòu)造函數(shù)使用參數(shù)值初始化創(chuàng)建的對象。
57、l運算符重載:運算符重載:指對于不同數(shù)據(jù)類型的操作數(shù),同一個運算符所代表的運算功能可以不同。l一個運算符定義了一種操作,一個函數(shù)也定義了一種操作,其本質(zhì)是相同的,當程序遇到運算符時會自動調(diào)用相應(yīng)的運算符函數(shù)。l雖然重載運算符完成的功能都能夠用一個真正的成員函數(shù)來實現(xiàn),但使用運算符重載使程序更易于理解。l與函數(shù)重載類似,編譯器是根據(jù)參加運算的操作數(shù)的類型來識別不同的運算。例:例:l對于表達式:10+20 編譯器把它看成如下函數(shù)調(diào)用: int operator+(10, 20);l對于表達式:10.0+20.0 編譯器把它看成如下函數(shù)調(diào)用: float operator+(10.0, 20.0);
58、參加運算的數(shù)是單精度實型數(shù)參加運算的數(shù)是整數(shù) 我們可以將字符串operator+看成一個運算符函數(shù)名,這些同名的運算符函數(shù)根據(jù)不同類型的操作數(shù)完成不同的加法運算。重載運算符的形式:重載運算符的形式: 重載一個運算符,就是編寫一個運算符函數(shù),重載運算符(函數(shù))的原型為: operator();例例 定義復(fù)數(shù)類型,重載運算符“+”。運算結(jié)果的類型要重載的運算符參加運算的操作數(shù)例如:c3=c1+c2class Complexpublic: / 公有成員,以便運算符函數(shù)(非成員函數(shù))訪問 float r;/ 實部 float i;/ 虛部public: Complex(float x=0, float
59、 y=0) r=x; i=y; ;Complex operator+(Complex c1 , Complex c2) Complex temp; temp.r=c1.r+c2.r; temp.i=c1.i+c2.i; return temp;利用普通函數(shù)重載運算符void main() Complex complex1(3.34f, 4.8f), complex2(12.8f, 5.2f); Complex complex; complex=complex1+complex2;/ 進行兩個復(fù)數(shù)的相加運算 coutcomplex.r+complex.iiendl;說明:說明:l本例采用普通函數(shù)
60、的形式重載運算符。l可以采用成員函數(shù)的形式重載運算符。并且如果運算符函數(shù)要求直接訪問類的非公有成員時,運算符函數(shù)不能定義為非成員函數(shù),除非將它聲明為該類的友元函數(shù)。例例 利用成員函數(shù)進行運算符重載。class Complexprivate: / 私有成員能夠在成員函數(shù)(運算符函數(shù))中訪問 float r;/ 實部 float i;/ 虛部public: Complex(float x=0, float y=0) r=x; i=y; ; Complex operator+(Complex); void Display() coutr+iir+other.r; temp.i=this-i+other.i; / 可以省略this指針 return temp;利用成員函數(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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度退股及員工持股計劃合同
- 個人股權(quán)轉(zhuǎn)讓合同2025年度含業(yè)績對賭條款
- 二零二五年度臨時項目經(jīng)理職務(wù)聘用與成果轉(zhuǎn)化合同
- 二零二五年度旅游團隊保險責任免除聲明
- 電線電纜購銷合同
- 管理層勞動合同工資
- 個人數(shù)字資產(chǎn)管理協(xié)議
- 全新池塘出租協(xié)議
- 月餅產(chǎn)品代銷合同
- 鄉(xiāng)村旅游發(fā)展策略與實施方案
- AQ6111-2023個體防護裝備安全管理規(guī)范
- GGD交流低壓配電柜運行、維護說明書、安裝、操作手冊
- JCT2354-2016 衛(wèi)生陶瓷企業(yè)安全生產(chǎn)規(guī)范
- 2024年全國國家版圖(中小學(xué)組)知識競賽題庫及答案
- QBT 2605-2003 工業(yè)氯化鎂行業(yè)標準
- 2024年江西機電職業(yè)技術(shù)學(xué)院單招職業(yè)適應(yīng)性測試題庫帶答案
- 《拒絕沉迷手機遠離“垃圾快樂”》班會課件
- 普通高中政治課程標準測試題及答案
- 2024年知識競賽-《民用爆炸物品安全管理條例》知識競賽筆試參考題庫含答案
- 屋頂 屋頂?shù)呐潘O(shè)計 屋頂?shù)呐潘绞剑ńㄖ?gòu)造)
- Web-of-sciencenew文獻檢索-課件
評論
0/150
提交評論