《C++程序設(shè)計(jì)》課件第2章_第1頁
《C++程序設(shè)計(jì)》課件第2章_第2頁
《C++程序設(shè)計(jì)》課件第2章_第3頁
《C++程序設(shè)計(jì)》課件第2章_第4頁
《C++程序設(shè)計(jì)》課件第2章_第5頁
已閱讀5頁,還剩84頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第2章類(class)2.1類的概念 2.2隱藏實(shí)現(xiàn)

2.3訪問控制

2.4訪問控制 本章小結(jié)

習(xí)題

2.1類的概念面向?qū)ο蟮某绦蛟O(shè)計(jì)是一種程序設(shè)計(jì)技術(shù)。面向?qū)ο蟮某绦蛟O(shè)計(jì)語言的意義是:某種程序設(shè)計(jì)語言特別提供了一些機(jī)制,以很好地支持使用該語言進(jìn)行面向?qū)ο蟮某绦蛟O(shè)計(jì)。我們能在C中寫出面向?qū)ο蟮某绦颍@會非常的困難,因?yàn)樗恢苯又С诌@些機(jī)制。設(shè)計(jì)C++就是為了支持?jǐn)?shù)據(jù)抽象、面向?qū)ο蟮某绦蛟O(shè)計(jì)和通用型程序設(shè)計(jì),以及在這些風(fēng)格約束下的傳統(tǒng)的C程序設(shè)計(jì)技術(shù)。從本章開始,我們將介紹這些風(fēng)格。本章介紹類——?C++里的數(shù)據(jù)抽象,它與第1章介紹的內(nèi)部類型同樣方便。2.1.1面向?qū)ο笏枷朊嫦驅(qū)ο笳Z言中的類就是對真實(shí)世界中的種類的抽象描述。例如,“人類”就是真實(shí)世界中的一個種類,那么,什么是人類?怎么來描述人類?首先來看看人類所具有的一些特征,這個特征包括屬性(一些參數(shù)、數(shù)值,也就是后面要介紹的類的成員變量)以及方法(一些行為,他能干什么,也就是后面要介紹的類的方法)。每個人都具有身高、體重、年齡、血型等“屬性”以及會勞動、會直立行走、會用自己的頭腦去創(chuàng)造工具等“方法”。人之所以能區(qū)別于其他類型的動物,是因?yàn)槊總€人都具有人這個群體的屬性與方法。“人類”只是一個抽象的概念,它僅僅是一個概念,它是不存在的實(shí)體,但是所有具備“人類”這個群體的屬性與方法的對象都叫人。這個對象“人”是實(shí)際存在的實(shí)體。每個人都是人這個群體的一個對象。老虎為什么不是人?因?yàn)樗痪邆淙诉@個群體的基本特征(屬性與方法)?——老虎不會直立行走、不會使用工具等等,所以說老虎不是人。由此可見,類描述了一組有相同特性和相同行為的對象。在面向?qū)ο蟪绦蛑校悓?shí)際上就是數(shù)據(jù)類型,如整數(shù)、小數(shù)等。整數(shù)也有一組特性和行為:整數(shù)的特性為int,整數(shù)的行為有?+、-、×、/?等事件行為。面向過程的語言與面向?qū)ο蟮恼Z言的區(qū)別就在于,面向過程的語言不允許程序員自己定義數(shù)據(jù)類型,而只能使用程序中內(nèi)置的(已經(jīng)定義好的)數(shù)據(jù)類型。為了模擬真實(shí)世界,為了更好地解決問題,程序員往往需要自己創(chuàng)建解決問題所必需的數(shù)據(jù)類型,比如前面講的“人”這個數(shù)據(jù)類型。2.1.2類與對象的定義抽象數(shù)據(jù)類型就是將類以屬性和方法組合的方式進(jìn)行的抽象描述。面向?qū)ο蟮木幊陶Z言最大的特色就是可以定義自己所需的抽象數(shù)據(jù)類型,也就是上一節(jié)提到的類,以更好地解決問題。下面回顧一下“類”、“對象”、“屬性”、“方法”之間的關(guān)系。就像前面所說的,人這個“類”是什么也做不了的,因?yàn)椤叭祟悺敝皇且粋€抽象的概念,它不是實(shí)實(shí)在在的“東西”,而這個“東西”就是所謂的對象。只有人這個“對象”才能去工作。而類是對象的描述,對象從類中產(chǎn)生,對象具有類所描述的所有屬性與方法。比如電視機(jī)都有工作原理圖,那么什么叫電視機(jī)呢?只要能夠?qū)崿F(xiàn)電視工作原理圖的所有功能的物體,都叫電視機(jī)??墒牵娨暀C(jī)原理圖是不能工作的,也即這個原理圖不能收看節(jié)目,只有電視機(jī)這個“實(shí)體”,即所謂的“對象”才能收看節(jié)目。也就是說,從類生成對象之后才真正有意義,才能開始工作。此時(shí),電視機(jī)擁有電視原理圖所描述的所有屬性與方法。每一個實(shí)體都是對象。有一些對象具有相同的結(jié)構(gòu)和特性。每個對象都屬于一個特定的類型。在C++中對象的類型稱為類(class)。類代表了某一批對象的共性和特征。類是對象的抽象,而對象是類的具體實(shí)例(instance)。

