第3章 C++面向對象程序設計_第1頁
第3章 C++面向對象程序設計_第2頁
第3章 C++面向對象程序設計_第3頁
第3章 C++面向對象程序設計_第4頁
第3章 C++面向對象程序設計_第5頁
已閱讀5頁,還剩137頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、版權所有版權所有 復制必究復制必究本章主要內容本章主要內容:l類和對象l類繼承l(wèi)多態(tài)性:虛函數(shù),重載,模板 與傳統(tǒng)的面向過程的程序設計語言相比,C+語言的最大特征是支持面向對象程序設計OOP(Object Oriented Programming),它引入了類、繼承、多態(tài)和重載等面向對象的新機制。通過本章的學習,使我們系統(tǒng)地介紹C+面向對象設計的基本方法。結構化程序設計的特點:結構化程序設計的特點:l是一種自上而下、逐步細化的模塊化程序設計方法。lWirth N的觀點:算法 + 數(shù)據(jù)結構 = 程序l是一種面向過程程序設計方法,即一個程序是由多個過程(在C+中為函數(shù))模塊組成,過程之間通過函數(shù)參

2、數(shù)和全局變量進行相互聯(lián)系。 3.1 面向對象程序設計概述面向對象程序設計概述 3.1.1 結構化程序設計結構化程序設計l與非結構化程序相比,結構化程序在調試、可讀性和可維護性等方面都有很大的改進。l代碼重用性不高:以過程為中心設計新系統(tǒng),除了一些標準函數(shù),大部分代碼都必須重新編寫。l由于軟、硬件技術的不斷發(fā)展和用戶需求的變化,按照功能劃分設計的系統(tǒng)模塊容易發(fā)生變化,使得開發(fā)出來的模塊的可維護性欠佳。l面向過程模式將數(shù)據(jù)與過程分離,若對某一數(shù)據(jù)結構做了修改,所有處理數(shù)據(jù)的過程都必須重新修訂,這樣就增加了很多的編程工作量。什么是對象:什么是對象:l現(xiàn)實世界是由各種各樣的事物組成,包括真實的事物和抽

3、象的事物。例如,人、動物、汽車(真實的事物)和程序、直線(抽象的事物)等。l每一類事物都有自己特定的屬性(如大小、形狀、重量等)和行為(如生長、行走、轉彎、運算等),人們通過研究事物的屬性和行為而認識事物。l在計算機科學中將這些現(xiàn)實世界中的事物稱之為對對象象。對象是包含現(xiàn)實世界中事物特征的抽象實體,它反映了系統(tǒng)為之保存信息和與之交互的方法。l在程序設計領域,可以用如下公式表示: 對象 = 數(shù)據(jù) + 作用于這些數(shù)據(jù)上的操作 3.1.2 面向對象程序設計方法及特征面向對象程序設計方法及特征l為了描述屬性和行為相同的一類對象,引入了類(class)的概念。l類是具有相同數(shù)據(jù)結構(屬性)和相同操作功能

4、(行為)的對象的集合,它規(guī)定了這些對象的公共屬性和行為方法。l對象是類的一個實例,例如,汽車是一個類,而行駛在公路上的一輛汽車則是一個對象。l對象和類的關系相當于程序設計語言中變量和變量類型的關系。lOOP圍繞現(xiàn)實世界的概念來組織模塊,采用對象描述問題空間的實體,用程序代碼模擬現(xiàn)實世界中的對象,使程序設計過程更自然、更直觀。lSP是以功能為中心來描述系統(tǒng),而OOP是以數(shù)據(jù)為中心來描述系統(tǒng)。相對于功能而言,數(shù)據(jù)具有更強的穩(wěn)定性。lOOP模擬了對象之間的通信。就象人們之間互通信息一樣,對象之間也可以通過消息進行通信。這樣,我們不必知道一個對象是怎樣實現(xiàn)其行為的,只需通過對象提供的接口進行通信并使用

5、對象所具有的行為功能。lOOP把一個復雜的問題分解成多個能夠完成獨立功能的對象(類),然后把這些對象組合起來去完成這個復雜的問題。l一個對象可由多個更小的對象組成,如汽車由發(fā)動機、傳送系統(tǒng)和排氣系統(tǒng)等組成。這些對象(類)可由不同的程序員來設計,可在不同程序中使用,就象一個汽車制造商使用許多零部件去組裝一輛汽車,而這些零部件可能不是自己生產(chǎn)的。l采用面向對象模式就象在流水線上工作,我們最終只需將多個零部件(已設計好的對象)按照一定關系組合成一個完整的系統(tǒng)。class Time private: int hour;/ 數(shù)據(jù)成員,表示小時 int minute;/ 數(shù)據(jù)成員,表示分鐘 int sec

6、ond;/ 數(shù)據(jù)成員,表示秒public: void setTime(int h, int m, int s) / 成員函數(shù),設置時間 hour=(h=0 & h=0 & m=0 & s60) ? s:0; void showTime()/ 成員函數(shù),輸出時間 couthour:minute:secondendl; ;main() Time EndTime;/ 聲明對象EndTime / 設置對象EndTime的時間(屬性,數(shù)據(jù)成員) EndTime.setTime(12, 23, 36); coutThe time is:; / 顯示對象EndTime的時間 EndTime.showTime

