




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第8章類和對象
8.1面向對象程序設計方法概述
8.2類的聲明和對象的定義
8.3類的成員函數
8.4對象成員的引用
8.5類的封裝性和信息隱蔽
8.6類和對象的簡單應用舉例
8.7面向對象程序設計方法概述
8.1面向對象程序設計方法概述
到目前為止,我們介紹的是C++在面向過程的程序設計中的應用。對于規模
比較小的程序,編程者可以直接編寫出一個面向過程的程序,詳細地描述每一瞬
時的數據結構及對其的操作過程。但是當程序規模較大時,就顯得力不從心了。
C++就是為了解決編寫大程序過程中的困難而產生的。
8.1.1什么是面向對象的程序設計
面向對象的程序設計的思路和人們日常生活中處理問題的思路是相似的。
在自然世界和社會生活中,一個復雜的事物總是由許多部分組成的。
當人們生產汽車時,分別設計和制造發動機、底盤、車身和輪子,最后把
它們組裝在一起。在組裝時,各部分之間有一定的聯系,以便協調工作。這就是
面向對象的程序設計的基本思路。
為了進一步說明問題,下面先討論幾個有關的概念。
1.對象
客觀世界中任何一個事物都可以看成一個對象(object)。對象可大可小。對
象是構成系統的基本單位。
任何一個對象都應當具有這兩個要素,即屬性(attribute)和行為(behavior),
它能根據外界給的信息進行相應的操作。
一個對象往往是由一組屬性和一組行為構成的。一般來說,凡是具備屬性
和行為這兩種要素的,都可以作為對象。在一個系統中的多個對象之間通過一定
的渠道相互聯系,如圖8.1示意。要使某一個對象實現某一種行為(即操作),應當
向它傳送相應的消息。對象之間就是這樣通過發送和接收消息互相聯系的。
時能2對象4
圖8.1
面向對象的程序設計采用了以上人們所熟悉的這種思路。使用面向對象的
程序設計方法設計一個復雜的軟件系統時,首要的問題是確定該系統是由哪些對
象組成的,并且設計這些對象。在C++中,每個對象都是由數據和函數(即操作
代碼)這兩部分組成的,見圖8.2。
數據體現了前面提到的“屬性”,如一個三角形對象,它的3個邊長就是它的
屬性。函數是用來對數據進行操作的,以便實現某些功能,例如可以通過邊長計
算出三角形的面積,并且輸出三角形的邊長和面積。計算三角形面積和輸出有關
數據就是前面提到的行為,在程序設計方法中也稱為方法(method)。調用對象中
的函數就是向該對象傳送一個消息(message),要求該對象實現某一行為(功能)。
2.封裝與信息隱蔽
可以對一個對象進行封裝處理,把它的一部分屬性和功能對外界屏蔽,也
就是說從外界是看不到的,甚至是不可知的。這樣做的好處是大大降低了操作對
象的復雜程度。
面向對象程序設計方法的一個重要特點就是“封裝性"(encapsulation),所謂
“封裝”,指兩方面的含義:
一是將有關的數據和操作代碼封裝在一個對象中,形成一個基本單位,各
個對象之間相對獨立,互不干擾。
二是將對象中某些部分對外隱蔽,即隱蔽其內部細節,只留下少量接口,
以便與外界聯系,接收外界的消息。這種對外界隱蔽的做法稱為信息隱蔽
(imformationhiding)。
信息隱蔽還有利于數據安全,防止無關的人了解和修改數據。
C++的對象中的函數名就是對象的對外接口,外界可以通過函數名來調用這
些函數來實現某些行為(功能)。這些將在以后詳細介紹。
3.抽象
在程序設計方法中,常用到抽象(abstraction)這一名詞。抽象的過程是將有
關事物的共性歸納、集中的過程。抽象的作用是表示同一類事物的本質。
C和C++中的數據類型就是對一批具體的數的抽象。對象是具體存在的,如
一個三角形可以作為一個對象,10個不同尺寸的三角形是10個對象。如果這10
個三角形對象有相同的屬性和行為,可以將它們抽象為一種類型,稱為三角形類
型。
在C++中,這種類型就稱為“類(class)”。這10個三角形就是屬于同一“類''的
對象。類是對象的抽象,而對象則是類的特例,或者說是類的具體表現形式。
4.繼承與重用
如果在軟件開發中已經建立了一個名為A的“類”,又想另外建立一個名為B
的“類”,而后者與前者內容基本相同,只是在前者的基礎上增加一些屬性和行為,
只需在類A的基礎上增加一些新內容即可。這就是面向對象程序設計中的繼承機
制。
利用繼承可以簡化程序設計的步驟。
“白馬”繼承了“馬”的基本特征,又增加了新的特征(顏色),“馬”是父類,或
稱為基類,"白馬''是從"馬”派生出來的,稱為子類或派生類。
C++提供了繼承機制,采用繼承的方法可以很方便地利用一個已有的類建立
一個新的類。這就是常說的“軟件重用"(softwarereusability)的思想。
5.多態性
如果有幾個相似而不完全相同的對象,有時人們要求在向它們發出同一個
消息時,它們的反應各不相同,分別執行不同的操作。這種情況就是多態現象。
如,在Windows環境下,用鼠標雙擊一個文件對象(這就是向對象傳送一個
消息),如果對象是一個可執行文件,則會執行此程序,如果對象是一個文本文
件,則啟動文本編輯器并打開該文件。
在C++中,所謂多態性(polymorphism)是指:由繼承而產生的相關的不同的
類,其對象對同一消息會作出不同的響應。多態性是面向對象程序設計的一個重
要特征,能增加程序的靈活性。
8.1.2面向對象程序設計的特點
傳統的面向過程程序設計是圍繞功能進行的,用一個函數實現一個功能。
所有的數據都是公用的,一個函數可以使用任何一組數據,而一組數據又能被多
個函數所使用(見圖8.3)。
圖8.3
面向對象程序設計采取的是另外一種思路。它面對的是一個個對象。實際
上,每一組數據都是有特定的用途的,是某種操作的對象。也就是說,一組操作
調用一組數據。
程序設計者的任務包括兩個方面:
一是設計所需的各種類和對象,即決定把哪些數據和操作封裝在一起;
二是考慮怎樣向有關對象發送消息,以完成所需的任務。
這時他如同一個總調度,不斷地向各個對象發出命令,讓這些對象活動起
來(或者說激活這些對象),完成自己職責范圍內的工作。各個對象的操作完成了,
整體任務也就完成了。
顯然,對一個大型任務來說,面向對象程序設計方法是十分有效的,它能
大大降低程序設計人員的工作難度,減少出錯機會。
8.1.3類和對象的作用
類是C++中十分重要的概念,它是實現面向對象程序設計的基礎。類是所有
面向對象的語言的共同特征,所有面向對象的語言都提供了這種類型。一個有一
定規模的C++程序是由許多類所構成的。
C++支持面向過程的程序設計,也支持基于對象的程序設計,又支持面向對
象的程序設計。在本章到第10章將介紹基于對象的程序設計。包括類和對象的概
念、類的機制和聲明、類對象的定義與使用等。這是面向對象的程序設計的基礎。
基于對象就是基于類。與面向過程的程序不同,基于對象的程序是以類和對象為
基礎的,程序的操作是圍繞對象進行的。
在此基礎上利用了繼承機制和多態性,就成為面向對象的程序設計(有時不
細分基于對象程序設計和面向對象程序設計,而把二者合稱為面向對象的程序設
計)。
基于對象程序設計所面對的是一個個對象。所有的數據分別屬于不同的對
象。在面向過程的結構化程序設計中,人們常使用這樣的公式來表述程序:
程序=算法+數據結構
算法和數據結構兩者是互相獨立、分開設計的,面向過程的程序設計是以
算法為主體的。在實踐中人們逐漸認識到算法和數據結構是互相緊密聯系不可分
的,應當以一個算法對應一組數據結構,而不宜提倡一個算法對應多組數據結構,
以及一組數據結構對應多個算法。基于對象和面向對象程序設計就是把一個算法
和一組數據結構封裝在一個對象中。因此,就形成了新的觀念:
對象=算法+數據結構
程序=(對象+對象+對象+…)+消息
或:
程序=對象S+消息
“對象S”表示多個對象。消息的作用就是對對象的控制。程序設計的關鍵是
設計好每一個對象,及確定向這些對象發出的命令,使各對象完成相應操作。
8.1.4面向對象的軟件開發
隨:著軟件規模的迅速增大,軟件人員面臨的問題十分復雜。需要規范整個
軟件開發過程,明確軟件開發過程中每個階段的任務,在保證前一個階段工作的
正確性的情況下,再進行下一階段的工作。這就是軟件工程學需要研究和解決的
問題。面向對象的軟件工程包括以下幾個部分:
1.面向對象分析(objectorientedanalysis,00A)
軟件工程中的系統分析階段,系統分析員要和用戶結合在一起,對用戶的
需求作出精確的分析和明確的描述,從宏觀的角度概括出系統應該做什么(而不
是怎么做)。面向對象的分析,要按照面向對象的概念和方法,在對任務的分析
中,從客觀存在的事物和事物之間的關系,歸納出有關的對象(包括對象的屬性
和行為)以及對象之間的聯系,并將具有相同屬性和行為的對象用一個類(class)
來表示。建立一個能反映真實工作情況的需求模型。
2.面向對象設計(objectorienteddesign,00D)
根據面向對象分析階段形成的需求模型,對每一部分分別進行具體的設計,
首先是進行類的設計,類的設計可能包含多個層次(利用繼承與派生)。
然后以這些類為基礎提出程序設計的思路和方法,包括對算法的設計。
在設計階段,并不牽涉某一種具體的計算機語言,而是用一種更通用的描
述工具(如偽代碼或流程圖)來描述。
3.面向對象編程(objectorientedprogramming,OOP)
根據面向對象設計的結果,用一種計算機語言把它寫成程序,顯然應當選
用面向對象的計算機語言(例如C++),否則無法實現面向對象設計的要求。
4.面向對象測試(objectorientedtest,OOT)
在寫好程序后交給用戶使用前,必須對程序進行嚴格的測試。測試的月的
是發現程序中的錯誤并改正它。面向對象測試是用面向對象的方法進行測試,以
類作為測試的基本單元。
5.面向對象維護(objectorientedsoftmaintenance,OOSM)
因為對象的封裝性,修改一個對象對其它對象影響很小。利用面向對象的
方法維護程序,大大提高了軟件維護的效率。現在設計一個大的軟件,是嚴格按
照面向對象軟件工程的5個階段進行的,這5個階段的工作不是由一個人從頭到尾
完成的,而是由不同的人分別完成的。這樣,OOP階段的任務就比較簡單了,程
序編寫者只需要根據00D提出的思路用面向對象語言編寫出程序即可。在一個
大型軟件的開發中,OOP只是面向對象開發過程中的一個很小的部分。如果所處
理的是一個較簡單的問題,可以不必嚴格按照以上5個階段進行,往往由程序設
計者按照面向對象的方法進行程序設計,包括類的設計(或選用已有的類)和程序
的設計。
8.2類的聲明和對象的定義
8.2.1類和對象的關系
每一個實體都是對象。有一些對象是具有相同的結構和特性的。每個對象
都屬于一個特定的類型。在C++中對象的類型稱為類(class)。類代表了某一批對
象的共性和特征。前面已說明:
類是對象的抽象,而對象是類的具體實例(instance)。
正如同結構體類型和結構體變量的關系一樣,人們先聲明一個結構體類型,
然后用它去定義結構體變量。同一個結構體類型可以定義出多個不同的結構體變
量。
在C++中也是先聲明一個類類型,然后用它去定義若干個同類型的對象。對
象就是類類型的一個變量。可以說類是對象的模板,是用來定義對象的一種抽象
類型。
類是抽象的,不占用內存,而對象是具體的,占用存儲空間。
在一開始時弄清對象和類的關系是十分重要的?
8.2.2聲明類類型
類是用戶自己指定的類型。如果程序中要用到類類型,必須自己根據需要
進行聲明,或者使用別人已設計好的類。C++標準本身并不提供現成的類的名稱、
結構和內容。
在C++中聲明一個類類型和聲明一個結構體類型是相似的.
下面是聲明一個結構體類型的方法:
structStudent〃聲明了一個名為Student的結構體類型
(
intnum;
charname[20];
charsex;
);
Studentstudl,stud2;
〃定義了兩個結構體變量studl和stud2
它只包括數據,沒有包括操作。
現在聲明一個類:
classStudent〃以class開頭
{
intnum;
charname[20];
charsex;〃以上3行是數據成員
voiddisplay()//這是成員函數
{cout?r,num:"?num?endl;
cout?rzname//?name?endl;
cout?,zsex/'?sex?endl;
〃以上4行是函數中的操作語句}
);
Studentstudl,stud2;〃定義了兩個Student類的對象studl和stud2
可以看到聲明類的方法是由聲明結構體類型的方法發展而來的。
可以看到,類(class)就是對象的類型。實際上,類是一種廣義的數據類型。
類這種數據類型中的數據既包含數據,也包含操作數據的函數。
不能把類中的全部成員與外界隔離,一般是把數據隱蔽起來,而把成員函
數作為對外界的接口。
可以將上面類的聲明改為
classStudent//聲明類類型
{private:〃聲明以下部分為私有的
intnum;
charname[20];
charsex;
public:〃聲明以下部分為公用的
voiddisplay()
{cout?"num/,?num?endl;
cout?,rname:,r?name?endl;
cout?r,sex:/r?sex?endl;}
);
Studentstudl,stud2;〃定義了兩個Student類的對象
如果在類的定義中既不指定private,也不指定public,則系統就默認為是私
有的。歸納以上對類類型的聲明,
可得到其一般形式如下:
class類名
(
private:私有的數據和成員函數;
public:公用的數據和成員函數;
);
private和public稱為成員訪問限定符(memberaccessspecifier)。除了private和
public之夕卜,還有一種成員訪問限定符protected(受保護的),用protected聲明的成
員稱為受保護的成員,它不能被類外訪問(這點與私有成員類似),但可以被派生
類的成員函數訪問。
在聲明類類型時,聲明為private的成員和聲明為public的成員的次序任意,
既可以先出現private部分,也可以先出現public部分。如果在類體中既不寫關鍵
字private,又不寫public,就默認為private。
在一個類體中,關鍵字private和public可以分別出現多次。每個部分的有效
范圍到出現另一個訪問限定符或類體結束時(最后一個右花括號)為止。
但是為了使程序清晰,應該養成這樣的習慣:使每一種成員訪問限定符在
類定義體中只出現一次。
在以前的C++程序中,常先出現private部分,后出現public部分,如上面所
示。
現在的C++程序多數先寫public部分,把private部分放在類體的后部。
這樣可以使用戶將注意力集中在能被外界調用的成員上,使閱讀者的思路
更清晰一些。
在C++程序中,經常可以看到類。為了用戶方便,常用的C++編譯系統往往
向用戶提供類庫(但不屬于C++語言的組成部分),內裝常用的基本的類,供用戶
使用。不少用戶也把自己或本單位經常用到的類放在一個專門的類庫中,需要用
時直接調用,這樣就減少了程序設計的工作量。
8.2.3定義對象的方法
8.2.2節的程序段中,最后一行用已聲明的Student類來定義對象,這種方法
是很容易理解的。經過定義后,stud1和stud2就成為具有Student類特征的對象。
studl和stud2這兩個對象都分別包括Student類中定義的數據和函數。
定義對象也可以有幾種方法。
1.先聲明類類型,然后再定義對象前面用的就是這種方法,如Studentstudl,
stud2;〃Student是已經聲明的類類型在CH中,聲明了類類型后,定義對象有兩種
形式。
(1)class類名對象名
如
classStudentstudl,stud2;
把class和Student合起來作為一個類名,用來定義對象。
(2)類名對象名
如Studentstudl,stud2;
直接用類名定義對象。
這兩種方法是等效的。第1種方法是從C語言繼承下來的,第2種方法是C++
的特色,顯然第2種方法更為簡捷方便。
2.在聲明類類型的同時定義對象
classStudent
〃聲明類類型
{public:
〃先聲明公用部分
voiddisplay()
{cout?,,num:,,?num?endl;
cout?,,name:,,?name?endl;
cout?/,sex:/,?sex?endl;}
private:
〃后聲明私有部分
intnum;
charname[20];
charsex;
}studl,stud2;〃定義了兩個Student類的對象
在定義Student類的同時,定義了兩個Student類的對象。
3.不出現類名,直接定義對象
class〃無類名
{private:〃聲明以下部分為私有的
?
?
?
public:〃聲明以下部分為公用的
I
I
I
}studl,stud2;〃定義了兩個無類名的類對象
直接定義對象,在C++中是合法的、允許的,但卻很少用,也不提倡用。在
實際的程序開發中,一般都采用上面3種方法中的第1種方法。在小型程序中或所
聲明的類只用于本程序時,也可以用第2種方法。在定義一個對象時,編譯系統
會為這個對象分配存儲空間,以存放對象中的成員。
8.2.4類和結構體類型的異同
C++增加了class類型后,仍保留了結構體類型(struct),而且把它的功能也
擴展了。C++允許用struct來定義一個類型。如可以將前面用關鍵字class聲明的類
類型改為用關鍵字struct:
structStudent〃用關鍵字struct來聲明一個類類型
{private:〃聲明以下部分為私有的
intnum;〃以下3行為數據成員
charname[20];charsex;
public:〃聲明以下部分為公用的
voiddisplay()〃成員函數
{cout?,,num:,,?num?endl;cout?,,name:,,?name?endl;cout?,,sex:M?se
x?endl;}
};
Studentstudl,stud2;〃定義了兩個Student類的對象
為了使結構體類型也具有封裝的特征,C++不是簡單地繼承C的結構體,而
是使它也具有類的特點,以便于用于面向對象程序設計。
用struct聲明的結構體類型實際上也就是類。用struct聲明的類,如果對其成
員不作private或public的聲明,系統將其默認為public。
如果想分別指定私有成員和公用成員,則應用private或public作顯式聲明。
而用class定義的類,如果不作private或public聲明,系統將其成員默認為
private,在需要時也可以自己用顯式聲明改變。
如果希望成員是公用的,使用struct比較方便,如果希望部分成員是私有的,
宜用class。建議盡量使用class來建立類,寫出完全體現CH風格的程序。
8.3類的成員函數
8.3.1成員函數的性質
類的成員函數(簡稱類函數)是函數的一種,它的用法和作用和第4章介紹過
的函數基本上是一樣的,它也有返回值和函數類型,
它與一般函數的區別只是:
它是屬于一個類的成員,出現在類體中。
它可以被指定為private(私有的)、public(公用的)或protected(受保護的)。
在使用類函數時,要注意調用它的權限(它能否被調用)以及它的作用域(函
數能使用什么范圍中的數據和函數)。
例如私有的成員函數只能被本類中的其它成員函數所調用,而不能被類外
調用。
成員函數可以訪問本類中任何成員(包括私有的和公用的),可以引用在本作
用域中有效的數據。
一般的做法是將需要被外界調用的成員函數指定為public,它們是類的對外
接口。
但應注意,并非要求把所有成員函數都指定為public。有的函數并不是準備
為外界調用的,而是為本類中的成員函數所調用的,就應該將它們指定為private。
這種函數的作用是支持其它函數的操作,是類中其它成員的工具函數(utility
function),類外用戶不能調用這些私有的工具函數。
類的成員函數是類體中十分重要的部分。如果一個類中不包含成員函數,
就等同于C語言中的結構體了,體現不出類在面向對象程序設計中的作用。
8.3.2在類外定義成員函數
在前面已經看到成員函數是在類體中定義的。也可以在類體中只寫成員函
數的聲明,而在類的外面進行函數定義。如
classStudent
{public:
voiddisplay();
〃公用成員函數原型聲明
private:
intnum;
stringname;
charsex;
〃以上3行是私有數據成員
);
voidStudentI*display()
〃在類外定義display類函數
{cout?zrnum:zr?num?endl;
〃函數體
cout?/rname:"?name?endl;
cout?Ksex:z,?sex?endl;
)
Studentstudl,stud2;
〃定義兩個類對象
注意:在類體中直接定義函數時,不需要在函數名前面加上類名,因為函
數屬于哪一個類是不言而喻的。
但成員函數在類外定義時,必須在函數名前面加上類名,予以限定
(qualifed),“::”是作用域限定符(fieldqualifier)或稱作用域運算符,用它聲明函數
是屬于哪個類的。
如果在作用域運算符“::”的前面沒有類名,或者函數名前面既無類名又無
作用域運算符“::”,
如::display()或display()
則表示display函數不屬于任何類,這個函數不是成員函數,而是全局函數,
即非成員函數的一般普通函數。
類函數必須先在類體中作原型聲明,然后在類外定義,也就是說類體的位
置應在函數定義之前,否則編譯時會出錯。
雖然函數在類的外部定義,但在調用成員函數時會根據在類中聲明的函數
原型找到函數的定義(函數代碼),從而執行該函數。
在類的內部對成員函數作聲明,而在類體外定義成員函數,這是程序設計
的一種良好習慣。
如果一個函數,其函數體只有2-3行,一般可在聲明類時在類體中定義。多
于3行的函數,一般在類體內聲明,在類外定義。
8.3.3inline成員函數
關于內置(inline)函數,已在第4章第4.5節中作過介紹。
類的成員函數也可以指定為內置函數。
在類體中定義的成員函數的規模一般都很小,而系統調用函數的過程所花
費的時間開銷相對是比較大的。調用一個函數的時間開銷遠遠大于小規模函數體
中全部語句的執行時間。
為了減少時間開銷,如果在類體中定義的成員函數中不包括循環等控制結
構,C++系統會自動將它們作為內置(inline)函數來處理。
也就是說,在程序調用這些成員函數時,并不是真正地執行函數的調用過
程(如保留返回地址等處理),而是把函數代碼嵌入程序的調用點。
這樣可以大大減少調用成員函數的時間開銷。C++要求對一般的內置函數要
用關鍵字inline聲明,但對類內定義的成員函數,可以省略inline,因為這些成員
函數已被隱含地指定為內置函數。如
classStudent
{public:
voiddisplay()
{coutvv"nunr"v<numv〈endl;coutv<"name:"
<<name?endl;cout?zzsex/z?sex?endl;}
private:
intnum;
stringname;
charsex;
);
其中第3行
voiddisplay()也可以寫成
inlinevoiddisplay()
將display函數顯式地聲明為內置函數。
以上兩種寫法是等效的。對在類體內定義的函數,一般都省寫inline。
應該注意的是:如果成員函數不在類體內定義,而在類體外定義,系統并
不把它默認為內置(inline)函數,調用這些成員函數的過程和調用一般函數的過
程是相同的。如果想將這些成員函數指定為內置函數,應當用inline作顯式聲明。
如
classStudent
(
public:inlinevoiddisplay();〃聲明此成員函數為內置函數
private:
intnum;
stringname;
charsex;
);
inlinevoidStudent:Idisplay()//在類外定義display函數為內置函數
{cout?,,num:,,?num?endl;cout?,,name:M?name?endl;cout?,,sex:/,?se
x?endl;}
在第4章第4.5節曾提到過,在函數的聲明或函數的定義兩者之一作inline聲
明即可。
值得注意的是:如果在類體外定義inline函數,則必須將類定義和成員函數
的定義都放在同一個頭文件中(或者寫在同一個源文件中),否則編譯時無法進行
置換(將函數代碼的拷貝嵌入到函數調用點)。
但是這樣做,不利于類的接口與類的實現分離,不利于信息隱蔽。雖然程
序的執行效率提高了,但從軟件工程質量的角度來看,這樣做并不是好的辦法。
只有在類外定義的成員函數規模很小而調用頻率較高時,才將此成員函數
指定為內置函數。
8.3.4成員函數的存儲方式
用類去定義對象時,系統會為每一個對象分配存儲空間.
如果一個類包括了數據和函數,要分別為數據和函數的代碼分配存儲空間。
按理說,如果用同一個類定義了10個對象,那么就需要分別為10個對象的
數據和函數代碼分配存儲單元,如圖8.4所示。
對象I對象2時象10
公用函數代碼
圖8.4
能否只用一段空間來存放這個共同的函數代碼段,在調用各對象的函數時,
都去調用這個公用的函數代碼。如圖8.5所示。
時象!對象2對象10
函數函數
代碼?代碼2
圖8.5
顯然,這樣做會大大節約存儲空間。C++編譯系統正是這樣做的,因此每個
對象所占用的存儲空間只是該對象的數據部分所占用的存儲空間,而不包括函數
代碼所占用的存儲空間。如果聲明了一個類:
classTime
public:
inthour;
intminute;
intsec;
voidset()
{cin?a?b?c;}
);
可以用下面的語句來輸出該類對象所占用的字節數:
cout?sizeof(Time)?endl;
輸出的值是12。
這就證明了一個對象所占的空間大小只取決于該對象中數據成員所占的空
間,而與成員函數無關。
函數代碼是存儲在對象空間之外的。如果對同一個類定義了10個對象,這
些對象的成員函數對應的是同一個函數代碼段,而不是10個不同的函數代碼段。
需要注意的是:雖然調用不同對象的成員函數時都是執行同一段函數代
碼,但是執行結果一般是不相同的。
不同的對象使用的是同一個函數代碼段,它怎么能夠分別對不同對象中的
數據進行操作呢?
原來C++為此專門設立了一個名為this的指針,用來指向不同的對象。需要
說明:
(1)不論成員函數在類內定義還是在類外定義,成員函數的代碼段都用同一
種方式存儲。
(2)不要將成員函數的這種存儲方式和inline(內置)函數的概念混淆。
(3)應當說明:常說的“某某對象的成員函數”,是從邏輯的角度而言的,
而成員函數的存儲方式,是從物理的角度而言的,二者是不矛盾的。
8.4對象成員的引用
在程序中經常需要訪問對象中的成員。訪問對象中的成員可以有3種方法:
通過對象名和成員運算符訪問對象中的成員;
通過指向對象的指針訪問對象中的成員;
通過對象的引用變量訪問對象中的成員。
8.4.1通過對象名和成員運算符訪問對象中的成員
例如在程序中可以寫出以下語句:
stud1,num=1001;〃假設num已定義為公用的整型數據成員
表示將整數1001賦給對象stud1中的數據成員num。
其中“.”是成員運算符,用來對成員進行限定,指明所訪問的是哪一個對象
中的成員。
注意不能只寫成員名而忽略對象名。
訪問對象中成員的一般形式為
對象名.成員名
不僅可以在類外引用對象的公用數據成員,而且還可以調用對象的公用成
員函數,但同樣必須指出對象名,如
studl.display();〃正確,調用對象stud1的公用成員函數
display();〃錯誤,沒有指明是哪一個對象的display函數
由于沒有指明對象名,編譯時把display作為普通函數處理。
應該注意所訪問的成員是公用的(public)還是私有的(private),,只能訪問
public成員,而不能訪問private成員,如果已定義num為私有數據成員,下面的語
句是錯誤的:
studl.num=10101;〃num是私有數據成員,不能被外界引用
在類外只能調用公用的成員函數。在一個類中應當至少有一個公用的成員
函數,作為對外的接口,否則就無法對對象進行任何操作。
8.4.2通過指向對象的指針訪問對象中的成員
在第7章第7.1.5節中介紹了指向結構體變量的指針,可以通過指針引用結構
體中的成員。用指針訪問對象中的成員的方法與此類似。如果有以下程序段:
classTime
(
public:〃數據成員是公用的
inthour;
intminute;
};Timet,*p;〃定義對象t和指針變量p
p=&t,使p指向對象t
cout?p->hour;〃輸出p指向的對象中的成員hour
在p指向t的前提下,p->hour,(*p).hour和t.hour三者等價。
8.4.3通過對象的引用變量來訪問對象中的成員
如果為一個對象定義了一個引用變量,它們是共占同一段存儲單元的,實
際上它們是同一個對象,只是用不同的名字表示而已。
因此完全可以通過引用變量來訪問對象中的成員。
如果已聲明了Time類,并有以下定義語句:
Timetl;//定義對象tl
Time&t2=tl;〃定義Time類引用變量t2,并使之初始化為tl
cout?t2.hour;//l#出對象11中的成員hour
由于t2與tl共占同一段存儲單元(即t2是tl的別名),因此t2.hour就是tl.hour。
本章第8.6節的例8.2中的程序(b),介紹的是引用變量作為形參的情況,讀者
可以參考。
8.5類的封裝性和信息隱蔽
8.5.1公用接口與私有實現的分離
從前面的介紹已知:C++通過類來實現封裝性,把數據和與這些數據有關
的操作封裝在一個類中,或者說,類的作用是把數據和算法封裝在用戶聲明的抽
象數據類型中。
在聲明了一個類以后,用戶主要是通過調用公用的成員函數來實現類提供
的功能(例如對數據成員設置值,顯示數據成員的值,對數據進行加工等)。
因此,公用成員函數是用戶使用類的公用接口(publicinterface),或者說是
類的對外接口。
當然并不一定要把所有成員函數都指定為public(公用)的,但這時這些成員
函數就不是公用接口了。
在類外雖然不能直接訪問私有數據成員,但可以通過調用公用成員函數來
引用甚至修改私有數據成員。
用戶可以調用公用成員函數來實現某些功能,而這些功能是在聲明類時已
指定的,用戶可以使用它們而不應改變它們。
實際上用戶往往并不關心這些功能是如何實現的細節,而只需知道調用哪
個函數會得到什么結果,能實現什么功能即可。
通過成員函數對數據成員進行操作稱為類的實現,為了防止用戶任意修改
公用成員函數,改變對數據進行的操作,往往不讓用戶看到公用成員函數的源代
碼,顯然更不能修改它,用戶只能接觸到公用成員函數的目標代碼(詳見852節)。
可以看到:類中被操作的數據是私有的,實現的細節對用戶是隱蔽的,這
種實現稱為私有實現(privateimplementation)o
這種“類的公用接口與私有實現的分離”形成了信息隱蔽。
軟件工程的一個最基本的原則就是將接口與實現分離,信息隱蔽是軟件工
程中一個非常重要的概念。它的好處在于:
(1)如果想修改或擴充類的功能,只需修改本類中有關的數據成員和與它有
關的成員函數,程序中類外的部分可以不必修改。
(2)如果在編譯時發現類中的數據讀寫有錯,不必檢查整個程序,只需檢查
本類中訪問這些數據的少數成員函數。
8.5.2類聲明和成員函數定義的分離
在面向對象的程序開發中,一般做法是將類的聲明(其中包含成員函數的聲
明)放在指定的頭文件中,用戶如果想用該類,只要把有關的頭文件包含進來即
可,不必在程序中重復書寫類的聲明,以減少工作量,節省篇幅,提高編程的效
率。
由于在頭文件中包含了類的聲明,因此在程序中就可以用該類來定義對象。
由于在類體中包含了對成員函數的聲明,在程序中就可以調用這些對象的公用成
員函數。
為了實現上一節所敘述的信息隱蔽,對類成員函數的定義一般不放在頭文
件中,而另外放在一個文件中。
例如,可以分別寫兩個文件:
//student.h(這是頭文件,在此文件中進行類的聲明)
classStudent〃類聲明
{public:
voiddisplay();〃公用成員函數原型聲明
private:
intnum;
charname[20];
charsex;};
//student,cpp
〃在此文件中進行函數的定義
#include<iostream>
#include“student.h"〃不要漏寫此行,否則編譯通不過
voidStudent::display()〃在類夕卜定義display類函數
{cout?zrnum:"r?num?endl;
cout?,rname:z,?name?endl;
cout?,rsex:r,?sex?endl;
)
為了組成一個完整的源程序,還應當有包括主函數的源文件:
//main.cpp主函數模塊
#include<iostream>
#include"student.h"〃將類聲明頭文件包含進來
intmain()
{Studentstud;〃定義對象
stud.display();〃執行stud對象的display函數
return0;
}
這是一個包括3個文件的程序,組成兩個文件模塊:
—個是主模塊main.cpp,
一個是student.cpp。
在主模塊中又包含頭文件student.h。
在預編譯時會將頭文件student.h中的內容取代#include"student.h"行。
請注意:由于將頭文件student.h放在用戶當前目錄中,因此在文件名兩側
用雙撇號包起來("student.h")而不用尖括號(〈student.h>),否則編譯時會找不到此
文件。
主帽]夬nwin.qip成,“函敢支XZ件studcnt.qrp
main.cNC
圖86
在運行程序時調用stud中的display函數,輸出各數據成員的值。
如果一個類聲明多次被不同的程序所選用,每次都要對包含成員函數定義
的源文件(如上面的student.cpp)進行編譯,這是否可以改進呢?
的確,可以不必每次都對它重復進行編譯,而只需編譯一次即可。
把第一次編譯后所形成的目標文件保存起來,以后在需要時把它調出來直
接與程序的目標文件相連接即可。這和使用函數庫中的函數是類似的。這也是把
成員函數的定義不放在頭文件中的一個好處。
在實際工作中,并不是將一個類聲明做成一個頭文件,而是將若干個常用
的功能相近的類聲明集中在一起,形成類庫。
類庫有兩種:
一種是C++編譯系統提供的標準類庫;
一種是用戶根據自己的需要做成的用戶類庫,提供給自己和自己授權的人
使用,這稱為自定義類庫。
在程序開發工作中,類庫是很有用的,它可以減少用戶自己對類和成員函
數進行定義的工作量。
類庫包括兩個組成部分:
(1)類聲明頭文件;
(2)已經過編譯的成員函數的定義,它是目標文件。
用戶只需把類庫裝入到自己的計算機系統中(一般裝到C++編譯系統所在的
子目錄下),并在程序中用#include命令行將有關的類聲明的頭文件包含到程序
中,就可以使用這些類和其中的成員函數,順利地運行程序。
這和在程序中使用C++系統提供的標準函數的方法是一樣的,例如用戶在調
用sin函數時只需將包含聲明此函數的頭文件包含到程序中,即可調用該庫函數,
而不必了解sin函數是怎么實現的(函數值是怎樣計算出來的)。
當然,前提是系統已裝了標準函數庫。在用戶源文件經過編譯后,與系統
庫(是目標文件)相連接。
在用戶程序中包含類聲明頭文件,類聲明頭文件就成為用戶使用類的公用
接口,在頭文件的類體中還提供了成員函數的函數原型聲明,用戶只有通過頭文
件才能使用有關的類。
用戶看得見和接觸到的是這個頭文件,任何要使用這個類的用戶只需包含
這個頭文件即可。包含成員函數定義的文件就是類的實現。
請特別注意:類聲明和函數定義一般是分別放在兩個文本中的。由于要求
接口與實現分離,為軟件開發商向用戶提供類庫創造了很好的條件。
開發商把用戶所需的各種類的聲明按類放在不同的頭文件中,同時對包含
成員函數定義的源文件進行編譯,得到成員函數定義的目標代碼。
軟件商向用戶提供這些頭文件和類的實現的目標代碼(不提供函數定義的源
代碼)。用戶在使用類庫中的類時,只需將有關頭文件包含到自己的程序中,并
且在編譯后連接成員函數定義的目標代碼即可。
由于類庫的出現,用戶可以像使用零件一樣方便地使用在實踐中積累的通
用的或專用的類,這就大大減少了程序設計的工作量,有效地提高了工作效率。
8.5.3面向對象程序設計中的幾個名詞
類的成員函數在面向對象程序理論中被稱為“方法”(method),“方法”是指對
數據的操作。一個“方法”對應一種操作。
顯然,只有被聲明為公用的方法(成員函數)才能被對象外界所激活。
外界是通過發“消息”來激活有關方法的。所謂“消息”,其實就是一個命令,
由程序語句來實現。前面的stud.display();就是向對象stud發出的一個"消息”,通
知它執行其中的display"方法''(即display函數)。
上面這個語句涉及3個術語:對象、方法和消息。
stud是對象,
display()是方法,
語句“stud.display();“是消息。
8.6類和對象的簡單應用舉例
例8.1最簡單的例子。
#include<iostream>
usingnamespacestd;
classTime〃定義Time類
{public:〃數據成員為公用的
inthour;
intminute;
intsec;};
intmain()
(
Timetl;〃定義tl為Time類對象
cin?t1.hour;〃輸入設定的時間
cin?tl.minute;
cin?tl.sec;
〃輸出時間:
cout?t1.hour?rr:,r?t1.minutew":r,?t1.sec?endl;
return0;
)
運行情況如下:123243/
12:32:43
注意:
(1)在引用數據成員hour,minute,sec時不要忘記在前面指定對象名。
(2)不要錯寫為類名,
如寫成
Time.hour,Time.minute,Time.sec是不對的。因為類是一種抽象的數據類型,
并不是一個實體,也不占存儲空間,而對象是實際存在的實體,是占存儲空間的,
其數據成員是有值的,可以被引用的。
(3)如果刪去主函數的3個輸入語句,即不向這些數據成員賦值,則它們的
值是不可預知的。
1'而6口;//定義對象口
Time&t2=tl;〃定義Time類引用變量t2,并初始化為tl
cout?t2.hour;〃輸出對象11中的成員hour
例8.2引用多個對象的成員。
(1)程序(a)
#include<iostream>
usingnamespacestd;
classTime
{public:
inthour;
intminute;
intsec;
);
intmain()
{Timetl;//定義對象tl
cin?tLhour;〃向11的數據成員輸入數據
cin?tl.minute;
cin?tl.sec;
cout?tl.hour?,,:,,?tl.minute?H:H?tl.sec?endl;〃輸出11中數據成員的值
Timet2;〃定義對象t2
cin?t2.hour;〃向t2的數據成員輸入數據
cin?t2.minute;
cin?t2.sec;
cout<<t2.hour<<"'<<t2.minute?":"<<t2.sec<<endl;〃輸出t2中數據成員的值
return0;
)
運行情況如下:
103243/
10:32:43
223243/
22:32:43
程序是清晰易懂的,但是在主函數中對不同的對象一一寫出有關操作,會
使程序冗長。為了
解決這個問題,可以使用函數來進行輸入和輸出。見程序(b)。
(2)程序(b)
#include<iostream>
usingnamespacestd;
classTime
{public:
inthour;
intminute;
intsec;
};
intmain()
{
voidset_time(Time&);〃函數聲明
voidshow_time(Time&);〃函數聲明
Timetl;〃定義tl為Time類對象
1);〃調用set_time函數,向11對象中的數據成員輸入數據
show_time(t1);〃調用show_time函數,輸出11對象中的數據
Timet2;〃定義t2為Time類對象
set_time(t2);〃調用set_time函數,向t2對象中的數據成員輸入數據
show_time(t2);〃調用show_time函數,輸出t2對象中的數據
return0;
)
voidset_time(Time&t)〃定義函數settime,形參t是弓I用變量
{
cin?t.hour;〃輸入設定的時間
cin?t.minute;
cin?t.sec;
)
voidshow_time(Time&t)〃定義函數show_time,形參t是引用變量
(
cout?t.hour?zz:rr?t.minute?,r:n?t.sec?endl;//W出對象中的數據
)
運行情況與程序(a)相同。
(3)程序(c)
可以對上面的程序作一些修改,數據成員的值不再由鍵盤輸入,而在調用
函數時由實參給出,并在函數中使用默認參數。將程序(b)第8行以下部分改為
intmain()
{
voidsettime(Time&,inthour=0,int
minute=0,intsec=0);〃函數聲明
voidshow_time(Time&);〃函數聲明
Timetl;
set_time(tl,12,23,34);//通過實參傳遞時、分、秒的值
showtime(tl);
Timet2;
set_time(t2);〃使用默認的時、分、秒的值
show_time(t2);
return0;
}
voidset_time(Time&t,inthour,intminute,intsec)
{
t.hour=hour;
t.minute=minute;
t.sec=sec;
)
voidshowtime(Time&t)
{
cout?t.hour?,r//?t.minute?z/:,r?t.sec?endl;
)
程序運行時的輸出為
12:23:34⑴中的時、分、秒)
0:0:0(t2中的時、分、秒)
以上兩個程序中定義的類都只有數據成員,沒有成員函數,這顯然沒有體
現出使用類的優越性。在下面的例子中,類體中就包含了成員函數。
例8.3將例8.2的程序改用含成員函數的類來處理。
#include<iostream>
usingnamespacestd;
classTime
{public:
voidset_time();〃公用成員函數
voidshow_time();〃公用成員函數
private:〃數據成員為私有
inthour;
intminute;
intsec;
);
intmain()
(
Time11;//定義對象”
tl.set_time();〃調用對象tl的成員函數settime,向tl的數據成員輸入數據
tl.show_time();〃調用對象tl的成員函數show_time,輸出tl的數據成員的值
Timet2;〃定義對象t2
t2.set_time();〃調用對象t2的成員函數sejtime,向t2的數據成員輸入數據
t2.show_time();〃調用對象t2的成員函數showjime,輸出t2的數據成員的值
return0;
void萬010::50匚日016()〃在類外定義50匚山11?函數
cin?hour;
cin?minute;
cin?sec;
}
voidTime::show_time()〃在類夕卜定義showtime函數
(
cout?hour〈v":"vvminute〈v":"vvsec?endl;
}
運行情況與例8.2中的程序(a)相同o
注意:
(1)在主函數中調用兩個成員函數時,應指明對象名(tl,t2)。表示調用的是
哪一個對象的成員函數。
(2)在類外定義函數時,應指明函數的作用域(如voidTime::set_time())。在
成員函數引用本對象的數據成員時,只需直接寫數據成員名,這時C++系統會把
它默認為本對象的數據成員。也可以顯式地寫出類名并使用域運算符。
(3)應注意區分什么場合用域運算符“::”,什么場合用成員運算符“二不要
搞混。
例8.4找出一個整型數組中的元素的最大值。這個問題可以不用類的方法來
解決,現在用類來處理,讀者可以比較不同方法的特點。
#include<iostream>
usingnamespacestd;
classArray_max〃聲明類
{public:〃以下3行為成員函數原型聲明
voidset_value();〃對數組元素設置值
voidmax_value();〃找出數組中的最大元素
voidshow_value();〃輸出最大值
private:
intarray[10];〃整型數組
intmax;//max用來存放最大值
};
voidArray_max::set_value()〃成員函數定義,向數組元素輸入數值
{inti;
for(i=0;i<10;i++)
cin?array[i];
}
voidArraymaxmax_value()//成員函數定義,找數組元素中
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 照明設備能效評估與改進措施考核試卷
- 生命體征測量技術 2
- 二年級奧數-二年級測試(二)A卷-帶答案
- 江西省上饒市藍天教育集團2023-2024學年高一下學期期中考試數學試題【含答案】
- 威海職業學院《計算機輔助翻譯》2023-2024學年第一學期期末試卷
- 吉林省長春新區重點達標名校2025年初三下學期第四次階段檢測試題生物試題含解析
- 上海市寶山區通河中學2025年高三下學期期初自測語文試題含解析
- 上饒師范學院《人體解剖生理學(生理)》2023-2024學年第二學期期末試卷
- 武漢工商學院《城市道路與交通》2023-2024學年第二學期期末試卷
- 攀枝花攀西職業學院《方案快速設計與表現實驗》2023-2024學年第一學期期末試卷
- 咯血-護理查房課件
- 安全用電-觸電與急救課件
- 初三任務型閱讀公開課一等獎省優質課大賽獲獎課件
- 公司組織架構圖(可編輯模版)
- 激光跟蹤儀使用手冊
- 貨物采購服務方案
- 初中英語 滬教牛津版 8B U6-1 Reading Head to head 課件
- DB11-T 1322.64-2019 安全生產等級評定技術規范 第64部分:城鎮供水廠
- 部編版小學五年級下冊語文說明科普類10篇閱讀試題(帶答案)
- 復變函數與積分變換第三章復變函數的積分
- (完整word版)西南財經大學管理學考博真題考點匯總和復習框架總結,推薦文檔
評論
0/150
提交評論