C語言中的結(jié)構(gòu)體struct是類的基礎(chǔ),類就是由struct發(fā)展而來的。例如,C++中用戶可以這樣定義“人”(person)這個類://Person.husingnamespacestd;

classPerson{stringname; //姓名

intage; //年齡

charsex; //性別

//…};

可以看到“人”這個類是由多種內(nèi)部類型的數(shù)據(jù)組成的。在這個例子中,它包括stringname(姓名,字符型)、intage(年齡,整數(shù)型)和charsex(性別,字符型)三個數(shù)據(jù)類型。這個類是內(nèi)部類型的數(shù)據(jù)的集合,它和C語言中的結(jié)構(gòu)體struct很相似。但并不是所有的類都如此,某些類中可能會包含自定義的數(shù)據(jù)類型,并且那些包含自定義的數(shù)據(jù)類型的類在C++編程中是很常見的。

注意:類定義后面要加分號(;),而C里的struct定義后面不加。類和對象的關(guān)系就如同結(jié)構(gòu)體類型和結(jié)構(gòu)體變量的關(guān)系,結(jié)構(gòu)體中需要先聲明一個結(jié)構(gòu)體類型,然后用它去定義結(jié)構(gòu)體變量。在C++中也是先聲明一個類類型,然后用它去定義若干個同類型的對象。對象就是類類型的一個變量。

類定義的一般語法格式如下:

class類名

{public:

數(shù)據(jù)成員,成員函數(shù)的說明;

prutected:

數(shù)據(jù)成員,成員函數(shù)的說明;

Private:

數(shù)據(jù)成員,成員函數(shù)的說明;

};

先定義類,然后就可以定義對象。對象定義的一般語法格式如下:類名對象1,對象2;2.1.3成員變量及成員函數(shù)類的成員函數(shù)是函數(shù)的一種,它的用法和作用與C語言中的函數(shù)基本上是一樣的,它與一般函數(shù)的區(qū)別是:它屬于一個類的成員,出現(xiàn)在類體中;它可以被指定為private(私有的)、public(公用的)或protected(受保護(hù)的)。在使用類函數(shù)時(shí),要注意調(diào)用它的權(quán)限以及它的作用域。下面來看看寫的類能夠干什么以及是怎么工作的。例如,有一個員工叫“peter”,修改他的名字后對其進(jìn)行輸出。

【程序2.1】//Person.cpp#include<iostream>;#include<string>;usingnamespacestd;classPerson{ stringname; //姓名

intage; //年齡

charsex; //性別

};intmain(){ Personpeter; ="peter"; cout<<<<endl; return0;}

首先看看自定義的數(shù)據(jù)類型Person,在應(yīng)用時(shí)它和int類型的數(shù)據(jù)相似,兩者都需要創(chuàng)建變量(對象peter),只不過前者是自己定義的,而后者是基本數(shù)據(jù)類型。main()函數(shù)是整個函數(shù)的入口,int表示main()函數(shù)將要返回一個整型值,一般0表示程序正常結(jié)束,非0表示程序異常結(jié)束;也可以分別用EXIT_SUCCESS和EXIT_FAILURE表示0和非0值,這樣更易讀。“Personpeter;”定義一個Person類型的對象peter;“="peter";”試圖修改peter的name屬性,將其賦值為"peter";“cout<<<<endl;”試圖輸出peter的name屬性,endl表示換行。

編譯這段代碼,發(fā)現(xiàn)編譯器提示以下信息:

errorC2248:'name':cannotaccessprivatememberdeclaredinclass'Person'errorC2248:'name':cannotaccessprivatememberdeclaredinclass'Person'

定位出錯的語句,發(fā)現(xiàn)錯誤產(chǎn)自以下語句:

="peter";cout<<<<endl;