7、();運行結果:運行結果:The time is:12 : 23 : 36面向對象程序設計方法的基本特征面向對象程序設計方法的基本特征面向對象程序設計方法具有四個基本特征:l抽象l封裝l繼承l(wèi)多態(tài)性1. 抽象抽象 抽象是人類認識問題的最基本手段之一。抽象是指對具體問題(對象)進行概括,抽出一類類對象的公共屬性和行為并加以描述的過程。抽象包括數(shù)據(jù)抽象和代碼抽象(或行為抽象)。2. 封裝封裝 封裝是把每個對象的數(shù)據(jù)(屬性)和操作(行為)包裝在一個類中。一旦定義了對象的屬性和行為,則必須決定哪些屬性和行為只用于表示內部狀態(tài),哪些屬性和行為在外部是可見的。 一般限制直接訪問對象的屬性,而應通過操作接口

8、訪問,這樣使程序中模塊之間關系更簡單、數(shù)據(jù)更安全。對程序的修改也僅限于類的內部,使得由于修改程序所帶來的影響局部化。3. 繼承繼承 繼承是指一個新類可以從現(xiàn)有的類派生而來。新類繼承了現(xiàn)有類的特性,包括一些屬性和行為,并且可以修改或增加新的屬性和行為,使之適合具體的需要。 例如,所有的Windows應用程序都有一個窗口,它們可以看作都是從一個窗口類派生出來的,但有的應用程序用于文字處理,有的應用程序用于繪圖,這是由于派生出了不同的類,它們增加了不同的屬性和行為。 繼承很好地解決了軟件的可重用性問題。4. 多態(tài)性多態(tài)性 多態(tài)性是指類中具有相似功能的不同函數(shù)使用同一個名稱來實現(xiàn),并允許不同類的對象對

9、同一消息作出的響應不相同。 例如,同樣的“編輯|粘貼”操作,在字處理程序和繪圖程序中有不同的結果;同樣的加法,把兩個時間值相加和把兩個整數(shù)相加的要求肯定不同。 多態(tài)性使程序設計靈活、抽象,具有行為共享和代碼共享的優(yōu)點,很好地解決了程序的函數(shù)同名問題。l為了支持面向對象程序設計,C+在C語言結構(struct)數(shù)據(jù)類型的基礎上引入了類類這種抽象數(shù)據(jù)類型。lC+面向對象編程實質上就是面向類類編程,只有定義和實現(xiàn)了類,才能聲明屬于這個類的對象,才能通過對象使用定義的成員。l傳統(tǒng)C程序員把編程重點放在函數(shù)的編寫上,而C+程序員把重點放在類類的定義和實現(xiàn)上。 3.2 C+類類lC+類將對象的屬性抽象為數(shù)

10、據(jù)成員,將對象的行為抽象為成員函數(shù),并對它們進行封裝。數(shù)據(jù)成員又稱成員變量,成員函數(shù)又稱為方法。lC+類在形式上類似于C語言中用戶自定義的結構類型,但定義類時規(guī)定了成員的訪問控制權限。對象只能訪問所屬類的公有成員,而類的私有成員只能在類的成員函數(shù)中被訪問。 C+類定義的基本形式 3.2.1 類的定義與實現(xiàn)類的定義與實現(xiàn)class private:;public:;protected:;l類的定義由關鍵字class開始,其后為用戶定義的類名,花括號括起來的部分稱為類體。l關鍵字private、public和protected稱為訪問權限控制符,用來設置數(shù)據(jù)成員和成員函數(shù)的訪問屬性,其默認值為pr

11、ivate。lprivate屬性表示數(shù)據(jù)成員和成員函數(shù)是類的私有成員,它們只允許被本類的成員函數(shù)訪問或調用,數(shù)據(jù)成員一般定義為private屬性;lpublic屬性表示數(shù)據(jù)成員和成員函數(shù)是類的公有成員,它們允許被本類或其它類的成員函數(shù)(通過對象)訪問或調用,是類的外部接口,成員函數(shù)一般定義為public屬性;lprotected屬性表示數(shù)據(jù)成員和成員函數(shù)是類的保護成員,它們允許被本類的成員函數(shù)和派生類的成員函數(shù)訪問或調用。例:例:class Timeprivate:/ 最好不要省略private int hour;/ 數(shù)據(jù)成員,表示小時 int minute;/ 數(shù)據(jù)成員,表示分鐘 int s

12、econd;/ 數(shù)據(jù)成員,表示秒public: void setTime(int, int, int); / 成員函數(shù),設置時間 void showTime();/ 成員函數(shù),輸出時間;例例 定義類Time(表示時間)。私有數(shù)據(jù)成員hour、minute和second只能在類的成員函數(shù)中被訪問或賦值;公有成員函數(shù)setTime、showTime可在外部被調用,但必須通過一個對象作為對象的成員使用。l利用C+類進行面向對象編程,定義類的成員只是完成了工作的第一步,最重要的工作是實現(xiàn)定義的類。l類的實現(xiàn)實質上是類的成員函數(shù)的實現(xiàn),即定義類的成員函數(shù)。l成員函數(shù)的定義形式與一般函數(shù)的定義形式基本相同

