版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第8章類和對象第9章關(guān)于類和對象的進(jìn)一步討論第10章運(yùn)算符重載第3篇
基于對象的程序設(shè)計(jì)第8章類和對象8.1面向?qū)ο蟪绦蛟O(shè)計(jì)方法概述8.2類的聲明和對象的定義8.3類的成員函數(shù)8.4對象成員的引用8.5類的封裝性和信息隱蔽8.6類和對象的簡單應(yīng)用舉例到目前為止,我們介紹的是C++在面向過程的程序設(shè)計(jì)中的應(yīng)用。對于規(guī)模比較小的程序,編程者可以直接編寫出一個面向過程的程序,詳細(xì)地描述每一瞬時的數(shù)據(jù)結(jié)構(gòu)及對其的操作過程。但是當(dāng)程序規(guī)模較大時,就顯得力不從心了。C++就是為了解決編寫大程序過程中的困難而產(chǎn)生的。8.1面向?qū)ο蟪绦蛟O(shè)計(jì)方法概述面向?qū)ο蟮某绦蛟O(shè)計(jì)的思路和人們?nèi)粘I钪刑幚韱栴}的思路是相似的。在自然世界和社會生活中,一個復(fù)雜的事物總是由許多局部組成的。當(dāng)人們生產(chǎn)汽車時,分別設(shè)計(jì)和制造發(fā)動機(jī)、底盤、車身和輪子,最后把它們組裝在一起。在組裝時,各局部之間有一定的聯(lián)系,以便協(xié)調(diào)工作。這就是面向?qū)ο蟮某绦蛟O(shè)計(jì)的根本思路。為了進(jìn)一步說明問題,下面先討論幾個有關(guān)的概念。8.1.1什么是面向?qū)ο蟮某绦蛟O(shè)計(jì)1.對象客觀世界中任何一個事物都可以看成一個對象〔object〕。對象可大可小。對象是構(gòu)成系統(tǒng)的根本單位。任何一個對象都應(yīng)當(dāng)具有這兩個要素,即屬性〔attribute〕和行為〔behavior〕,它能根據(jù)外界給的信息進(jìn)行相應(yīng)的操作。一個對象往往是由一組屬性和一組行為構(gòu)成的。一般來說,但凡具備屬性和行為這兩種要素的,都可以作為對象。在一個系統(tǒng)中的多個對象之間通過一定的渠道相互聯(lián)系,如圖8.1示意。要使某一個對象實(shí)現(xiàn)某一種行為〔即操作〕,應(yīng)當(dāng)向它傳送相應(yīng)的消息。對象之間就是這樣通過發(fā)送和接收消息互相聯(lián)系的。圖8.1圖8.2面向?qū)ο蟮某绦蛟O(shè)計(jì)采用了以上人們所熟悉的這種思路。使用面向?qū)ο蟮某绦蛟O(shè)計(jì)方法設(shè)計(jì)一個復(fù)雜的軟件系統(tǒng)時,首要的問題是確定該系統(tǒng)是由哪些對象組成的,并且設(shè)計(jì)這些對象。在C++中,每個對象都是由數(shù)據(jù)和函數(shù)〔即操作代碼〕這兩局部組成的,見圖8.2。數(shù)據(jù)表達(dá)了前面提到的“屬性”,如一個三角形對象,它的3個邊長就是它的屬性。函數(shù)是用來對數(shù)據(jù)進(jìn)行操作的,以便實(shí)現(xiàn)某些功能,例如可以通過邊長計(jì)算出三角形的面積,并且輸出三角形的邊長和面積。計(jì)算三角形面積和輸出有關(guān)數(shù)據(jù)就是前面提到的行為,在程序設(shè)計(jì)方法中也稱為方法〔method〕。調(diào)用對象中的函數(shù)就是向該對象傳送一個消息〔message〕,要求該對象實(shí)現(xiàn)某一行為〔功能〕。2.封裝與信息隱蔽可以對一個對象進(jìn)行封裝處理,把它的一局部屬性和功能對外界屏蔽,也就是說從外界是看不到的,甚至是不可知的。這樣做的好處是大大降低了操作對象的復(fù)雜程度。面向?qū)ο蟪绦蛟O(shè)計(jì)方法的一個重要特點(diǎn)就是“封裝性”〔encapsulation〕,所謂“封裝”,指兩方面的含義:一是將有關(guān)的數(shù)據(jù)和操作代碼封裝在一個對象中,形成一個根本單位,各個對象之間相對獨(dú)立,互不干擾。二是將對象中某些局部對外隱蔽,即隱蔽其內(nèi)部細(xì)節(jié),只留下少量接口,以便與外界聯(lián)系,接收外界的消息。這種對外界隱蔽的做法稱為信息隱蔽〔imformationhiding〕。信息隱蔽還有利于數(shù)據(jù)平安,防止無關(guān)的人了解和修改數(shù)據(jù)。C++的對象中的函數(shù)名就是對象的對外接口,外界可以通過函數(shù)名來調(diào)用這些函數(shù)來實(shí)現(xiàn)某些行為〔功能〕。這些將在以后詳細(xì)介紹。3.抽象在程序設(shè)計(jì)方法中,常用到抽象〔abstraction〕這一名詞。抽象的過程是將有關(guān)事物的共性歸納、集中的過程。抽象的作用是表示同一類事物的本質(zhì)。C和C++中的數(shù)據(jù)類型就是對一批具體的數(shù)的抽象。對象是具體存在的,如一個三角形可以作為一個對象,10個不同尺寸的三角形是10個對象。如果這10個三角形對象有相同的屬性和行為,可以將它們抽象為一種類型,稱為三角形類型。在C++中,這種類型就稱為“類〔class〕”。這10個三角形就是屬于同一“類”的對象。類是對象的抽象,而對象那么是類的特例,或者說是類的具體表現(xiàn)形式。4.繼承與重用如果在軟件開發(fā)中已經(jīng)建立了一個名為A的“類”,又想另外建立一個名為B的“類”,而后者與前者內(nèi)容根本相同,只是在前者的根底上增加一些屬性和行為,只需在類A的根底上增加一些新內(nèi)容即可。這就是面向?qū)ο蟪绦蛟O(shè)計(jì)中的繼承機(jī)制。利用繼承可以簡化程序設(shè)計(jì)的步驟?!鞍遵R”繼承了“馬”的根本特征,又增加了新的特征〔顏色〕,“馬”是父類,或稱為基類,“白馬”是從“馬”派生出來的,稱為子類或派生類。C++提供了繼承機(jī)制,采用繼承的方法可以很方便地利用一個已有的類建立一個新的類。這就是常說的“軟件重用”〔softwarereusability〕的思想。5.多態(tài)性如果有幾個相似而不完全相同的對象,有時人們要求在向它們發(fā)出同一個消息時,它們的反響各不相同,分別執(zhí)行不同的操作。這種情況就是多態(tài)現(xiàn)象。如,在Windows環(huán)境下,用鼠標(biāo)雙擊一個文件對象〔這就是向?qū)ο髠魉鸵粋€消息〕,如果對象是一個可執(zhí)行文件,那么會執(zhí)行此程序,如果對象是一個文本文件,那么啟動文本編輯器并翻開該文件。在C++中,所謂多態(tài)性〔polymorphism〕是指:由繼承而產(chǎn)生的相關(guān)的不同的類,其對象對同一消息會作出不同的響應(yīng)。多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計(jì)的一個重要特征,能增加程序的靈活性。傳統(tǒng)的面向過程程序設(shè)計(jì)是圍繞功能進(jìn)行的,用一個函數(shù)實(shí)現(xiàn)一個功能。所有的數(shù)據(jù)都是公用的,一個函數(shù)可以使用任何一組數(shù)據(jù),而一組數(shù)據(jù)又能被多個函數(shù)所使用〔見圖8.3〕。圖8.38.1.2面向?qū)ο蟪绦蛟O(shè)計(jì)的特點(diǎn)面向?qū)ο蟪绦蛟O(shè)計(jì)采取的是另外一種思路。它面對的是一個個對象。實(shí)際上,每一組數(shù)據(jù)都是有特定的用途的,是某種操作的對象。也就是說,一組操作調(diào)用一組數(shù)據(jù)。程序設(shè)計(jì)者的任務(wù)包括兩個方面:一是設(shè)計(jì)所需的各種類和對象,即決定把哪些數(shù)據(jù)和操作封裝在一起;二是考慮怎樣向有關(guān)對象發(fā)送消息,以完成所需的任務(wù)。這時他如同一個總調(diào)度,不斷地向各個對象發(fā)出命令,讓這些對象活動起來〔或者說激活這些對象〕,完成自己職責(zé)范圍內(nèi)的工作。各個對象的操作完成了,整體任務(wù)也就完成了。顯然,對一個大型任務(wù)來說,面向?qū)ο蟪绦蛟O(shè)計(jì)方法是十分有效的,它能大大降低程序設(shè)計(jì)人員的工作難度,減少出錯時機(jī)。類是C++中十分重要的概念,它是實(shí)現(xiàn)面向?qū)ο蟪绦蛟O(shè)計(jì)的根底。類是所有面向?qū)ο蟮恼Z言的共同特征,所有面向?qū)ο蟮恼Z言都提供了這種類型。一個有一定規(guī)模的C++程序是由許多類所構(gòu)成的。C++支持面向過程的程序設(shè)計(jì),也支持基于對象的程序設(shè)計(jì),又支持面向?qū)ο蟮某绦蛟O(shè)計(jì)。在本章到第10章將介紹基于對象的程序設(shè)計(jì)。包括類和對象的概念、類的機(jī)制和聲明、類對象的定義與使用等。這是面向?qū)ο蟮某绦蛟O(shè)計(jì)的根底。8.1.3類和對象的作用基于對象就是基于類。與面向過程的程序不同,基于對象的程序是以類和對象為根底的,程序的操作是圍繞對象進(jìn)行的。在此根底上利用了繼承機(jī)制和多態(tài)性,就成為面向?qū)ο蟮某绦蛟O(shè)計(jì)〔有時不細(xì)分基于對象程序設(shè)計(jì)和面向?qū)ο蟪绦蛟O(shè)計(jì),而把二者合稱為面向?qū)ο蟮某绦蛟O(shè)計(jì)〕?;趯ο蟪绦蛟O(shè)計(jì)所面對的是一個個對象。所有的數(shù)據(jù)分別屬于不同的對象。在面向過程的結(jié)構(gòu)化程序設(shè)計(jì)中,人們常使用這樣的公式來表述程序:程序=算法+數(shù)據(jù)結(jié)構(gòu)算法和數(shù)據(jù)結(jié)構(gòu)兩者是互相獨(dú)立、分開設(shè)計(jì)的,面向過程的程序設(shè)計(jì)是以算法為主體的。在實(shí)踐中人們逐漸認(rèn)識到算法和數(shù)據(jù)結(jié)構(gòu)是互相緊密聯(lián)系不可分的,應(yīng)當(dāng)以一個算法對應(yīng)一組數(shù)據(jù)結(jié)構(gòu),而不宜提倡一個算法對應(yīng)多組數(shù)據(jù)結(jié)構(gòu),以及一組數(shù)據(jù)結(jié)構(gòu)對應(yīng)多個算法?;趯ο蠛兔嫦?qū)ο蟪绦蛟O(shè)計(jì)就是把一個算法和一組數(shù)據(jù)結(jié)構(gòu)封裝在一個對象中。因此,就形成了新的觀念:對象=算法+數(shù)據(jù)結(jié)構(gòu)程序=〔對象+對象+對象+…〕+消息或:
程序=對象s+消息“對象s”表示多個對象。消息的作用就是對對象的控制。程序設(shè)計(jì)的關(guān)鍵是設(shè)計(jì)好每一個對象,及確定向這些對象發(fā)出的命令,使各對象完成相應(yīng)操作。隨著軟件規(guī)模的迅速增大,軟件人員面臨的問題十分復(fù)雜。需要標(biāo)準(zhǔn)整個軟件開發(fā)過程,明確軟件開發(fā)過程中每個階段的任務(wù),在保證前一個階段工作的正確性的情況下,再進(jìn)行下一階段的工作。這就是軟件工程學(xué)需要研究和解決的問題。面向?qū)ο蟮能浖こ贪ㄒ韵聨讉€局部:8.1.4面向?qū)ο蟮能浖_發(fā)1.面向?qū)ο蠓治觥瞣bjectorientedanalysis,OOA〕軟件工程中的系統(tǒng)分析階段,系統(tǒng)分析員要和用戶結(jié)合在一起,對用戶的需求作出精確的分析和明確的描述,從宏觀的角度概括出系統(tǒng)應(yīng)該做什么〔而不是怎么做〕。面向?qū)ο蟮姆治?,要按照面向?qū)ο蟮母拍詈头椒?,在對任?wù)的分析中,從客觀存在的事物和事物之間的關(guān)系,歸納出有關(guān)的對象〔包括對象的屬性和行為〕以及對象之間的聯(lián)系,并將具有相同屬性和行為的對象用一個類〔class〕來表示。建立一個能反映真實(shí)工作情況的需求模型。2.面向?qū)ο笤O(shè)計(jì)〔objectorienteddesign,OOD〕根據(jù)面向?qū)ο蠓治鲭A段形成的需求模型,對每一局部分別進(jìn)行具體的設(shè)計(jì),首先是進(jìn)行類的設(shè)計(jì),類的設(shè)計(jì)可能包含多個層次〔利用繼承與派生〕。然后以這些類為根底提出程序設(shè)計(jì)的思路和方法,包括對算法的設(shè)計(jì)。在設(shè)計(jì)階段,并不牽涉某一種具體的計(jì)算機(jī)語言,而是用一種更通用的描述工具〔如偽代碼或流程圖〕來描述。3.面向?qū)ο缶幊獭瞣bjectorientedprogramming,
OOP〕根據(jù)面向?qū)ο笤O(shè)計(jì)的結(jié)果,用一種計(jì)算機(jī)語言把它寫成程序,顯然應(yīng)中選用面向?qū)ο蟮挠?jì)算機(jī)語言〔例如C++〕,否那么無法實(shí)現(xiàn)面向?qū)ο笤O(shè)計(jì)的要求。4.面向?qū)ο鬁y試〔objectorientedtest,OOT〕在寫好程序后交給用戶使用前,必須對程序進(jìn)行嚴(yán)格的測試。測試的目的是發(fā)現(xiàn)程序中的錯誤并改正它。面向?qū)ο鬁y試是用面向?qū)ο蟮姆椒ㄟM(jìn)行測試,以類作為測試的根本單元。5.面向?qū)ο缶S護(hù)〔objectorientedsoftmaintenance,
OOSM〕因?yàn)閷ο蟮姆庋b性,修改一個對象對其他對象影響很小。利用面向?qū)ο蟮姆椒ňS護(hù)程序,大大提高了軟件維護(hù)的效率。現(xiàn)在設(shè)計(jì)一個大的軟件,是嚴(yán)格按照面向?qū)ο筌浖こ痰?個階段進(jìn)行的,這5個階段的工作不是由一個人從頭到尾完成的,而是由不同的人分別完成的。這樣,OOP階段的任務(wù)就比較簡單了,程序編寫者只需要根據(jù)OOD提出的思路用面向?qū)ο笳Z言編寫出程序即可。在一個大型軟件的開發(fā)中,OOP只是面向?qū)ο箝_發(fā)過程中的一個很小的局部。如果所處理的是一個較簡單的問題,可以不必嚴(yán)格按照以上5個階段進(jìn)行,往往由程序設(shè)計(jì)者按照面向?qū)ο蟮姆椒ㄟM(jìn)行程序設(shè)計(jì),包括類的設(shè)計(jì)〔或選用已有的類〕和程序的設(shè)計(jì)。每一個實(shí)體都是對象。有一些對象是具有相同的結(jié)構(gòu)和特性的。每個對象都屬于一個特定的類型。在C++中對象的類型稱為類〔class〕。類代表了某一批對象的共性和特征。前面已說明:類是對象的抽象,而對象是類的具體實(shí)例〔instance〕。正如同結(jié)構(gòu)體類型和結(jié)構(gòu)體變量的關(guān)系一樣,人們先聲明一個結(jié)構(gòu)體類型,然后用它去定義結(jié)構(gòu)體變量。同一個結(jié)構(gòu)體類型可以定義出多個不同的結(jié)構(gòu)體變量。8.2類的聲明和對象的定義
8.2.1類和對象的關(guān)系在C++中也是先聲明一個類類型,然后用它去定義假設(shè)干個同類型的對象。對象就是類類型的一個變量。可以說類是對象的模板,是用來定義對象的一種抽象類型。類是抽象的,不占用內(nèi)存,而對象是具體的,占用存儲空間。在一開始時弄清對象和類的關(guān)系是十分重要的。類是用戶自己指定的類型。如果程序中要用到類類型,必須自己根據(jù)需要進(jìn)行聲明,或者使用別人已設(shè)計(jì)好的類。C++標(biāo)準(zhǔn)本身并不提供現(xiàn)成的類的名稱、結(jié)構(gòu)和內(nèi)容。在C++中聲明一個類類型和聲明一個結(jié)構(gòu)體類型是相似的。下面是聲明一個結(jié)構(gòu)體類型的方法:structStudent//聲明了一個名為Student的結(jié)構(gòu)體類型{intnum;charname[20];charsex;};Studentstud1,stud2;//定義了兩個結(jié)構(gòu)體變量stud1和stud28.2.2聲明類類型它只包括數(shù)據(jù),沒有包括操作?,F(xiàn)在聲明一個類:classStudent//以class開頭{intnum;charname[20];charsex;//以上3行是數(shù)據(jù)成員voiddisplay〔〕//這是成員函數(shù){cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;//以上4行是函數(shù)中的操作語句}};Studentstud1,stud2;//定義了兩個Student類的對象stud1和stud2可以看到聲明類的方法是由聲明結(jié)構(gòu)體類型的方法開展而來的??梢钥吹?,類〔class〕就是對象的類型。實(shí)際上,類是一種廣義的數(shù)據(jù)類型。類這種數(shù)據(jù)類型中的數(shù)據(jù)既包含數(shù)據(jù),也包含操作數(shù)據(jù)的函數(shù)。不能把類中的全部成員與外界隔離,一般是把數(shù)據(jù)隱蔽起來,而把成員函數(shù)作為對外界的接口??梢詫⑸厦骖惖穆暶鞲臑閏lassStudent//聲明類類型{private://聲明以下局部為私有的intnum;charname[20];charsex;public://聲明以下局部為公用的voiddisplay〔〕{cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;}};Studentstud1,stud2;//定義了兩個Student類的對象如果在類的定義中既不指定private,也不指定public,那么系統(tǒng)就默認(rèn)為是私有的。歸納以上對類類型的聲明,可得到其一般形式如下:class類名{private:私有的數(shù)據(jù)和成員函數(shù);public:公用的數(shù)據(jù)和成員函數(shù);};private和public稱為成員訪問限定符〔memberaccessspecifier〕。除了private和public之外,還有一種成員訪問限定符protected〔受保護(hù)的〕,用protected聲明的成員稱為受保護(hù)的成員,它不能被類外訪問〔這點(diǎn)與私有成員類似〕,但可以被派生類的成員函數(shù)訪問。在聲明類類型時,聲明為private的成員和聲明為public的成員的次序任意,既可以先出現(xiàn)private局部,也可以先出現(xiàn)public局部。如果在類體中既不寫關(guān)鍵字private,又不寫public,就默認(rèn)為private。在一個類體中,關(guān)鍵字private和public可以分別出現(xiàn)屢次。每個局部的有效范圍到出現(xiàn)另一個訪問限定符或類體結(jié)束時〔最后一個右花括號〕為止。但是為了使程序清晰,應(yīng)該養(yǎng)成這樣的習(xí)慣:使每一種成員訪問限定符在類定義體中只出現(xiàn)一次。在以前的C++程序中,常先出現(xiàn)private局部,后出現(xiàn)public局部,如上面所示。現(xiàn)在的C++程序多數(shù)先寫public局部,把private局部放在類體的后部。這樣可以使用戶將注意力集中在能被外界調(diào)用的成員上,使閱讀者的思路更清晰一些。在C++程序中,經(jīng)??梢钥吹筋?。為了用戶方便,常用的C++編譯系統(tǒng)往往向用戶提供類庫〔但不屬于C++語言的組成局部〕,內(nèi)裝常用的根本的類,供用戶使用。不少用戶也把自己或本單位經(jīng)常用到的類放在一個專門的類庫中,需要用時直接調(diào)用,這樣就減少了程序設(shè)計(jì)的工作量。節(jié)的程序段中,最后一行用已聲明的Student類來定義對象,這種方法是很容易理解的。經(jīng)過定義后,stud1和stud2就成為具有Student類特征的對象。stud1和stud2這兩個對象都分別包括Student類中定義的數(shù)據(jù)和函數(shù)。定義對象也可以有幾種方法。8.2.3定義對象的方法1.先聲明類類型,然后再定義對象前面用的就是這種方法,如Studentstud1,stud2;//Student是已經(jīng)聲明的類類型在C++中,聲明了類類型后,定義對象有兩種形式?!?〕class類名對象名如classStudentstud1,stud2;把class和Student合起來作為一個類名,用來定義對象?!?〕類名對象名如Studentstud1,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;}stud1,stud2;//定義了兩個Student類的對象在定義Student類的同時,定義了兩個Student類的對象。3.不出現(xiàn)類名,直接定義對象class//無類名{private://聲明以下局部為私有的┆public://聲明以下局部為公用的┆}stud1,stud2;//定義了兩個無類名的類對象直接定義對象,在C++中是合法的、允許的,但卻很少用,也不提倡用。在實(shí)際的程序開發(fā)中,一般都采用上面3種方法中的第1種方法。在小型程序中或所聲明的類只用于本程序時,也可以用第2種方法。在定義一個對象時,編譯系統(tǒng)會為這個對象分配存儲空間,以存放對象中的成員。C++增加了class類型后,仍保存了結(jié)構(gòu)體類型〔struct〕,而且把它的功能也擴(kuò)展了。C++允許用struct來定義一個類型。如可以將前面用關(guān)鍵字class聲明的類類型改為用關(guān)鍵字struct:structStudent//用關(guān)鍵字struct來聲明一個類類型{private://聲明以下局部為私有的intnum;//以下3行為數(shù)據(jù)成員charname[20];charsex;public://聲明以下局部為公用的voiddisplay〔〕//成員函數(shù){cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;}};Studentstud1,stud2;//定義了兩個Student類的對象8.2.4類和結(jié)構(gòu)體類型的異同為了使結(jié)構(gòu)體類型也具有封裝的特征,C++不是簡單地繼承C的結(jié)構(gòu)體,而是使它也具有類的特點(diǎn),以便于用于面向?qū)ο蟪绦蛟O(shè)計(jì)。用struct聲明的結(jié)構(gòu)體類型實(shí)際上也就是類。用struct聲明的類,如果對其成員不作private或public的聲明,系統(tǒng)將其默認(rèn)為public。如果想分別指定私有成員和公用成員,那么應(yīng)用private或public作顯式聲明。而用class定義的類,如果不作private或public聲明,系統(tǒng)將其成員默認(rèn)為private,在需要時也可以自己用顯式聲明改變。如果希望成員是公用的,使用struct比較方便,如果希望局部成員是私有的,宜用class。建議盡量使用class來建立類,寫出完全表達(dá)C++風(fēng)格的程序。類的成員函數(shù)〔簡稱類函數(shù)〕是函數(shù)的一種,它的用法和作用和第4章介紹過的函數(shù)根本上是一樣的,它也有返回值和函數(shù)類型,它與一般函數(shù)的區(qū)別只是:它是屬于一個類的成員,出現(xiàn)在類體中。它可以被指定為private〔私有的〕、public〔公用的〕或protected〔受保護(hù)的〕。在使用類函數(shù)時,要注意調(diào)用它的權(quán)限〔它能否被調(diào)用〕以及它的作用域〔函數(shù)能使用什么范圍中的數(shù)據(jù)和函數(shù)〕。例如私有的成員函數(shù)只能被本類中的其他成員函數(shù)所調(diào)用,而不能被類外調(diào)用。8.3類的成員函數(shù)
8.3.1成員函數(shù)的性質(zhì)成員函數(shù)可以訪問本類中任何成員〔包括私有的和公用的〕,可以引用在本作用域中有效的數(shù)據(jù)。一般的做法是將需要被外界調(diào)用的成員函數(shù)指定為public,它們是類的對外接口。但應(yīng)注意,并非要求把所有成員函數(shù)都指定為public。有的函數(shù)并不是準(zhǔn)備為外界調(diào)用的,而是為本類中的成員函數(shù)所調(diào)用的,就應(yīng)該將它們指定為private。這種函數(shù)的作用是支持其他函數(shù)的操作,是類中其他成員的工具函數(shù)〔utilityfunction〕,類外用戶不能調(diào)用這些私有的工具函數(shù)。類的成員函數(shù)是類體中十分重要的局部。如果一個類中不包含成員函數(shù),就等同于C語言中的結(jié)構(gòu)體了,表達(dá)不出類在面向?qū)ο蟪绦蛟O(shè)計(jì)中的作用。在前面已經(jīng)看到成員函數(shù)是在類體中定義的。也可以在類體中只寫成員函數(shù)的聲明,而在類的外面進(jìn)行函數(shù)定義。如classStudent{public:voiddisplay〔〕;//公用成員函數(shù)原型聲明private:intnum;stringname;charsex;//以上3行是私有數(shù)據(jù)成員};voidStudent∷display〔〕//在類外定義display類函數(shù){cout<<″num:″<<num<<endl;//函數(shù)體cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;}Studentstud1,stud2;//定義兩個類對象8.3.2在類外定義成員函數(shù)注意:在類體中直接定義函數(shù)時,不需要在函數(shù)名前面加上類名,因?yàn)楹瘮?shù)屬于哪一個類是不言而喻的。但成員函數(shù)在類外定義時,必須在函數(shù)名前面加上類名,予以限定〔qualifed〕,“∷”是作用域限定符〔fieldqualifier〕或稱作用域運(yùn)算符,用它聲明函數(shù)是屬于哪個類的。如果在作用域運(yùn)算符“∷”的前面沒有類名,或者函數(shù)名前面既無類名又無作用域運(yùn)算符“∷”,如∷display〔〕或display〔〕那么表示display函數(shù)不屬于任何類,這個函數(shù)不是成員函數(shù),而是全局函數(shù),即非成員函數(shù)的一般普通函數(shù)。類函數(shù)必須先在類體中作原型聲明,然后在類外定義,也就是說類體的位置應(yīng)在函數(shù)定義之前,否那么編譯時會出錯。雖然函數(shù)在類的外部定義,但在調(diào)用成員函數(shù)時會根據(jù)在類中聲明的函數(shù)原型找到函數(shù)的定義〔函數(shù)代碼〕,從而執(zhí)行該函數(shù)。在類的內(nèi)部對成員函數(shù)作聲明,而在類體外定義成員函數(shù),這是程序設(shè)計(jì)的一種良好習(xí)慣。如果一個函數(shù),其函數(shù)體只有2~3行,一般可在聲明類時在類體中定義。多于3行的函數(shù),一般在類體內(nèi)聲明,在類外定義。關(guān)于內(nèi)置〔inline〕函數(shù),已在第4章第4.5節(jié)中作過介紹。類的成員函數(shù)也可以指定為內(nèi)置函數(shù)。在類體中定義的成員函數(shù)的規(guī)模一般都很小,而系統(tǒng)調(diào)用函數(shù)的過程所花費(fèi)的時間開銷相對是比較大的。調(diào)用一個函數(shù)的時間開銷遠(yuǎn)遠(yuǎn)大于小規(guī)模函數(shù)體中全部語句的執(zhí)行時間。為了減少時間開銷,如果在類體中定義的成員函數(shù)中不包括循環(huán)等控制結(jié)構(gòu),C++系統(tǒng)會自動將它們作為內(nèi)置〔inline〕函數(shù)來處理。也就是說,在程序調(diào)用這些成員函數(shù)時,并不是真正地執(zhí)行函數(shù)的調(diào)用過程〔如保存返回地址等處理〕,而是把函數(shù)代碼嵌入程序的調(diào)用點(diǎn)。這樣可以大大減少調(diào)用成員函數(shù)的時間開銷。8.3.3inline成員函數(shù)C++要求對一般的內(nèi)置函數(shù)要用關(guān)鍵字inline聲明,但對類內(nèi)定義的成員函數(shù),可以省略inline,因?yàn)檫@些成員函數(shù)已被隱含地指定為內(nèi)置函數(shù)。如classStudent{public:voiddisplay〔〕{cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;}private:intnum;stringname;charsex;};其中第3行voiddisplay〔〕也可以寫成inlinevoiddisplay〔〕將display函數(shù)顯式地聲明為內(nèi)置函數(shù)。以上兩種寫法是等效的。對在類體內(nèi)定義的函數(shù),一般都省寫inline。應(yīng)該注意的是:如果成員函數(shù)不在類體內(nèi)定義,而在類體外定義,系統(tǒng)并不把它默認(rèn)為內(nèi)置〔inline〕函數(shù),調(diào)用這些成員函數(shù)的過程和調(diào)用一般函數(shù)的過程是相同的。如果想將這些成員函數(shù)指定為內(nèi)置函數(shù),應(yīng)當(dāng)用inline作顯式聲明。如classStudent{public:inlinevoiddisplay〔〕;//聲明此成員函數(shù)為內(nèi)置函數(shù)private:intnum;stringname;charsex;};inlinevoidStudent∷display〔〕//在類外定義display函數(shù)為內(nèi)置函數(shù){cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;}在第4章第4.5節(jié)曾提到過,在函數(shù)的聲明或函數(shù)的定義兩者之一作inline聲明即可。值得注意的是:如果在類體外定義inline函數(shù),那么必須將類定義和成員函數(shù)的定義都放在同一個頭文件中〔或者寫在同一個源文件中〕,否那么編譯時無法進(jìn)行置換〔將函數(shù)代碼的拷貝嵌入到函數(shù)調(diào)用點(diǎn)〕。但是這樣做,不利于類的接口與類的實(shí)現(xiàn)別離,不利于信息隱蔽。雖然程序的執(zhí)行效率提高了,但從軟件工程質(zhì)量的角度來看,這樣做并不是好的方法。只有在類外定義的成員函數(shù)規(guī)模很小而調(diào)用頻率較高時,才將此成員函數(shù)指定為內(nèi)置函數(shù)。用類去定義對象時,系統(tǒng)會為每一個對象分配存儲空間。如果一個類包括了數(shù)據(jù)和函數(shù),要分別為數(shù)據(jù)和函數(shù)的代碼分配存儲空間。按理說,如果用同一個類定義了10個對象,那么就需要分別為10個對象的數(shù)據(jù)和函數(shù)代碼分配存儲單元,如圖8.4所示。圖8.48.3.4成員函數(shù)的存儲方式能否只用一段空間來存放這個共同的函數(shù)代碼段,在調(diào)用各對象的函數(shù)時,都去調(diào)用這個公用的函數(shù)代碼。如圖8.5所示。圖8.5顯然,這樣做會大大節(jié)約存儲空間。C++編譯系統(tǒng)正是這樣做的,因此每個對象所占用的存儲空間只是該對象的數(shù)據(jù)局部所占用的存儲空間,而不包括函數(shù)代碼所占用的存儲空間。如果聲明了一個類:classTime{public:inthour;intminute;intsec;voidset〔〕{cin>>a>>b>>c;}};可以用下面的語句來輸出該類對象所占用的字節(jié)數(shù):cout<<sizeof〔Time〕<<endl;輸出的值是12。這就證明了一個對象所占的空間大小只取決于該對象中數(shù)據(jù)成員所占的空間,而與成員函數(shù)無關(guān)。函數(shù)代碼是存儲在對象空間之外的。如果對同一個類定義了10個對象,這些對象的成員函數(shù)對應(yīng)的是同一個函數(shù)代碼段,而不是10個不同的函數(shù)代碼段。需要注意的是:雖然調(diào)用不同對象的成員函數(shù)時都是執(zhí)行同一段函數(shù)代碼,但是執(zhí)行結(jié)果一般是不相同的。不同的對象使用的是同一個函數(shù)代碼段,它怎么能夠分別對不同對象中的數(shù)據(jù)進(jìn)行操作呢?原來C++為此專門設(shè)立了一個名為this的指針,用來指向不同的對象。需要說明:〔1〕不管成員函數(shù)在類內(nèi)定義還是在類外定義,成員函數(shù)的代碼段都用同一種方式存儲?!?〕不要將成員函數(shù)的這種存儲方式和inline〔內(nèi)置〕函數(shù)的概念混淆?!?〕應(yīng)當(dāng)說明:常說的“某某對象的成員函數(shù)”,是從邏輯的角度而言的,而成員函數(shù)的存儲方式,是從物理的角度而言的,二者是不矛盾的。在程序中經(jīng)常需要訪問對象中的成員。訪問對象中的成員可以有3種方法:通過對象名和成員運(yùn)算符訪問對象中的成員;通過指向?qū)ο蟮闹羔樤L問對象中的成員;通過對象的引用變量訪問對象中的成員。8.4對象成員的引用例如在程序中可以寫出以下語句:stud1.num=1001;//假設(shè)num已定義為公用的整型數(shù)據(jù)成員表示將整數(shù)1001賦給對象stud1中的數(shù)據(jù)成員num。其中“.”是成員運(yùn)算符,用來對成員進(jìn)行限定,指明所訪問的是哪一個對象中的成員。注意不能只寫成員名而忽略對象名。訪問對象中成員的一般形式為對象名.成員名不僅可以在類外引用對象的公用數(shù)據(jù)成員,而且還可以調(diào)用對象的公用成員函數(shù),但同樣必須指出對象名,如8.4.1通過對象名和成員運(yùn)算符訪問對象中的成員stud1.display〔〕;//正確,調(diào)用對象stud1的公用成員函數(shù)display〔〕;//錯誤,沒有指明是哪一個對象的display函數(shù)由于沒有指明對象名,編譯時把display作為普通函數(shù)處理。應(yīng)該注意所訪問的成員是公用的〔public〕還是私有的〔private〕。只能訪問public成員,而不能訪問private成員,如果已定義num為私有數(shù)據(jù)成員,下面的語句是錯誤的:stud1.num=10101;//num是私有數(shù)據(jù)成員,不能被外界引用在類外只能調(diào)用公用的成員函數(shù)。在一個類中應(yīng)當(dāng)至少有一個公用的成員函數(shù),作為對外的接口,否那么就無法對對象進(jìn)行任何操作。在第7章第節(jié)中介紹了指向結(jié)構(gòu)體變量的指針,可以通過指針引用結(jié)構(gòu)體中的成員。用指針訪問對象中的成員的方法與此類似。如果有以下程序段:classTime{public://數(shù)據(jù)成員是公用的inthour;intminute;};Timet,*p;//定義對象t和指針變量pp=&t;//使p指向?qū)ο髏cout<<p->hour;//輸出p指向的對象中的成員hour在p指向t的前提下,p->hour,〔*p〕.hour和t.hour三者等價。8.4.2通過指向?qū)ο蟮闹羔樤L問對象中的成員如果為一個對象定義了一個引用變量,它們是共占同一段存儲單元的,實(shí)際上它們是同一個對象,只是用不同的名字表示而已。因此完全可以通過引用變量來訪問對象中的成員。如果已聲明了Time類,并有以下定義語句:Timet1;//定義對象t1Time&t2=t1;//定義Time類引用變量t2,并使之初始化為t1cout<<t2.hour;//輸出對象t1中的成員hour由于t2與t1共占同一段存儲單元〔即t2是t1的別名〕,因此t2.hour就是t1.hour。本章第8.6節(jié)的例8.2中的程序〔b〕,介紹的是引用變量作為形參的情況,讀者可以參考。8.4.3通過對象的引用變量來訪問對象中的成員從前面的介紹:C++通過類來實(shí)現(xiàn)封裝性,把數(shù)據(jù)和與這些數(shù)據(jù)有關(guān)的操作封裝在一個類中,或者說,類的作用是把數(shù)據(jù)和算法封裝在用戶聲明的抽象數(shù)據(jù)類型中。在聲明了一個類以后,用戶主要是通過調(diào)用公用的成員函數(shù)來實(shí)現(xiàn)類提供的功能〔例如對數(shù)據(jù)成員設(shè)置值,顯示數(shù)據(jù)成員的值,對數(shù)據(jù)進(jìn)行加工等〕。因此,公用成員函數(shù)是用戶使用類的公用接口〔publicinterface〕,或者說是類的對外接口。8.5類的封裝性和信息隱蔽
8.5.1公用接口與私有實(shí)現(xiàn)的別離當(dāng)然并不一定要把所有成員函數(shù)都指定為public〔公用〕的,但這時這些成員函數(shù)就不是公用接口了。在類外雖然不能直接訪問私有數(shù)據(jù)成員,但可以通過調(diào)用公用成員函數(shù)來引用甚至修改私有數(shù)據(jù)成員。用戶可以調(diào)用公用成員函數(shù)來實(shí)現(xiàn)某些功能,而這些功能是在聲明類時已指定的,用戶可以使用它們而不應(yīng)改變它們。實(shí)際上用戶往往并不關(guān)心這些功能是如何實(shí)現(xiàn)的細(xì)節(jié),而只需知道調(diào)用哪個函數(shù)會得到什么結(jié)果,能實(shí)現(xiàn)什么功能即可。通過成員函數(shù)對數(shù)據(jù)成員進(jìn)行操作稱為類的實(shí)現(xiàn),為了防止用戶任意修改公用成員函數(shù),改變對數(shù)據(jù)進(jìn)行的操作,往往不讓用戶看到公用成員函數(shù)的源代碼,顯然更不能修改它,用戶只能接觸到公用成員函數(shù)的目標(biāo)代碼〔詳見節(jié)〕。可以看到:類中被操作的數(shù)據(jù)是私有的,實(shí)現(xiàn)的細(xì)節(jié)對用戶是隱蔽的,這種實(shí)現(xiàn)稱為私有實(shí)現(xiàn)〔privateimplementation〕。這種“類的公用接口與私有實(shí)現(xiàn)的別離”形成了信息隱蔽。軟件工程的一個最根本的原那么就是將接口與實(shí)現(xiàn)別離,信息隱蔽是軟件工程中一個非常重要的概念。它的好處在于:〔1〕如果想修改或擴(kuò)充類的功能,只需修改本類中有關(guān)的數(shù)據(jù)成員和與它有關(guān)的成員函數(shù),程序中類外的局部可以不必修改?!?〕如果在編譯時發(fā)現(xiàn)類中的數(shù)據(jù)讀寫有錯,不必檢查整個程序,只需檢查本類中訪問這些數(shù)據(jù)的少數(shù)成員函數(shù)。在面向?qū)ο蟮某绦蜷_發(fā)中,一般做法是將類的聲明〔其中包含成員函數(shù)的聲明〕放在指定的頭文件中,用戶如果想用該類,只要把有關(guān)的頭文件包含進(jìn)來即可,不必在程序中重復(fù)書寫類的聲明,以減少工作量,節(jié)省篇幅,提高編程的效率。由于在頭文件中包含了類的聲明,因此在程序中就可以用該類來定義對象。由于在類體中包含了對成員函數(shù)的聲明,在程序中就可以調(diào)用這些對象的公用成員函數(shù)。為了實(shí)現(xiàn)上一節(jié)所表達(dá)的信息隱蔽,對類成員函數(shù)的定義一般不放在頭文件中,而另外放在一個文件中。8.5.2類聲明和成員函數(shù)定義的別離例如,可以分別寫兩個文件://student.h〔這是頭文件,在此文件中進(jìn)行類的聲明〕classStudent//類聲明{public:voiddisplay〔〕;//公用成員函數(shù)原型聲明private:intnum;charname[20];charsex;};//student.cpp//在此文件中進(jìn)行函數(shù)的定義#include<iostream>#include″student.h″//不要漏寫此行,否那么編譯通不過voidStudent∷display〔〕//在類外定義display類函數(shù){cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;}為了組成一個完整的源程序,還應(yīng)當(dāng)有包括主函數(shù)的源文件://main.cpp主函數(shù)模塊#include<iostream>#include″student.h″//將類聲明頭文件包含進(jìn)來intmain〔〕{Studentstud;//定義對象stud.display〔〕;//執(zhí)行stud對象的display函數(shù)return0;}這是一個包括3個文件的程序,組成兩個文件模塊:一個是主模塊main.cpp,一個是student.cpp。在主模塊中又包含頭文件student.h。在預(yù)編譯時會將頭文件student.h中的內(nèi)容取代#include″student.h″行。請注意:由于將頭文件student.h放在用戶當(dāng)前目錄中,因此在文件名兩側(cè)用雙撇號包起來〔″student.h″〕而不用尖括號〔<student.h>〕,否那么編譯時會找不到此文件。圖8.6。在運(yùn)行程序時調(diào)用stud中的display函數(shù),輸出各數(shù)據(jù)成員的值。如果一個類聲明屢次被不同的程序所選用,每次都要對包含成員函數(shù)定義的源文件〔如上面的student.cpp〕進(jìn)行編譯,這是否可以改進(jìn)呢?確實(shí),可以不必每次都對它重復(fù)進(jìn)行編譯,而只需編譯一次即可。把第一次編譯后所形成的目標(biāo)文件保存起來,以后在需要時把它調(diào)出來直接與程序的目標(biāo)文件相連接即可。這和使用函數(shù)庫中的函數(shù)是類似的。這也是把成員函數(shù)的定義不放在頭文件中的一個好處。在實(shí)際工作中,并不是將一個類聲明做成一個頭文件,而是將假設(shè)干個常用的功能相近的類聲明集中在一起,形成類庫。類庫有兩種:一種是C++編譯系統(tǒng)提供的標(biāo)準(zhǔn)類庫;一種是用戶根據(jù)自己的需要做成的用戶類庫,提供給自己和自己授權(quán)的人使用,這稱為自定義類庫。在程序開發(fā)工作中,類庫是很有用的,它可以減少用戶自己對類和成員函數(shù)進(jìn)行定義的工作量。類庫包括兩個組成局部:〔1〕類聲明頭文件;〔2〕已經(jīng)過編譯的成員函數(shù)的定義,它是目標(biāo)文件。用戶只需把類庫裝入到自己的計(jì)算機(jī)系統(tǒng)中〔一般裝到C++編譯系統(tǒng)所在的子目錄下〕,并在程序中用#include命令行將有關(guān)的類聲明的頭文件包含到程序中,就可以使用這些類和其中的成員函數(shù),順利地運(yùn)行程序。這和在程序中使用C++系統(tǒng)提供的標(biāo)準(zhǔn)函數(shù)的方法是一樣的,例如用戶在調(diào)用sin函數(shù)時只需將包含聲明此函數(shù)的頭文件包含到程序中,即可調(diào)用該庫函數(shù),而不必了解sin函數(shù)是怎么實(shí)現(xiàn)的〔函數(shù)值是怎樣計(jì)算出來的〕。當(dāng)然,前提是系統(tǒng)已裝了標(biāo)準(zhǔn)函數(shù)庫。在用戶源文件經(jīng)過編譯后,與系統(tǒng)庫〔是目標(biāo)文件〕相連接。在用戶程序中包含類聲明頭文件,類聲明頭文件就成為用戶使用類的公用接口,在頭文件的類體中還提供了成員函數(shù)的函數(shù)原型聲明,用戶只有通過頭文件才能使用有關(guān)的類。用戶看得見和接觸到的是這個頭文件,任何要使用這個類的用戶只需包含這個頭文件即可。包含成員函數(shù)定義的文件就是類的實(shí)現(xiàn)。請?zhí)貏e注意:類聲明和函數(shù)定義一般是分別放在兩個文本中的。由于要求接口與實(shí)現(xiàn)別離,為軟件開發(fā)商向用戶提供類庫創(chuàng)造了很好的條件。開發(fā)商把用戶所需的各種類的聲明按類放在不同的頭文件中,同時對包含成員函數(shù)定義的源文件進(jìn)行編譯,得到成員函數(shù)定義的目標(biāo)代碼。軟件商向用戶提供這些頭文件和類的實(shí)現(xiàn)的目標(biāo)代碼〔不提供函數(shù)定義的源代碼〕。用戶在使用類庫中的類時,只需將有關(guān)頭文件包含到自己的程序中,并且在編譯后連接成員函數(shù)定義的目標(biāo)代碼即可。由于類庫的出現(xiàn),用戶可以像使用零件一樣方便地使用在實(shí)踐中積累的通用的或?qū)S玫念?,這就大大減少了程序設(shè)計(jì)的工作量,有效地提高了工作效率。類的成員函數(shù)在面向?qū)ο蟪绦蚶碚撝斜环Q為“方法”〔method〕,“方法”是指對數(shù)據(jù)的操作。一個“方法”對應(yīng)一種操作。顯然,只有被聲明為公用的方法〔成員函數(shù)〕才能被對象外界所激活。外界是通過發(fā)“消息”來激活有關(guān)方法的。所謂“消息”,其實(shí)就是一個命令,由程序語句來實(shí)現(xiàn)。前面的stud.display〔〕;就是向?qū)ο髎tud發(fā)出的一個“消息”,通知它執(zhí)行其中的display“方法”〔即display函數(shù)〕。上面這個語句涉及3個術(shù)語:對象、方法和消息。stud是對象,display〔〕是方法,語句“stud.display〔〕;”是消息。8.5.3面向?qū)ο蟪绦蛟O(shè)計(jì)中的幾個名詞例8.1最簡單的例子。#include<iostream>usingnamespacestd;classTime//定義Time類{public://數(shù)據(jù)成員為公用的inthour;intminute;intsec;};intmain〔〕{Timet1;//定義t1為Time類對象cin>>t1.hour;//輸入設(shè)定的時間cin>>t1.minute;cin>>t1.sec;cout<<t1.hour<<″:″<<t1.minute<<″:″<<t1.sec<<endl;//輸出時間return0;}8.6類和對象的簡單應(yīng)用舉例運(yùn)行情況如下:123243↙12:32:43注意:〔1〕在引用數(shù)據(jù)成員hour,minute,sec時不要忘記在前面指定對象名?!?〕不要錯寫為類名,如寫成Time.hour,Time.minute,Time.sec是不對的。因?yàn)轭愂且环N抽象的數(shù)據(jù)類型,并不是一個實(shí)體,也不占存儲空間,而對象是實(shí)際存在的實(shí)體,是占存儲空間的,其數(shù)據(jù)成員是有值的,可以被引用的。〔3〕如果刪去主函數(shù)的3個輸入語句,即不向這些數(shù)據(jù)成員賦值,那么它們的值是不可預(yù)知的。例8.2引用多個對象的成員?!?〕程序〔a〕#include<iostream>usingnamespacestd;classTime{public:inthour;intminute;intsec;};intmain〔〕{Timet1;//定義對象t1cin>>t1.hour;//向t1的數(shù)據(jù)成員輸入數(shù)據(jù)cin>>t1.minute;cin>>t1.sec;cout<<t1.hour<<″:″<<t1.minute<<″:″<<t1.sec<<endl;//輸出t1中數(shù)據(jù)成員的值Timet2;//定義對象t2cin>>t2.hour;//向t2的數(shù)據(jù)成員輸入數(shù)據(jù)cin>>t2.minute;cin>>t2.sec;cout<<t2.hour<<″:″<<t2.minute<<″:″<<t2.sec<<endl;//輸出t2中數(shù)據(jù)成員的值return0;}運(yùn)行情況如下:103243↙10:32:43223243↙22:32:43程序是清晰易懂的,但是在主函數(shù)中對不同的對象一一寫出有關(guān)操作,會使程序冗長。為了解決這個問題,可以使用函數(shù)來進(jìn)行輸入和輸出。見程序〔b〕。〔2〕程序〔b〕#include<iostream>usingnamespacestd;classTime{public:inthour;intminute;intsec;};intmain〔〕{voidset_time〔Time&〕;//函數(shù)聲明voidshow_time〔Time&〕;//函數(shù)聲明Timet1;//定義t1為Time類對象set_time〔t1〕;//調(diào)用set_time函數(shù),向t1對象中的數(shù)據(jù)成員輸入數(shù)據(jù)show_time〔t1〕;//調(diào)用show_time函數(shù),輸出t1對象中的數(shù)據(jù)Timet2;//定義t2為Time類對象set_time〔t2〕;//調(diào)用set_time函數(shù),向t2對象中的數(shù)據(jù)成員輸入數(shù)據(jù)show_time〔t2〕;//調(diào)用show_time函數(shù),輸出t2對象中的數(shù)據(jù)return0;}voidset_time〔Time&t〕//定義函數(shù)set_time,形參t是引用變量{cin>>t.hour;//輸入設(shè)定的時間cin>>t.minute;cin>>t.sec;}voidshow_time〔Time&t〕//定義函數(shù)show_time,形參t是引用變量{cout<<t.hour<<″:″<<t.minute<<″:″<<t.sec<<endl;//輸出對象中的數(shù)據(jù)}運(yùn)行情況與程序〔a〕相同?!?〕程序〔c〕可以對上面的程序作一些修改,數(shù)據(jù)成員的值不再由鍵盤輸入,而在調(diào)用函數(shù)時由實(shí)參給出,并在函數(shù)中使用默認(rèn)參數(shù)。將程序〔b〕第8行以下局部改為intmain〔〕{voidset_time〔Time&,inthour=0,intminute=0,intsec=0〕;//函數(shù)聲明voidshow_time〔Time&〕;//函數(shù)聲明Timet1;set_time〔t1,12,23,34〕;//通過實(shí)參傳遞時、分、秒的值show_time〔t1〕;Timet2;set_time〔t2〕;//使用默認(rèn)的時、分、秒的值show_time〔t2〕;return0;}voidset_time〔Time&t,inthour,intminute,intsec〕{t.hour=hour;t.minute=minute;t.sec=sec;}voidshow_time〔Time&t〕{cout<<t.hour<<″:″<<t.minute<<″:″<<t.sec<<endl;}程序運(yùn)行時的輸出為12:23:34〔t1中的時、分、秒〕0:0:0〔t2中的時、分、秒〕以上兩個程序中定義的類都只有數(shù)據(jù)成員,沒有成員函數(shù),這顯然沒有表達(dá)出使用類的優(yōu)越性。在下面的例子中,類體中就包含了成員函數(shù)。例8.3將例8.2的程序改用含成員函數(shù)的類來處理。#include<iostream>usingnamespacestd;classTime{public:voidset_time〔〕;//公用成員函數(shù)voidshow_time〔〕;//公用成員函數(shù)private://數(shù)據(jù)成員為私有inthour;intminute;intsec;};intmain〔〕{Timet1;//定義對象t1t1.set_time〔〕;//調(diào)用對象t1的成員函數(shù)set_time,向t1的數(shù)據(jù)成員輸入數(shù)據(jù)t1.show_time〔〕;//調(diào)用對象t1的成員函數(shù)show_time,輸出t1的數(shù)據(jù)成員的值Timet2;//定義對象t2t2.set_time〔〕;//調(diào)用對象t2的成員函數(shù)set_time,向t2的數(shù)據(jù)成員輸入數(shù)據(jù)t2.show_time〔〕;//調(diào)用對象t2的成員函數(shù)show_time,輸出t2的數(shù)據(jù)成員的值return0;}voidTime∷set_time〔〕//在類外定義set_time函數(shù){cin>>hour;cin>>minute;cin>>sec;}voidTime∷show_time〔〕//在類外定義show_time函數(shù){cout<<hour<<″:″<<minute<<″:″<<sec<<endl;}運(yùn)行情況與例8.2中的程序〔a〕相同。注意:〔1〕在主函數(shù)中調(diào)用兩個成員函數(shù)時,應(yīng)指明對象名〔t1,t2〕。表示調(diào)用的是哪一個對象的成員函數(shù)?!?〕在類外定義函數(shù)時,應(yīng)指明函數(shù)的作用域〔如voidTime∷set_time〔〕〕。在成員函數(shù)引用本對象的數(shù)據(jù)成員時,只需直接寫數(shù)據(jù)成員名,這時C++系統(tǒng)會把它默認(rèn)為本對象的數(shù)據(jù)成員。也可以顯式地寫出類名并使用域運(yùn)算符?!?〕應(yīng)注意區(qū)分什么場合用域運(yùn)算符“∷”,什么場合用成員運(yùn)算符“.”,不要搞混。例8.4找出一個整型數(shù)組中的元素的最大值。這個問題可以不用類的方法來解決,現(xiàn)在用類來處理,讀者可以比較不同方法的特點(diǎn)。#include<iostream>usingnamespacestd;classArray_max//聲明類{public://以下3行為成員函數(shù)原型聲明voidset_value〔〕;//對數(shù)組元素設(shè)置值voidmax_value〔〕;//找出數(shù)組中的最大元素voidshow_value〔〕;//輸出最大值private:intarray[10];//整型數(shù)組intmax;//max用來存放最大值};voidArray_max∷set_value〔〕//成員函數(shù)定義,向數(shù)組元素輸入數(shù)值{inti;for〔i=0;i<10;i++〕cin>>array[i];}voidArray_max∷max_value〔〕//成員函數(shù)定義,找數(shù)組元素中的最大值{inti;max=array[0];for〔i=1;i<10;i++〕if〔array[i]>max〕max=array[i];}voidArray_max∷show_value〔〕//成員函數(shù)定義,輸出最大值{cout<<″max=″<<max;}intmain〔〕{Array_maxarrmax;//定義對象arrmaxarrmax.set_value〔〕;//調(diào)用arrmax的set_value函數(shù),向數(shù)組元素輸入數(shù)值arrmax.max_value〔〕;//調(diào)用arrmax的max_value函數(shù),找出數(shù)組元素中的最大值arrmax.show_value〔〕;//調(diào)用arrmax的show_value函數(shù),輸出數(shù)組元素中的最大值return0;}運(yùn)行結(jié)果如下:121239-3417134045-9176↙〔輸入10個元素的值〕max=134〔輸入10個元素中的最大值〕請注意成員函數(shù)定義與調(diào)用成員函數(shù)的關(guān)系,定義成員函數(shù)只是設(shè)計(jì)了一組操作代碼,并未實(shí)際執(zhí)行,只有在被調(diào)用時才真正地執(zhí)行這一組操作??梢钥闯觯褐骱瘮?shù)很簡單,語句很少,只是調(diào)用有關(guān)對象的成員函數(shù),去完成相應(yīng)的操作。在大多數(shù)情況下,主函數(shù)中甚至不出現(xiàn)控制結(jié)構(gòu)〔判斷結(jié)構(gòu)和循環(huán)結(jié)構(gòu)〕,而在成員函數(shù)中使用控制結(jié)構(gòu)。在面向?qū)ο蟮某绦蛟O(shè)計(jì)中,最關(guān)鍵的工作是類的設(shè)計(jì)。所有的數(shù)據(jù)和對數(shù)據(jù)的操作都表達(dá)在類中。只要把類定義好,編寫程序的工作就顯得很簡單了。第9章關(guān)于類和對象的進(jìn)一步討論9.1構(gòu)造函數(shù)9.2析構(gòu)函數(shù)9.3調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)的順序9.4對象數(shù)組9.5對象指針9.6共用數(shù)據(jù)的保護(hù)9.7對象的動態(tài)建立和釋放9.8對象的賦值和復(fù)制9.9靜態(tài)成員9.10友元9.11類模板在建立一個對象時,常常需要作某些初始化的工作,例如對數(shù)據(jù)成員賦初值。如果一個數(shù)據(jù)成員未被賦值,那么它的值是不可預(yù)知的,因?yàn)樵谙到y(tǒng)為它分配內(nèi)存時,保存了這些存儲單元的原狀,這就成為了這些數(shù)據(jù)成員的初始值。這種狀況顯然是與人們的要求不相符的,對象是一個實(shí)體,它反映了客觀事物的屬性〔例如時鐘的時、分、秒的值〕,是應(yīng)該有確定的值的。注意:類的數(shù)據(jù)成員是不能在聲明類時初始化的。9.1構(gòu)造函數(shù)
9.1.1對象的初始化如果一個類中所有的成員都是公用的,那么可以在定義對象時對數(shù)據(jù)成員進(jìn)行初始化。如classTime{public://聲明為公用成員hour;minute;sec;};Timet1={14,56,30};//將t1初始化為14:56:30這種情況和結(jié)構(gòu)體變量的初始化是差不多的,在一個花括號內(nèi)順序列出各公用數(shù)據(jù)成員的值,兩個值之間用逗號分隔。但是,如果數(shù)據(jù)成員是私有的,或者類中有private或protected的成員,就不能用這種方法初始化。在第8章的幾個例子中,是用成員函數(shù)來對對象中的數(shù)據(jù)成員賦初值的〔例如例8.3中的set_time函數(shù)〕。從例8.3中可以看到,用戶在主函數(shù)中調(diào)用set_time函數(shù)來為數(shù)據(jù)成員賦值。如果對一個類定義了多個對象,而且類中的數(shù)據(jù)成員比較多,那么,程序就顯得非常臃腫煩瑣。為了解決這個問題,C++提供了構(gòu)造函數(shù)〔constructor〕來處理對象的初始化。構(gòu)造函數(shù)是一種特殊的成員函數(shù),與其他成員函數(shù)不同,不需要用戶來調(diào)用它,而是在建立對象時自動執(zhí)行。構(gòu)造函數(shù)的名字必須與類名同名,而不能由用戶任意命名,以便編譯系統(tǒng)能識別它并把它作為構(gòu)造函數(shù)處理。它不具有任何類型,不返回任何值。構(gòu)造函數(shù)的功能是由用戶定義的,用戶根據(jù)初始化的要求設(shè)計(jì)函數(shù)體和函數(shù)參數(shù)。9.1.2構(gòu)造函數(shù)的作用例9.1在例8.3根底上定義構(gòu)造成員函數(shù)。#include<iostream>usingnamespacestd;classTime{public:Time〔〕//定義構(gòu)造成員函數(shù),函數(shù)名與類名相同{hour=0;//利用構(gòu)造函數(shù)對對象中的數(shù)據(jù)成員賦初值minute=0;sec=0;}voidset_time〔〕;//函數(shù)聲明voidshow_time〔〕;//函數(shù)聲明private:inthour;//私有數(shù)據(jù)成員intminute;intsec;};voidTime∷set_time〔〕//定義成員函數(shù),向數(shù)據(jù)成員賦值{cin>>hour;cin>>minute;cin>>sec;}voidTime∷show_time〔〕//定義成員函數(shù),輸出數(shù)據(jù)成員的值{cout<<hour<<″:″<<minute<<″:″<<sec<<endl;}intmain〔〕{Timet1;//建立對象t1,同時調(diào)用構(gòu)造函數(shù)t1.Time〔〕t1.set_time〔〕;//對t1的數(shù)據(jù)成員賦值t1.show_time〔〕;//顯示t1的數(shù)據(jù)成員的值Timet2;//建立對象t2,同時調(diào)用構(gòu)造函數(shù)t2.Time〔〕t2.show_time〔〕;//顯示t2的數(shù)據(jù)成員的值return0;}程序運(yùn)行的情況為:102554↙〔從鍵盤輸入新值賦給t1的數(shù)據(jù)成員〕10:25:54〔輸出t1的時、分、秒值〕0:0:0〔輸出t2的時、分、秒值〕上面是在類內(nèi)定義構(gòu)造函數(shù)的,也可以只在類內(nèi)對構(gòu)造函數(shù)進(jìn)行聲明而在類外定義構(gòu)造函數(shù)。將程序中的第4~7行改為下面一行:Time〔〕;//對構(gòu)造函數(shù)進(jìn)行聲明在類外定義構(gòu)造函數(shù):Time∷Time〔〕//在類外定義構(gòu)造成員函數(shù),要加上類名Time和域限定符“∷”{hour=0;minute=0;sec=0;}有關(guān)構(gòu)造函數(shù)的使用,有以下說明:〔1〕在類對象進(jìn)入其作用域時調(diào)用構(gòu)造函數(shù)?!?〕構(gòu)造函數(shù)沒有返回值,因此也不需要在定義構(gòu)造函數(shù)時聲明類型,這是它和一般函數(shù)的一個重要的不同之點(diǎn)?!?〕構(gòu)造函數(shù)不需用戶調(diào)用,也不能被用戶調(diào)用。〔4〕在構(gòu)造函數(shù)的函數(shù)體中不僅可以對數(shù)據(jù)成員賦初值,而且可以包含其他語句。但是一般不提倡在構(gòu)造函數(shù)中參加與初始化無關(guān)的內(nèi)容,以保持程序的清晰?!?〕如果用戶自己沒有定義構(gòu)造函數(shù),那么C++系統(tǒng)會自動生成一個構(gòu)造函數(shù),只是這個構(gòu)造函數(shù)的函數(shù)體是空的,也沒有參數(shù),不執(zhí)行初始化操作。在例9.1中構(gòu)造函數(shù)不帶參數(shù),在函數(shù)體中對數(shù)據(jù)成員賦初值。這種方式使該類的每一個對象都得到同一組初值〔例如例9.1中各數(shù)據(jù)成員的初值均為0〕。但是有時用戶希望對不同的對象賦予不同的初值??梢圆捎脦?shù)的構(gòu)造函數(shù),在調(diào)用不同對象的構(gòu)造函數(shù)時,從外面將不同的數(shù)據(jù)傳遞給構(gòu)造函數(shù),以實(shí)現(xiàn)不同的初始化。構(gòu)造函數(shù)首部的一般格式為構(gòu)造函數(shù)名〔類型1形參1,類型2形參2,…〕前面已說明:用戶是不能調(diào)用構(gòu)造函數(shù)的,因此無法采用常規(guī)的調(diào)用函數(shù)的方法給出實(shí)參。實(shí)參是在定義對象時給出的。定義對象的一般格式為類名對象名〔實(shí)參1,實(shí)參2,…〕;9.1.3帶參數(shù)的構(gòu)造函數(shù)例9.2有兩個長方柱,其長、寬、高分別為:〔1〕12,20,25;〔2〕10,14,20。求它們的體積。編一個基于對象的程序,在類中用帶參數(shù)的構(gòu)造函數(shù)。#include<iostream>usingnamespacestd;classBox{public:Box〔int,int,int〕;//聲明帶參數(shù)的構(gòu)造函數(shù)intvolume〔〕;//聲明計(jì)算體積的函數(shù)private:intheight;intwidth;intlength;};Box∷Box〔inth,intw,intlen〕//在類外定義帶參數(shù)的構(gòu)造函數(shù){height=h;width=w;length=len;}intBox∷volume〔〕//定義計(jì)算體積的函數(shù){return〔height*width*length〕;}intmain〔〕{Boxbox1〔12,25,30〕;//建立對象box1,并指定box1長、寬、高的值cout<<″Thevolumeofbox1is″<<box1.volume〔〕<<endl;Boxbox2〔15,30,21〕;//建立對象box2,并指定box2長、寬、高的值cout<<″Thevolumeofbox2is″<<box2.volume〔〕<<endl;return0;}程序運(yùn)行結(jié)果如下:Thevolumeofbox1is9000Thevolumeofbox2is9450可以知道:〔1〕帶參數(shù)的構(gòu)造函數(shù)中的形參,其對應(yīng)的實(shí)參在定義對象時給定?!?〕用這種方法可以方便地實(shí)現(xiàn)對不同的對象進(jìn)行不同的初始化。在節(jié)中介紹的是在構(gòu)造函數(shù)的函數(shù)體內(nèi)通過賦值語句對數(shù)據(jù)成員實(shí)現(xiàn)初始化。C++還提供另一種初始化數(shù)據(jù)成員的方法——參數(shù)初始化表來實(shí)現(xiàn)對數(shù)據(jù)成員的初始化。這種方法不在函數(shù)體內(nèi)對數(shù)據(jù)成員初始化,而是在函數(shù)首部實(shí)現(xiàn)。例如例9.2中定義構(gòu)造函數(shù)可以改用以下形式:Box∷Box〔inth,intw,intlen〕:height〔h〕,width〔w〕,length〔len〕{}這種寫法方便、簡練,尤其當(dāng)需要初始化的數(shù)據(jù)成員較多時更顯其優(yōu)越性。甚至可以直接在類體中〔而不是在類外〕定義構(gòu)造函數(shù)。9.1.4用參數(shù)初始化表對數(shù)據(jù)成員初始化在一個類中可以定義多個構(gòu)造函數(shù),以便對類對象提供不同的初始化的方法,供用戶選用。這些構(gòu)造函數(shù)具有相同的名字,而參數(shù)的個數(shù)或參數(shù)的類型不相同。這稱為構(gòu)造函數(shù)的重載。在第4章第4.6節(jié)中所介紹的函數(shù)重載的知識也適用于構(gòu)造函數(shù)。通過下面的例子可以了解怎樣應(yīng)用構(gòu)造函數(shù)的重載。9.1.5構(gòu)造函數(shù)的重載例9.3在例9.2的根底上,定義兩個構(gòu)造函數(shù),其中一個無參數(shù),一個有參數(shù)。#include<iostream>usingnamespacestd;classBox{public:Box〔〕;//聲明一個無參的構(gòu)造函數(shù)Box〔inth,intw,intlen〕:height〔h〕,width〔w〕,length〔len〕{}//聲明一個有參的構(gòu)造函數(shù),用參數(shù)的初始化表對數(shù)據(jù)成員初始化intvolume〔〕;private:intheight;intwidth;intlength;};Box∷Box〔〕
溫馨提示
- 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)目管理運(yùn)輸?shù)母倪M(jìn)案例分析資料
- 2024年長期租賃公司車輛協(xié)議精簡版版
- 2024年電子商務(wù)平臺技術(shù)升級與服務(wù)合同
- 2024消防工程驗(yàn)收報(bào)告編制合同3篇
- 蘇州2025年江蘇蘇州大學(xué)附屬第一醫(yī)院博士專項(xiàng)招聘55人筆試歷年典型考點(diǎn)(頻考版試卷)附帶答案詳解版
- 2024版詳細(xì)公路施工協(xié)議樣本版B版
- 2024年荒山植被恢復(fù)承包合同
- 2024幼兒園兒童心理輔導(dǎo)信息保密及倫理準(zhǔn)則協(xié)議3篇
- 小學(xué)三年級狀物作文650字(十篇)
- 2024廢品回收合作協(xié)議書
- 高等數(shù)學(xué)說課稿PPT課件(PPT 49頁)
- 單片機(jī)交通燈系統(tǒng)設(shè)計(jì)報(bào)告
- 標(biāo)桿房企人力資源體系研究之龍湖
- 規(guī)則大副貨運(yùn)知識點(diǎn)
- 《2022年上海市初中語文課程終結(jié)性評價指南》中規(guī)定的150個文言實(shí)詞
- 關(guān)于轉(zhuǎn)發(fā)《關(guān)于進(jìn)一步加強(qiáng)少先隊(duì)輔導(dǎo)員隊(duì)伍建設(shè)的若干意見》的通知
- 愛麗絲夢游仙境話劇中英文劇本(共6頁)
- 書法少年宮活動記錄
- 鐵路橋梁鋼結(jié)構(gòu)設(shè)計(jì)規(guī)范(TB100022--99)修訂簡介
- 水文氣象報(bào)告
- 應(yīng)急資金投入保障機(jī)制
評論
0/150
提交評論