提示信息的意思是,程序無法訪問私有(private)變量name。上述第一句試圖修改name屬性,第二句試圖獲得name屬性并輸出。因此,錯誤信息是兩條。產(chǎn)生錯誤的原因是:類的屬性包括私有屬性和公有屬性。私有屬性是無法通過對象名直接獲得的,即類似于這種訪問方式是無效的。而公有屬性可以通過對象名直接訪問。那么,Person類中的屬性是私有的還是公有的呢?似乎沒有特別說明過。此處需要注意的是,在C++中,默認(rèn)的類的屬性的修飾符是private。如何修改這個錯誤呢?只需要將屬性定義為公有屬性就可以了。修改后的代碼如下:【程序2.2】classPerson{public: stringname; //姓名

intage; //年齡

charsex; //性別

};

這樣錯誤就被改正了。重新編譯程序并運(yùn)行,運(yùn)行結(jié)果如下:

由程序2.2可以看出,只是在屬性前加了語句“public:”,這樣它后面的屬性就被定義為公有屬性。前面介紹過,類是屬性與方法的集合。為了實(shí)現(xiàn)數(shù)據(jù)的封裝,提高數(shù)據(jù)的安全性,一般應(yīng)該把類的屬性聲明為私有的,而把類的方法聲明為公有的。這樣,對象能夠直接調(diào)用類中定義的所有方法,當(dāng)對象想要修改或得到自己的屬性時(shí)就必須調(diào)用已定義的專用的方法才能夠?qū)崿F(xiàn)。因此,我們提倡“對象調(diào)用方法,方法改變屬性”。也許有人會說,直接定義屬性為公有的,就可以省去寫公有方法的麻煩了。乍一看似乎有道理,其實(shí)不然。這樣的設(shè)計(jì)原則是有好處的,比如,類的屬性一般都有自己的取值范圍或變化規(guī)則,用成員方法來訪問就可以在成員方法里面加入這些限制和規(guī)則,使程序更加健壯。更極端的情況是某一屬性允許被訪問但不允許被修改,這樣的屬性聲明為私有屬性,只提供一個訪問該屬性的成員函數(shù)即可。例如上一節(jié)定義的Person類,將其中所有的屬性都修改為private,即定義為私有屬性,對這些屬性的讀取和修改只能用用戶提供的方法。對Person類的修改如下:

【程序2.3】//Person.husingnamespacestd;classPerson{private: stringname; //姓名 intage; //年齡

charsex; //性別

public: voidsetName(stringiniName) {//這個方法是修改員工的姓名

name=iniName; } stringgetName(){//這個方法是得到員工的姓名

returnname; } intgetAge(){//只允許訪問,提供get方法。而不允許修改,不提供set方法。

returnage; }};//Person.cpp#include<iostream>;#include"person.h";usingnamespacestd;intmain(){Personpeter; //定義一個Person的對象(最好用中文)peter.setName("peter"); //將對象名賦給perterstringp=peter.getName(); //獲取對象名

cout<<p<<endl;return0;}

運(yùn)行結(jié)果如下:

現(xiàn)在,Person這個類中包含許多屬性和方法。此時(shí),不能直接用創(chuàng)建好的對象調(diào)用它的屬性來進(jìn)行修改。因?yàn)樗膶傩允莗rivate類型的,是不能夠被直接引用從而進(jìn)行修改的。要想修改姓名就要用對象調(diào)用setName()方法,而要得到姓名就要調(diào)用getName()方法。這就是“對象調(diào)用方法,方法改變屬性”。下面來看看修改后的person.cpp文件?!?include<iostream>;”的意思是說下面的程序要用到iostream中已經(jīng)定義好的類來工作,比如屏幕輸出類的對象cout;“#include"person.h"”的意思是下面要用到定義的Person類了;“Personpeter;”定義一個Person類型的對象peter,“peter.setName("peter");”設(shè)定了peter這個對象的屬性name的值為"peter";“stringp=peter.getName();”獲得peter的屬性name的值,并賦給p變量,最后用“cout<<p<<endl;”打印出這個名字。

程序中對peter這個對象進(jìn)行操作才會有實(shí)際意義。千萬不要有這種想法:“試圖對類進(jìn)行操作!”這是毫無意義的。對象peter擁有了類所描述的所有的屬性及方法,下面一一列舉:

/*所有的Person對象都擁有這些屬性。每創(chuàng)建一個對象就會重新分配一塊內(nèi)存來存放相應(yīng)對象的這些屬性。每個對象都有自己“獨(dú)特”的一份。*/

private:stringname; //員工姓名

intage; //員工年齡

charsex; //員工性別/*所有的Person對象都擁有這些方法。但在內(nèi)存中只有一份*/public:voidsetName(stringiniName){//這個方法修改姓名

name=iniName;}stringgetName(){//這個方法得到姓名

returnname;}intgetAge(){//這個方法得到年齡

returnage;}