13、,但必須在成員函數(shù)名前加上類名和作用域限定符(:)。l成員函數(shù)的定義也可放在類體內(該函數(shù)聲明之處),這時成員函數(shù)將變成內聯(lián)函數(shù)。 例:例:類的實現(xiàn):類的實現(xiàn):void Time:setTime(int h, int m, int s) hour=(h=0 & h=0 & m=0 & s60) ? s:0;void Time:showTime() couthour:minute:second”訪問對象的公有成員,但不能訪問對象的私有成員。 例如,公有成員函數(shù)調用:t1.setTime();start.showTime();pt1-setTime(); 而任何形如t1.hour、t1.minut

14、e、start.second等私有成員變量的直接訪問都是非法的。成員的訪問:成員的訪問: 例:例:main() Time EndTime;/ 聲明對象EndTime EndTime.setTime(12, 23, 36);/ 設置對象EndTime的時間 coutThe time is:; EndTime.showTime();/ 顯示對象EndTime的時間l在定義類時不能對成員變量進行初始化,因為無法確定成員變量屬于哪一個對象。l成員變量一般都定義為私有屬性,也不能在聲明對象后利用賦值運算對成員變量進行初始化。l成員變量的初始化一般是利用一個名為構造函數(shù)構造函數(shù)的成員函數(shù)來完成。 3.2.

15、2 構造函數(shù)和析構函數(shù)構造函數(shù)和析構函數(shù)如何進行成員變量的初始化? 構造函數(shù)是一種特殊的成員函數(shù),它是在創(chuàng)建對象時(聲明或new動態(tài)創(chuàng)建)系統(tǒng)自動調用的成員函數(shù)。什么是構造函數(shù):什么是構造函數(shù): 析構函數(shù)也是一種特殊的成員函數(shù),它是在對象生存期結束時系統(tǒng)自動調用的成員函數(shù)。什么是析構函數(shù):什么是析構函數(shù): 構造函數(shù)的名稱與類名相同,析構函數(shù)的名稱必須在類名前加上“”符號。注意,構造函數(shù)和析構函數(shù)不能指定任何返回值類型,包括void返回類型。#include class Timeprivate: int hour; int minute; int second;public: Time(int,

16、 int, int);/ 構造函數(shù) Time();/ 析構函數(shù) . . . . . . ; 例例 為類Time添加構造函數(shù)和析構函數(shù)。Time:Time(int h, int m, int s) hour=h;/ 對私有成員變量初始化 minute=m; second=s; coutThe constructor be called: hour: minute:secondendl; 構造函數(shù)和析構函數(shù)的實現(xiàn):構造函數(shù)和析構函數(shù)的實現(xiàn):功能與成員函數(shù)Time:setTime()類似Time:Time() coutThe destructor be called: hour: minute:se

17、condendl;void main(void) Time t1(10, 35, 55) ; / 自動調用構造函數(shù)自動調用構造函數(shù) Time t2(16, 53, 9) ; / 自動調用構造函數(shù)自動調用構造函數(shù)/ 退出退出main()主函數(shù)時自動調用析構函數(shù)主函數(shù)時自動調用析構函數(shù)構造函數(shù)和析構函數(shù)的自動調用:構造函數(shù)和析構函數(shù)的自動調用:程序運行結果為:程序運行結果為: The constructor be called:10:35:55The constructor be called:16:53:9The destructor be called:16:53:9The destructo

18、r be called:10:35:55為什么是這個結果? 當創(chuàng)建一個對象時,系統(tǒng)先根據(jù)類定義的成員變量為對象分配內存空間,然后自動調用對象的構造函數(shù)對這段內存空間進行初始化處理,從而完成對象的初始化。 當撤消一個對象時,系統(tǒng)先自動調用對象的析構函數(shù),然后釋放對象所占內存空間。 從程序的運行結果可以看出,析構函數(shù)的調用順序一般與構造函數(shù)的調用順序相反。 棧棧:后進先出表結果分析:結果分析:l與一般數(shù)據(jù)類型的變量相比,對象在它的生存期會有大量的操作,有時這些操作的結果必須在對象的生存期結束時加以清理。因此可以在析構函數(shù)中進行動態(tài)分配的內存清理工作。l如果定義類時沒有提供構造函數(shù)和析構函數(shù),編譯系

19、統(tǒng)將會自動為類分別添加一個缺省的構造函數(shù)和析構函數(shù)。如果用戶加上自定義的構造函數(shù)和析構函數(shù),編譯系統(tǒng)將不會再添加缺省的構造函數(shù)和析構函數(shù)。l若構造函數(shù)無參數(shù),則聲明對象時也不能給出參數(shù)。補充說明:補充說明: 3.2.3 this指針指針lthis指針是一個特殊的隱藏在對象中的指針,每一個處于生存期的對象都有一個this指針,用于指向對象本身。l當類的某個非靜態(tài)成員函數(shù)被調用時,系統(tǒng)通過this指針確定是哪一個對象的該成員函數(shù)被調用。實際上,this指針總是作為一個隱含參數(shù)傳遞給類的每一個成員函數(shù)。 例:例:下面定義的成員函數(shù)并沒有聲明this參數(shù):void Time:showTime() co