實(shí)際上在創(chuàng)建peter這個對象時(shí)計(jì)算機(jī)只給這個對象的所有的屬性分配了內(nèi)存,而并沒有給方法分配內(nèi)存。方法只有一個,是屬于所有的對象的,所以無論創(chuàng)建了多少個對象,計(jì)算機(jī)只會為一個方法分配一塊內(nèi)存。所有的方法其實(shí)都是一樣的,不必為每個對象都拷貝方法,但不同對象的相同屬性的值是不一樣的,所以必須給每個對象的屬性都分配內(nèi)存來保存這些不同的數(shù)據(jù)。

對于上面的實(shí)例,它已經(jīng)能完成絕大部分工作了,但它還是不完善的,還有許多的細(xì)節(jié)需要去完善。也許有的讀者已經(jīng)注意到了,當(dāng)創(chuàng)建完“peter”這個對象時(shí),這個對象的所有屬性都是空的,也就是說,這個對象的姓名是未定的、年齡是未定的、性別是未定的。而想把這些屬性都添加上去,就還要用對象調(diào)用相應(yīng)的方法,去一個一個地修改。有沒有什么好方法能夠在創(chuàng)建對象的同時(shí)完成對屬性的初始化呢?答案是肯定的,這就需要所謂的構(gòu)造函數(shù)。構(gòu)造函數(shù)是類中最特殊的函數(shù),它與析構(gòu)函數(shù)的功能正好相反。構(gòu)造函數(shù)具有以下特征:

(1)它沒有返回值類型;(2)它的名稱與類的名稱必須完全相同;

(3)可以對構(gòu)造函數(shù)進(jìn)行重載;

(4)它在創(chuàng)建對象時(shí)自動被調(diào)用。構(gòu)造函數(shù)可對類中的屬性進(jìn)行初始化工作。上面的程序沒有自己定義構(gòu)造函數(shù),在這種情況下,系統(tǒng)會自動定義一個“默認(rèn)構(gòu)造函數(shù)”。默認(rèn)構(gòu)造函數(shù)是沒有參數(shù)的構(gòu)造函數(shù)。如果程序員定義了構(gòu)造函數(shù),那么系統(tǒng)就不會再為你的程序添加一個默認(rèn)構(gòu)造函數(shù)了。(通常提倡自己定義構(gòu)造函數(shù),而不用系統(tǒng)的默認(rèn)構(gòu)造函數(shù)。)【程序2.4】//Person.hclassPerson{private: stringname; //姓名

intage; //年齡

charsex; //員工性別

public: Person(){//這個就是默認(rèn)構(gòu)造函數(shù)

name="peter"; //設(shè)置姓名 age=20; //設(shè)置年齡

sex="M"; //設(shè)置性別

} voidsetName(stringiniName){//這個方法是修改員工的姓名

name=iniName; } stringgetName(){//這個方法是得到員工的姓名

returnname; }intgetAge(){//只允許訪問,提供get方法。而不允許修改,不提供set方法

returnage;} //…};

在創(chuàng)建“peter”這個對象的同時(shí),它所有的屬性也被初始化了。顯然,這大大地提高了工作效率,但是,它還是不符合要求。想想看,如果現(xiàn)在創(chuàng)建這個類型的第二個對象,會發(fā)生什么事情?實(shí)際上,除了對象的“名”(這個名稱不是對象屬性中的name屬性,而是對象本身的名稱)不一樣外,其所有的“屬性值”都一樣。比如,現(xiàn)在創(chuàng)建第二個對象john,會發(fā)現(xiàn)這個對象的所有屬性和peter這個對象的所有屬性完全相同。因此只能再用對象的方法去改變寫屬性了。很顯然,這種方法不太好,需要一種在創(chuàng)建對象時(shí)為對象的屬性賦予“想要的值”的方法。默認(rèn)構(gòu)造函數(shù)就顯得無能為力了。需要的是帶參數(shù)的構(gòu)造函數(shù),在創(chuàng)建對象時(shí),把參數(shù)傳給構(gòu)造函數(shù),這樣就能完成上述功能?!境绦?.5】//Person.hclassPerson{private: stringname; //姓名

intage; //年齡

charsex; //員工性別

//…public:Person(stringiniName,intiniAge,chariniSex){//看這個構(gòu)造函數(shù)

name=iniName; //設(shè)置姓名

age=iniAge; //設(shè)置年齡

sex=iniSex; //設(shè)置性別

} //…};

這樣一來,在創(chuàng)建對象的同時(shí)就可以賦予想要的值,很顯然,這是方便而有效的。例如,“peter=Person("peter",20,'M');”,這樣,所有的工作都完成了(在創(chuàng)建對象的同時(shí)賦予了想要的“初值”)。2.2隱藏實(shí)現(xiàn)在整個設(shè)計(jì)和編碼過程中,都應(yīng)該貫穿著隱藏實(shí)現(xiàn)的思想,這是一個很重要的概念。將程序員分成類的創(chuàng)建者(classcreator,即創(chuàng)建新的數(shù)據(jù)類型的人)和客戶程序員(clientprogrammer,即使用這些類編程的人)能幫助更好地理解這種思想。客戶程序員最需要的是直接使用一個已經(jīng)實(shí)現(xiàn)功能的類,對他們來講,也許不知道這個類是如何實(shí)現(xiàn)的,但是這個類為他們提供了使用方法,使得他們能夠簡潔地去使用一個現(xiàn)成的東西。而對于創(chuàng)建者來說,源程序一般是不會向客戶提供的,他們只給客戶提供使用方法。就像手機(jī)一樣,他們不會給客戶說明這部手機(jī)是如何制造出來的,而客戶也只是關(guān)心這部手機(jī)該如何使用,創(chuàng)建者可以將它們的實(shí)現(xiàn)隱藏起來。這樣,創(chuàng)建者就有了自己的權(quán)利,也不用擔(dān)心程序會被惡意破壞和修改。當(dāng)客戶使用創(chuàng)建者的類時(shí),客戶的許多功能也許就是基于創(chuàng)建者的功能之上的,也許還會創(chuàng)建出更多的類,而這些類又可能會被其他人所使用。而最基本的東西,就是創(chuàng)建者原先提供給客戶的類。創(chuàng)建者必須去保護(hù)它,維護(hù)它的正確性,這樣才不會造成更大的錯誤。“接口”(Interface)是已創(chuàng)建好的類和客戶程序員的橋梁,客戶程序員必須遵照接口的規(guī)定去使用它。在實(shí)現(xiàn)的背后必然存放著某些代碼和數(shù)據(jù),這些代碼與那些隱藏起來的數(shù)據(jù)叫做“隱藏的實(shí)現(xiàn)”??蛻舫绦騿T只需使用接口去發(fā)送請求,隱藏起來的這些程序會幫他完成剩下的問題,而客戶不用去關(guān)心程序是如何實(shí)現(xiàn)的。在程序內(nèi)部,有許多數(shù)據(jù)是不想讓客戶使用的,可能是因?yàn)檫@個數(shù)據(jù)是一個輔助作用,只是為了完成自己的功能而創(chuàng)建的,它們沒有功能可以向客戶提供,所以也不必要讓客戶知道。也可能是因?yàn)檫@個數(shù)據(jù)是不應(yīng)該被使用的,使用它會造成錯誤,可能程序提供了其他的函數(shù)來使用它以避免錯誤,這樣它更不應(yīng)該對客戶開放。在這里舉個簡單的例子,讓讀者初步體會隱藏實(shí)現(xiàn)的作用和意義。在這里,對Person類做了簡化。先看程序2.6?!境绦?.6】#include<iostream>;#include<string>;usingnamespacestd;classPerson{private: intage; //年齡

public: voidsetAge(intinitAge){ age=initAge; }};intmain(){ Personpeter; Person*p=&peter; peter.setAge(21); void*add1=(void*)p; int*add2=(int*)add1; *add2=33; cout<<*add2<<endl; return0;}

運(yùn)行結(jié)果如下:

可以看到,在這個例子中,Person類只有setAge()成員方法,而沒有g(shù)etAge()成員方法。這樣設(shè)計(jì)的目的是不希望類的使用者獲得成員屬性age。然而,通過一種特殊的方法(指針),不但獲得了私有屬性age的值,而且還將age的屬性值修改為33。這種特殊的方法將在下一節(jié)“訪問控制”中詳細(xì)討論。這種指針的方法之所以能夠成功,很大程度上是因?yàn)轭惖氖褂谜呖梢粤私忸惖脑敿?xì)的定義內(nèi)容。在這個例子中,使用者知道Person類中含有一個int型的age屬性,所以才能夠通過整型的指針(int*)訪問和修改私有屬性age。為了避免這種情況出現(xiàn),可以將實(shí)現(xiàn)部分與接口部分分開,并將實(shí)現(xiàn)部分盡可能地隱藏起來。將程序2.6修改如下:【程序2.7】//Person.h#ifndefPERSON_H#definePERSON_HclassPerson{ structInformation; Information*p;public: voidinitialize(); intgetAge();voidsetAge(int);};#endif//PERSON_H//Person.cpp#include"Person.h";structPerson::Information{ intage;};voidPerson::initialize(){ p=newInformation; p->age=31;}intPerson::getAge(){ returnp->age;}voidPerson::setAge(intinitAge){ p->age=initAge;}

//UsePerson.cpp#include"Person.h"intmain(){ Personpeter; peter.initialize(); peter.setAge(10); peter.getAge(); return0;}