20、uthour:minute:secondendl;編譯器會把this指針作為成員函數(shù)的參數(shù):void Time:showTime(Time* * this); couthour:minute: secondendl;調用時:調用時: 當程序中調用某個成員函數(shù)時,編譯器會把該對象的地址賦值給this指針,并將該地址值加入到參數(shù)表中,如下所示:EndTime.showTime(&EndTime);作用:作用: 在一個成員函數(shù)中經(jīng)常需要調用其它函數(shù)(非本類的成員函數(shù)),而有時需要把對象本身(即對象的地址)作為參數(shù)傳遞給被調用函數(shù),這時必須使用this指針。 例:例:例例 this指針的使用。#inc

21、lude #include class Personpublic: / 可在外部直接訪問public屬性的數(shù)據(jù)成員 char m_strName20; char m_ID18;public: Person(char* strName, char* ID) / 內聯(lián)構造函數(shù) strcpy(m_strName, strName); strcpy(m_ID, ID); void Show();void Display(Person* pObj)/ 非成員函數(shù) coutName:m_strNameendlID:m_IDShow(); / 通過調用Show調用Display3.2.4 靜態(tài)成員的概念:靜

22、態(tài)成員的概念: 一般情況下,同一個類不同對象的數(shù)據(jù)成員所占用的內存空間是不同的(體現(xiàn)了不同對象具有不同的屬性值)。在有些情況下,類的數(shù)據(jù)成員的值對每個對象都是相同的,如當前已創(chuàng)建對象的數(shù)量,這時可以將該數(shù)據(jù)成員聲明為靜態(tài)數(shù)據(jù)成員(占有相同的存儲單元)。靜態(tài)成員的聲明:靜態(tài)成員的聲明: 在聲明成員時以關鍵字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ù)成員只有一份,存儲

23、在同一個內存空間。即使沒有創(chuàng)建類的一個對象,類的靜態(tài)數(shù)據(jù)成員也是存在的。l使用靜態(tài)數(shù)據(jù)成員保證了該數(shù)據(jù)成員值的唯一性。靜態(tài)成員的初始化:靜態(tài)成員的初始化:放在類定義的外部 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 i

24、nt GetCount();/ 獲取靜態(tài)數(shù)據(jù)成員l靜態(tài)成員函數(shù)也與一個類相關聯(lián),而不只與一個特定的對象相關聯(lián)。l區(qū)別非靜態(tài)成員函數(shù),靜態(tài)成員函數(shù)沒有this指針,因為類的靜態(tài)成員函數(shù)只有一個運行實例。l成員函數(shù)一般是公有屬性,可以通過對象、類名和作用域限定符、在成員函數(shù)中三種方式調用靜態(tài)成員函數(shù)。 靜態(tài)成員函數(shù)只能訪問類的靜態(tài)成員(成員變量和成員函數(shù)),而不能訪問類的非靜態(tài)成員。因為當通過類名和運算符“:”調用一個靜態(tài)成員函數(shù)時,不能確定函數(shù)中所訪問的非靜態(tài)成員屬于哪一個對象。 解決方法:解決方法: 將對象作為靜態(tài)成員函數(shù)的參數(shù),然后在靜態(tài)成員函數(shù)中通過對象訪問它的非靜態(tài)成員。注注 意意例例例

25、例 靜態(tài)成員變量和靜態(tài)成員函數(shù)的使用。#include #include class Personpublic: char m_strName20; long m_ID; static int m_nCount; / 靜態(tài)成員變量,表示已創(chuàng)建對象的數(shù)量public: Person(char*, long);/ 構造函數(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);

26、m_ID=ID; m_nCount+;/ 對象數(shù)目加1int Person:GetCount() return m_nCount;/ 訪問靜態(tài)成員變量long Person:GetID(Person 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; / 通過類或對象訪問靜態(tài)成員變量 coutPerson:GetCount() , ” Person:GetID(

27、e1)n; / 通過類調用靜態(tài)成員函數(shù) coute1.GetCount() , e1.GetID(e1)n; / 通過對象調用靜態(tài)成員函數(shù) Person e2(WangXiaogang,1101058); coutPerson:GetCount() , ” Person:GetID(e2)n; coute2.GetCount() , e2.GetID(e2)n; coute1.GetCount() , e1.GetID(e1)n; / e1和e2共享靜態(tài)成員變量m_nCount程序運行結果為:程序運行結果為: 1,11,11010511,11010512,11010582,11010582,1

28、101051作業(yè):作業(yè):P109-114,3-37(1),3-423.2.5 類具有封裝性,類的私有成員一般只能通過該類的成員函數(shù)訪問,這種封裝性隱藏了對象的數(shù)據(jù)成員,保證了對象的安全,但有時帶來了編程的不方便。友元函數(shù):友元函數(shù): C+提供了一種函數(shù),它雖然不是一個類的成員函數(shù),但可以象成員函數(shù)一樣訪問該類的所有成員,包括私有成員和保護成員。這種函數(shù)稱為友元(friend)函數(shù)。 一個函數(shù)要成為一個類的友員函數(shù),需要在類的定義中聲明該函數(shù),并在函數(shù)聲明的前面加上關鍵字friend。 友元函數(shù)本身的定義沒有什么特殊要求,可以是一般函數(shù),也可以是另一個類的成員函數(shù)。 為了能夠在友元函數(shù)中訪問并設

29、置類的私有數(shù)據(jù)成員,一個類的友元函數(shù)一般將該類的引用作為函數(shù)參數(shù)。 例例class A friend void display(A);/ 友元函數(shù)是一個一般函數(shù) friend void B:BMemberFun(A&); / 友元函數(shù)是另一個類B的成員函數(shù)public:. . . 友元類:友元類: 友元的另一種類型是友元類,一個類可以聲明另一個類為其友元類,這個友元類的所有成員函數(shù)都可以訪問聲明其為友元的類的所有成員。 由于訪問權限控制符不影響友元聲明,友元聲明可放在類體中任何地方,建議把友元聲明放在類體的開始位置。例例 友元(一般友元函數(shù)、友元成員函數(shù)和友元類)的聲明和使用。 P78-79,

30、例3-7。說明:說明: l友元關系是單方向的,不具有交換性和傳遞性。l使用友元雖然簡化了編程,并可避免調用成員函數(shù)的開銷,但破壞了類的封裝性,建議謹慎使用。作業(yè):作業(yè):P114,3-43l繼承繼承是面向對象程序設計方法的四個基本特征之一,是程序代碼可重用性的具體體現(xiàn)。l在C+面向對象程序設計中,所謂類的繼承就是利用現(xiàn)有的類創(chuàng)建一個新的類。新類繼承了現(xiàn)有類的屬性和行為。l為了使新類具有自己所需的功能,它可以擴充和完善現(xiàn)有類的屬性和行為,使之更具體。l微軟基礎類MFC就是通過類的繼承來體現(xiàn)類的可重用性和可擴充性。 繼承繼承發(fā)揚發(fā)揚l在現(xiàn)實世界中,一類事物的對象常常也屬于另一類事物。l在面向對象程序

31、設計方法中,一個類的對象也常常是另一個類的對象,即一個類具有了另一個類的屬性和方法。l在定義一個類時,根據(jù)類的繼承性,我們能夠且應盡可能地利用現(xiàn)有的類來定制新的類,而不必重新設計新的類。 在繼承關系中,新定義的類稱為被繼承類的派派生類生類或子類子類,而被繼承的類稱為新定義類的基類基類或父類父類。派生類繼承了基類的所有成員。 一個派生類也可以作為另一個派生類的基類。class : . . . / 派生類新增加的成員聲明列表;l派生方式?jīng)Q定了基類的成員在派生類中的訪問權限。派生方式共有三種:public、private和protected(缺省值為private)。l雖然派生類繼承了基類的所有成員

32、,但為了不破壞基類的封裝性,無論采用哪種派生方式,基類的私有成員在派生類中都是不可見的,即不允許在派生類的成員函數(shù)中訪問基類的私有成員。l采用public派生,基類成員的訪問權限在派生類中保持不變,即基類所有的公有或保護成員在派生類中仍為公有或保護成員。public派生最常用。 (1) 可以在派生類的成員函數(shù)中訪問基類的非私有成員; (2) 可通過派生類的對象直接訪問基類的公有成員。l采用private私有派生,基類所有的公有和保護成員在派生類中都成為私有成員,只允許在派生類的成員函數(shù)中訪問基類的非私有成員。private派生很少使用。l采用protected保護派生,基類所有的公有和保護成員

33、在派生類中都成為保護成員,只允許在派生類的成員函數(shù)和該派生類的派生類的成員函數(shù)中訪問基類的非私有成員。 例 定義類Point,然后定義類Point的派生類Circle。 #include class Point/ 定義基類,表示點private: int x; int y;public: void setPoint(int a, int b) x=a; y=b; ; / 設置坐標 int getX() return x; ;/ 取得X坐標 int getY() return y; ;/ 取得Y坐標;class Circle : public Point / 定義派生類,表示圓private:

34、int radius;public: void setRadius(int r) radius=r; ; / 設置半徑 int getRadius() return radius; ; / 取得半徑 int getUpperLeftX() return getX()radius; ;/ 取得外接正方形左上角的X坐標 int getUpperLeftY() return getY() + radius; ;/ 取得外接正方形左上角的Y坐標;main() Circle c; c.setPoint(200, 250); c.setRadius(100); coutX=c.getX(), Y=c.ge

35、tY() , Radius=c.getRadius()endl; coutUpperLeft X=c.getUpperLeftX() , UpperLeft Y=c.getUpperLeftY()endl; 公有派生類的對象可以直接訪問基類Point的公有成員l派生類Circle通過public派生方式繼承了基類Point的所有成員(除私有成員外所有成員的訪問權限不變),同時還定義了自己的成員變量和成員函數(shù)。l若將類Circle的派生方式改為private或protected,則下述語句是非法的:c.setPoint(200, 250); 容易混淆l無論哪種派生方式,派生類都繼承了基類的所有成

36、員,包括私有成員。我們雖然不能在派生類Circle中直接訪問私有數(shù)據(jù)成員x和y,但可以通過繼承的公有成員函數(shù)getX()、getY()和setPoint()訪問或設置它們。 利用類繼承定義類可能帶來一個問題問題:派生類會繼承它不需要的基類中的數(shù)據(jù)成員和成員函數(shù),這時,基類中不適合于派生類的成員可以在派生類中重新加以定義。 #include class Apublic:void Show( ) coutA:Shown; ;class B : public Apublic: void Show( ) coutB:Shown; ;/ 在派生類中重新定義成員函數(shù) void Display() Show