在這個例子中,將Person類分成了兩部分,分別是接口部分即Person.h和實(shí)現(xiàn)部分即Person.cpp。在頭文件Person.h中,只包含公共的接口和一個指針,這個指針指向一個沒有完全定義的類。所有程序員都能看到下面這行:

structInformation;

這只是一個不完全的類型說明或類聲明,它沒有提供有關(guān)該Information的任何細(xì)節(jié)。而類的實(shí)現(xiàn)部分被隱藏在Person.cpp中,并且Person.cpp不需要向程序的使用者提供。這樣使得安全性和可擴(kuò)展性都得到了提高。在任何項(xiàng)目中,都應(yīng)該盡可能地從隱藏實(shí)現(xiàn)的角度去考慮問題。C++語言采用三個顯式(明確)關(guān)鍵字即public、private和protected來設(shè)置類邊界。這樣能很好地保護(hù)和開放資源。這些關(guān)鍵字的使用和含義都是相當(dāng)直觀的,它們決定了誰能使用后續(xù)的定義內(nèi)容?!皃ublic”(公共)意味著后續(xù)的定義能被任何人所使用,將其開放出去。“private”(私有)意味著除類的創(chuàng)建者以及類的內(nèi)部函數(shù)成員,其他任何方式都不能訪問后續(xù)的定義信息。private很好的保護(hù)了私有變量,使得它們不被其他類所訪問?!皃rotected”(受保護(hù)的)與“private”相似,不同的是一個繼承的類可訪問受保護(hù)的成員,但不能訪問私有成員。有關(guān)繼承的詳細(xì)內(nèi)容將在第9章介紹。2.3訪問控制在C++中,用戶可以說明成員數(shù)據(jù)和成員函數(shù)的訪問級別。共有三種訪問級別:公共的(public)、保護(hù)的(protected)和私有的(private)。它們用于在結(jié)構(gòu)中設(shè)置邊界,被稱為訪問說明符(accessspecifier)。無論什么時(shí)候使用訪問說明符,后面都必須加上一個冒號。那么,設(shè)置這些訪問控制的理由是什么?理由有兩點(diǎn):一是信息的隱蔽,即以后將要提到的封裝,將類的內(nèi)部實(shí)現(xiàn)和外部接口分開,這樣當(dāng)客戶程序員要使用這個類時(shí)不需要關(guān)心類的內(nèi)部實(shí)現(xiàn)而只需要了解該類的接口。而且對類進(jìn)行維護(hù)時(shí)只需修改類的實(shí)現(xiàn)部分,基本上不用修改接口。二是數(shù)據(jù)的保護(hù),即將程序員認(rèn)為的類中的重要的信息保護(hù)起來,以免其他程序的不恰當(dāng)?shù)脑L問或不恰當(dāng)?shù)男薷摹?.3.1private

具有private訪問控制級別的成員是完全保密的,即只能通過指向當(dāng)前類(不包括派生類)的this指針才可以訪問,其他環(huán)境均無法直接訪問這個成員。如果有人企圖訪問一個私有成員,就會產(chǎn)生一個編譯錯誤。

例如下面的Person類。

【程序2.8】classPerson{private:stringname;public: stringgetName(){ returnname; //類的成員可以訪問類的私有成員

} voidsetName(stringinitName){//類的成員可以訪問類的私有成員

name=initName; }};

下面定義該類的一個實(shí)例:

Personperson;

其成員訪問的合法性如下:

; //非法,name為Person的私有成員類Person的私有成員name可以通過提供給外部訪問的接口getName()和setName()來訪問。2.3.2protected

具有protected訪問控制級別的成員是半公開的,外界無法直接訪問這個控制級別的成員,但是派生類的this指針可以獲得訪問能力。protected與private基本相似,只有在繼承時(shí)有較大的區(qū)別。繼承的類可以訪問protected成員,但是不能訪問private成員。有關(guān)繼承的詳細(xì)內(nèi)容,將在第9章介紹。2.3.3public

具有public訪問控制級別的成員是完全公開的,任何環(huán)境下都可以通過對象訪問這個成員。同樣稍微更改一下程序2.8中的Person類,改動后如程序2.9所示。

【程序2.9】classPerson{public: stringsex;public: stringgetSex(){ returnsex;//類的成員可以訪問類的公有成員

} voidsetSex(stringinitSex){//類的成員可以訪問類的公有成員

If(initSex!="male"&&initSex!="female"){ cout<<"Wronginput,Pleaseinputmaleorfemale!"<<endl; }else{sex=initSex; } }};

同樣定義該類的一個實(shí)例:

Personperson;

其成員訪問的合法性如下:

person.sex; //合法,sex為Person的公有成員類Person的公有成員sex還可以通過提供給外部訪問的接口getSex()和setSex()來訪問。