37、( ); ;/ 調用派生類B的成員函數(shù)Show(); void main() A a; B b; a.Show();/ 調用基類A的成員函數(shù)Show() b.Show();/ 調用派生類B的成員函數(shù)Show() b.Display();如果想調用基類A的成員函數(shù)Show(),可以使用作用域限 定 符 “ : : ” : A : : Show(); 從本例可以看出,雖然派生類繼承了基類的所有成員函數(shù),但如果派生類某個成員函數(shù)的名稱和參數(shù)與基類成員函數(shù)一致(即在派生類中對該成員函數(shù)重新進行了定義),則在派生類中調用的成員函數(shù)是派生類的成員函數(shù)。 請問:如果在派生類B中沒有對成員函數(shù)Show()重新

38、進行定義,程序運行結果如何? 程序運行結果:程序運行結果:A:ShowB:ShowB:Show 為什么我們經(jīng)常在現(xiàn)有類的基礎上采用繼承的方法來定制新類,而不通過直接修改現(xiàn)有類來設計自己的類?除了代碼重用的優(yōu)越性,其主要原因是可能得不到基類的實現(xiàn)源碼。繼承的重要性! 在利用微軟基礎類MFC派生自己的類時,我們只需要MFC類聲明的頭文件(利用#include指令將頭文件包含)和含有成員函數(shù)目標代碼的OBJ文件,并不需要整個MFC類庫的實現(xiàn)源碼。 l一個派生類對象也屬于其基類,因此當程序創(chuàng)建一個派生類對象時,系統(tǒng)首先自動創(chuàng)建一個基類對象。l在調用派生類的構造函數(shù)構建派生類對象時,系統(tǒng)首先調用基類的構

39、造函數(shù)構建基類對象。當派生類對象的生存期結束時,首先調用派生類的析構函數(shù),然后調用基類的析構函數(shù)。 編譯器在對程序編譯時,首先生成基類構造函數(shù)的調用代碼,然后生成派生類構造函數(shù)的調用代碼。 隱式調用和顯式調用兩種方式: 注意:注意:除非基類有默認的構造函數(shù),否則必須采用顯式調用方式。(1)隱式方式隱式方式是指在派生類的構造函數(shù)中不指定對應的基類的構造函數(shù),調用的是基類的默認構造函數(shù)(即含有缺省參數(shù)值或不帶參數(shù)的構造函數(shù))。 (2)顯式方式顯式方式是指在派生類的構造函數(shù)中指定要調用的基類構造函數(shù),并將派生類構造函數(shù)的部分參數(shù)值傳遞給基類構造函數(shù)。 設類B是類A的派生類,則派生類B顯式方式構造函數(shù)

40、的定義形式如下: B:B( ) : A( ). . . / 類B構造函數(shù)的實現(xiàn)代碼 形參聲明中的部分參數(shù),傳遞給基類構造函數(shù) 派生類構造函數(shù)形參的名稱和類型 l派生類構造函數(shù)既初始化派生類的數(shù)據(jù)成員,又通過基類構造函數(shù)初始化其基類的數(shù)據(jù)成員。l參數(shù)表中參數(shù)的個數(shù)和類型要與基類某個構造函數(shù)的形參聲明一致。 PointCircle Cylinder 注意:注意: 當基類有多個構造函數(shù)時,編譯器根據(jù)派生類構造函數(shù)為基類構造函數(shù)提供的參數(shù)表來確定調用基類的哪一個構造函數(shù)。例例 首先定義類Point,然后定義類Point的派生類Circle,再定義類Circle的派生類Cylinder。 (x, y)(

41、x, y)r(x, y)hr#include class Point/ 定義基類Pointprotected: int x, y;public: Point(int a=0, int b=0) / 含有缺省參數(shù)值的構造函數(shù)也是默認的構造函數(shù)x=a; y=b;coutPoint constructor:x,yendl; ; Point() coutPoint destructor:x,yendl; ;class Circle : public Point/ 定義類Point的派生類protected: int radius;public:/ 顯式調用基類的構造函數(shù) Circle(int a=0,

42、 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的派生類protected: int height;public: / 顯式調用基類的構造函數(shù) Cylinder(int a=0, int b=0, int r=0, int h=0) : Circle(a, b, r) height=h; coutCylin

43、der constructor:heightradiusx,yendl; ; Cylinder() coutCylinder destructor:heightradiusx,yendl; ;main() Cylinder cylinder(200, 300, 100, 400); / 調用了類Point、Circle和Cylinder的構造函數(shù)Point constructor:200,300Circle constructor:100 200,300Cylinder constructor:400 100 200,300Cylinder destructor:400 100 200,300

44、Circle destructor:100 200,300Point destructor:200,300程序運行結果:程序運行結果:構造函數(shù)的執(zhí)行順序:構造函數(shù)的執(zhí)行順序:析構函數(shù)的執(zhí)行順序:析構函數(shù)的執(zhí)行順序:Point( )Circle( )Cylinder( )當聲明Cylinder對象時 Cylinder( ) Circle( ) Point( )當程序結束時作業(yè):作業(yè):P110-1143-37(2),3-45(上機),3-46class Aclass Bclass Cclass Aclass Bclass C每個派生類只有一個直接基類 單繼承一個派生類同時從多個基類派生而來,即有多

45、個直接基類 多重繼承 設類B是類A1、A2、An的派生類,多重繼承的派生類的定義形式為: class : , , , . . . / 派生類新增加的成員聲明列表 ;多 重 繼 承 的 派 生 方 式 也 有private、public和protected三種,各基類的派生方式可以不同 class BaseA / 定義基類protected: int a;public: void setA(int);class BaseB / 定義基類protected: int b;public: void setB(int); 定義兩個基類void BaseA:setA(int x) a=x;void Ba

46、seB:setB(int x) b=x;int MultiDerived:getAB() return a+b;可以直接訪問基類中protected屬性成員成員函數(shù)的實現(xiàn)程序運行結果:a+b=100二義性錯誤:二義性錯誤:編譯器無法確定數(shù)據(jù)成員a是哪一個副本 class Bclass Cclass Dclass ADBACA派生類D的對象中存在間接基類A的兩份副本 利用作用域限定符(:)把基類的成員與下一層基類關聯(lián)起來: d1.B:a=100;或: d1.C:a=100 從路徑DBA繼承而來 從路徑DCA繼承而來 缺點:缺點: 浪費了存儲空間; 在訪問基類的成員時,要求指明訪問路徑。 大部分情

47、況下不需要保存基類多個相同的副本。 虛基類并不是一種新的類型的類,而是一種派生方式。采用虛基類方式定義派生類,在創(chuàng)建派生類的對象時,類層次結構中虛基類的成員只出現(xiàn)一次,即基類的一個副本被所有派生類對象所共享。 class Bclass Cclass Dclass AD A B C主函數(shù)中:d1.a=100; 采用虛基類方式定義派生類的方法是在基類的前面加上關鍵字virtual,而定義基類時與一般基類完全一樣。 節(jié)約內存空間; 避免在多重派生類中類成員的不明確性。 作業(yè):作業(yè):P111-1143-37(3),3-48下節(jié)講授的內容 l聲明一個派生類的對象的同時也自動聲明了一個基類的對象。 3.3

48、小節(jié)內容l派生類的對象可以認為是其基類的對象。C+允許一個基類對象的指針指向其派生類的對象 這是實現(xiàn)虛函數(shù)的關鍵l不允許派生類對象的指針指向其基類的對象。l即使將一個基類對象的指針指向其派生類的對象,通過該指針也只能訪問派生類中從基類繼承的公有成員,不能訪問派生類自定義的成員,除非通過強制類型轉換將基類指針轉換為派生類指針。 例例 程序運行結果為:程序運行結果為:a=100b=200 pb=&a pa-setB()pa-showB() class Apublic: void Show( ) coutShow();class B : public Apublic: void Show( ) co

49、utB:Shown; ; 調用哪一個Show()如果想通過基類指針調用派生類中覆蓋的成員函數(shù),只有使用虛函數(shù)虛函數(shù)。 要將一個成員函數(shù)聲明為虛函數(shù),只需在定義基類時在成員函數(shù)聲明的開始位置加上關鍵字virtual。class Apublic: virtual void Show() coutA:shown; ;class B : public Apublic: void Show() coutDraw()”可能是繪制矩形,也可能是繪制三角形或圓。具體繪制什么圖形,取決于pShape所指的對象。l即將函數(shù)調用語句與函數(shù)代碼相關聯(lián)。l兩種聯(lián)編方式:靜態(tài)聯(lián)編和動態(tài)聯(lián)編。靜態(tài)聯(lián)編是指編譯器在編譯階段就

50、確定了要調用的函數(shù),即早期綁定。動態(tài)聯(lián)編是指在程序執(zhí)行過程中根據(jù)具體情況再確定要調用的函數(shù),即后期綁定。l重載重載采用靜態(tài)聯(lián)編方式:雖然函數(shù)名相同,但編譯器能夠根據(jù)函數(shù)參數(shù)類型的不同確定要調用的函數(shù)。重載體現(xiàn)出一種靜態(tài)多態(tài)性或編譯時多態(tài)性。l當通過基類指針調用虛函數(shù)虛函數(shù)時,C+采用動態(tài)聯(lián)編方式。虛函數(shù)體現(xiàn)出一種動態(tài)多態(tài)性或運行時多態(tài)性。 l基于構造函數(shù)的特點,不能將構造函數(shù)定義為虛函數(shù)。聲明派生類對象時自動調用基類的構造函數(shù)例例 l當撤消派生類的對象時,先調用派生類析構函數(shù),然后自動調用基類析構函數(shù),如此看來析構函數(shù)沒必要定義為虛函數(shù)。但是,假如使用基類指針指向其派生類的對象,而這個派生類對

51、象是用new運算創(chuàng)建的。當程序使用delete運算撤消派生類對象時,這時只調用了基類的析構函數(shù),而沒有調用派生類的析構函數(shù)。l如果使用虛析構函數(shù),無論指針所指的對象是基類對象還是派生類對象,程序執(zhí)行時都會調用對應的析構函數(shù)。 class Apublic:A() ;/ 構造函數(shù)不能是虛函數(shù)virtual A() coutA:destructorn; ; / 析構函數(shù)是虛函數(shù);class B : public Apublic:B() ;B() coutB:destructorn; ; / 虛析構函數(shù);void main()A *pA=new B;/ . . . . . . delete pA;/

52、先調用派生類B的構造函數(shù),再調用基類A的構造函數(shù) 程序運行結果:程序運行結果: B:destructor A:destructor 總結總結:由于使用了虛析構函數(shù),當撤消pA所指派生類B的對象時,首先調用派生類B的析構函數(shù),然后再調用基類A的析構函數(shù)。如果析構函數(shù)不是虛函數(shù),則得不到下面的運行結果。請讀者思考會是什么結果 l抽象類是抽象類是類的一些行為(成員函數(shù))沒有給出具體定義的類,即純粹的一種抽象。l抽象類抽象類只能用于類的繼承,其本身不能用來創(chuàng)建對象,抽象類又稱為抽象基類。l抽象基類只提供了一個框架,僅僅起著一個統(tǒng)一接口的作用,而很多具體的功能由派生出來的類去實現(xiàn)。l雖然不能聲明抽象類的

53、對象,但可以聲明指向抽象類的指針。 在一般的類庫中都使用了抽象基類,如類CObject就是微軟基礎類庫MFC的抽象基類。l不定義具體實現(xiàn)的成員函數(shù)稱為純虛函數(shù)純虛函數(shù)。純虛函數(shù)不能被調用,僅起提供一個統(tǒng)一接口的作用。l純虛函數(shù)的聲明聲明:virtual ()= 0 ;l當基類是抽象類時,只有在派生類中重新定義基類中的所有純虛函數(shù),該派生類才不會再成為抽象類。 一個類如果滿足以下兩個條件之一就是抽象類:l至少有一個成員函數(shù)不定義具體的實現(xiàn);l定義了一個protected屬性的構造函數(shù)或析構函數(shù)。純虛函數(shù)純虛函數(shù)純虛函數(shù)/ 定義抽象基類定義抽象基類class CShapepublic:double

54、 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(); coutArea=circle.sendl;作業(yè):作業(yè):P111-1143-38(1)、(2),3- 49,3-52(上

55、機)3.5 重載重載l重載重載是C+提供的一個新特性。C+重載分為函數(shù)重載和運算符重載,這兩種重載的實質是一樣的,因為進行運算可以理解為是調用一個函數(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ù)的調用語句時,它能夠根據(jù)不同的參數(shù)類型或不同的參數(shù)個數(shù)選擇一個合適的函數(shù)。int abs(int

56、 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ù)重載。因為在沒有確定調用的是哪個函數(shù)之前,不知道函數(shù)的返回類型。 long abc(int); float abc(int);l

57、 同樣,不能利用引用進行函數(shù)重載: void fun(int&); void fun(int); 因為對于下面的調用語句,編譯器無法決定調用哪一個函數(shù): fun(i);/ i是一個整型變量 從上面可以看出,一般函數(shù)的重載重載使C+程序具有更好的可擴充性。此外,類的成員函數(shù)也可以重載,特別是構造函數(shù)的重載給C+程序設計帶來很大的靈活性。例例 構造函數(shù)的重載。class Boxprivate: int height, width, depth;public: Box() height=0; width=0; depth=0; / 避免給成員變量賦不安全的值 Box(int ht, int wd,

58、int dp) / 重載構造函數(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;程序運行結果:程序運行結果:Volume1=0,Volume2=3000類Box有兩個構造函數(shù)。第一個構造函數(shù)不帶參數(shù),把默認值0賦給對象;第二個構造函數(shù)使用參數(shù)值初始化創(chuàng)建的對象。 3.5.2 運算符重載運算符重載l運算符

59、重載:運算符重載:指對于不同數(shù)據(jù)類型的操作數(shù),同一個運算符所代表的運算功能可以不同。l一個運算符定義了一種操作,一個函數(shù)也定義了一種操作,其本質是相同的,當程序遇到運算符時會自動調用相應的運算符函數(shù)。l雖然重載運算符完成的功能都能夠用一個真正的成員函數(shù)來實現(xiàn),但使用運算符重載使程序更易于理解。l與函數(shù)重載類似,編譯器是根據(jù)參加運算的操作數(shù)的類型來識別不同的運算。例:例:l對于表達式:10+20 編譯器把它看成如下函數(shù)調用: int operator+(10, 20);l對于表達式:10.0+20.0 編譯器把它看成如下函數(shù)調用: float operator+(10.0, 20.0);參加運算

60、的數(shù)是單精度實型數(shù)參加運算的數(shù)是整數(shù) 我們可以將字符串operator+看成一個運算符函數(shù)名,這些同名的運算符函數(shù)根據(jù)不同類型的操作數(shù)完成不同的加法運算。重載運算符的形式:重載運算符的形式: 重載一個運算符,就是編寫一個運算符函數(shù),重載運算符(函數(shù))的原型為: operator();例例 定義復數(shù)類型,重載運算符“+”。運算結果的類型要重載的運算符參加運算的操作數(shù)例如:c3=c1+c2class Complexpublic: / 公有成員,以便運算符函數(shù)(非成員函數(shù))訪問 float r;/ 實部 float i;/ 虛部public: Complex(float x=0, float y=0

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論