綜合以上三個例子可以看出,訪問指示符決定了跟在它后面的名稱的訪問級別,一直影響到下一個訪問指示符出現(xiàn)或類說明結(jié)束為止?,F(xiàn)在可以用一段簡單的并且完整的程序來體現(xiàn)訪問控制的作用。如下面的Person.cpp,為了避免累贅和重復(fù),只注釋了main函數(shù)中的語句。

【程序2.10】//Person.cpp//展示訪問控制的作用

#include<iostream>#include<string>usingnamespacestd;classPerson{public: stringsex;protected: intage;private: stringname;public: Person(){}stringgetSex() { returnsex; } intgetAge() { returnage; } stringgetName() { returnname; }voidsetSex(stringinitSex) {//類的成員可以訪問類的公有成員

if(initSex!="male"&&initSex!="female") { cout<<"Wronginput,Pleaseinputmaleorfemale!"<<endl; }else{ sex=initSex; } }voidsetAge(intinitAge) { age=initAge; } voidsetName(stringinitName) { name=initName; }};intmain(){ Personperson; //定義Person的一個對象(實(shí)例) person.sex="male"; //Person的實(shí)例person可以直接訪問Person中的公有成員sex //person.age=20; //Person的實(shí)例person不能直接訪問Person中的保護(hù)成員age person.setAge(20);//Person的實(shí)例person可以通過setAge()方法訪問私有成員age//="Tom"; //Person的實(shí)例person不能直接訪問Person中的私有成員name person.setName("Tom");//Person的實(shí)例person可以通過setName()方法訪問私有成員name cout<<"Thevalueofsexis"<<person.getSex()<<endl; cout<<"Thevalueofageis"<<person.getAge()<<endl;//Person的實(shí)例person可以通過getAge()方法訪問保護(hù)成員agecout<<"Thevalueofnameis"<<person.getName()<<endl;//Person的實(shí)例person可以通過getName()方法訪問私有成員name return0;}

運(yùn)行結(jié)果如下:

值得注意的是,訪問控制可以阻止用戶對對象進(jìn)行的無規(guī)劃的使用。但是在顯式地使用類型轉(zhuǎn)換時(shí),這種保護(hù)也會失去作用。如在上面的Person.cpp中假設(shè)沒有提供getAge()這個方法時(shí),也可以通過以下的方式得到Age的值。同理,name的值也可以用這種方法得到。由于這種方法用到了強(qiáng)制類型轉(zhuǎn)換,因此要謹(jǐn)慎使用。

Person*personPointer=&person;//取得Person實(shí)例在內(nèi)存中的地址

void*add1=(void*)personPointer;//先將地址強(qiáng)制轉(zhuǎn)換為void*型int*add2=(int*)add1;//先將地址強(qiáng)制轉(zhuǎn)換為int*型

cout<<"Nowthevalueofageis"<<*(add2+4)<<endl;//即使age是Person中的保護(hù)成員,仍然可以得到age的值以上程序片段在MicrosoftVisualC++中完全可以替代Person.cpp中的語句:

cout<<"Thevalueofageis"<<person.getAge()<<endl;

如果讀者使用的是其他的編譯器如GNU的gcc編譯器或者其他的操作系統(tǒng)如Linux,指針add2的偏移也許會不同,在調(diào)試時(shí)需要做相應(yīng)的改動。

一般來說,公有的成員是類的對外接口,而私有和保護(hù)成員是類的內(nèi)部實(shí)現(xiàn)并且不希望外部了解和訪問的。需要區(qū)分類的成員對類的實(shí)例(instant)的可見性和對類的成員函數(shù)的可見性的不同。類的成員函數(shù)可以訪問類的所有的成員,不存在任何限制。而其實(shí)例對類的成員的訪問則要受到上述所說的訪問控制符的制約。2.4訪問控制出于信息隱藏的目的,類的成員一般被定義為私有的,并通過公有的成員函數(shù)提供對類私有數(shù)據(jù)的訪問。但是有些時(shí)候,可能希望類外的函數(shù)可以訪問類的保護(hù)成員。為了解決這個問題,可以將這些函數(shù)定義為友元(friend)函數(shù)。友元包括友元函數(shù)和友元類。友元可以提高類的訪問效率,但會破壞類的封裝性。2.4.1友元函數(shù)有時(shí)候,希望通過類外部的某個函數(shù)去操作類的屬性。比如希望在這個函數(shù)中修改Person類的屬性值,并以特定的格式輸出Person類的信息。不希望在Person類中定義這樣的輸出函數(shù),而導(dǎo)致類變得臃腫和不易維護(hù)?!境绦?.11】#include<iostream>#include<string>usingnamespacestd;classPerson{private: stringname; intage;public: Person(stringinitName,intinitAge){ name=initName; age=initAge; } stringgetName(){ returnname; } intgetAge(){ returnage; }};//函數(shù)定義在類的外部

voidinformation(Personp){ p.age=20; cout<<<<"'sageis"<<p.age<<endl;}intmain(){ Personp("WangWei",23); cout<<p.getName()<<"'sageis"<<p.getAge()<<endl; information(p); return0;}

編譯這段代碼,出現(xiàn)下面的錯誤提示:

errorC2248:'age':cannotaccessprivatememberdeclaredinclass'Person'

錯誤產(chǎn)生在語句“p.age=20;”,原因是函數(shù)information不能訪問Person類中的私有成員age。在C++的設(shè)計(jì)規(guī)則中,類外部的函數(shù)是無法對類的屬性進(jìn)行操作的。但是,友元函數(shù)是一個例外。友元函數(shù)可以超脫類的訪問控制,具有訪問類中所有數(shù)據(jù)成員的能力。友元通過關(guān)鍵字friend聲明。它們不受其在類體中被聲明的public、private和protected區(qū)的影響。修改上面的代碼,把友元函數(shù)的聲明放在了類定義的末尾部分?!境绦?.12】classPerson{private: stringname; intage;public: Person(stringinitName,intinitAge){ name=initName; age=initAge; }stringgetName(){ returnname; } intgetAge(){ returnage; } friendvoidinformation(Personp);//友元函數(shù)的聲明

};

運(yùn)行結(jié)果如下:

可以看到,程序中通過兩種方式對Person類的私有成員name和age進(jìn)行了訪問。第一種通過成員函數(shù)訪問成員變量,由語句“cout<<p.getName()<<"'sageis"<<p.getAge()<<endl;”完成。第二種通過友元函數(shù)修改成員變量并進(jìn)行輸出,由語句“information(p);”完成。函數(shù)information被類Person聲明為友元函數(shù),通過這種方式函數(shù)information獲得了對類Person中所有成員的訪問權(quán)限。注意,友元函數(shù)并不能看做是類的成員函數(shù),它只是個被聲明為類的友元的普通函數(shù)。2.4.2嵌套友元下面回顧一下2.2.3節(jié)關(guān)于隱藏實(shí)現(xiàn)的例子,Person類的定義如下:

classPerson{ structInformation; Information*p;public: voidinitialize(); intgetAge(); voidsetAge(int);};

仔細(xì)觀察,在Person類中嵌套了一個結(jié)構(gòu)體Information。但是,結(jié)構(gòu)體不能封裝方法,可操作性較低。然而,類可以定義自己的方法。如果把結(jié)構(gòu)體Information改為類會如何呢?分析程序2.13的代碼:

【程序2.13】#include<iostream>#include<string>usingnamespacestd;classPerson{private: stringname;intage; public: Person(stringinitName,intinitAge){ name=initName; age=initAge; }

stringgetName(){ returnname; }voidsetName(stringinitName){ name=initName; }

intgetAge(){ returnage; } voidsetAge(intinitAge){ age=initAge; }classInformation{ private: Person*p; public: Information(Person*initp){ p=initp; } voidinfor(){ cout<<p->name<<"'sageis"<<p->age<<endl;} };};

在分析這段程序之前,先介紹一些嵌套類的相關(guān)知識。在一個類中定義的類稱為嵌套類,包含嵌套類的類稱為外圍類。例如,類B定義在類A的內(nèi)部,則類B為嵌套類,類A為外部類。這樣做的好處是隱藏類名,提高類的抽象能力,并且強(qiáng)調(diào)了兩個類的主從關(guān)系。

可以看到,類Information定義在類Person中。類Person是外圍類,類Information是嵌套類。類Information擁有一個私有成員Person*p,p是指向Person對象的指針變量。函數(shù)infor試圖通過指針p輸出Person對象的私有成員變量name和age。編譯這段代碼,將會出現(xiàn)以下錯誤信息:

'name':cannotaccessprivatememberdeclaredinclass'Person''age':cannotaccessprivatememberdeclaredinclass'Person'

錯誤產(chǎn)生在語句“cout<<p->name<<"'sageis"<<p->age<<endl;”中,這里提示編程人員函數(shù)information不具有訪問Person私有成員的權(quán)限。這是因?yàn)樵贑++中,嵌套類的成員函數(shù)對外圍類的成員沒有訪問權(quán)限,反之亦然。

如果將嵌套類聲明為外圍類的友元類,結(jié)果會如何呢?將代碼修改如下:

【程序2.14】classPerson{private: …public: … classInformation; friendInformation; classInfo

溫馨提示

  • 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)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論