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

下載本文檔

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

文檔簡(jiǎn)介

第7章名字空間與異常處理7.1模塊與接口的基本概念7.2名字空間7.3異常處理7.4綜合示例小結(jié)練習(xí)題

7.1模塊與接口的基本概念

任何一個(gè)設(shè)計(jì)、構(gòu)造良好的實(shí)用程序均是由若干模塊(Module)/構(gòu)件(Component)組成的,即使它是一個(gè)相對(duì)簡(jiǎn)單的程序。這意味著:

(1)可以將程序劃分成一組部件。

(2)程序模塊的劃分可以從多個(gè)角度來進(jìn)行:一是按自己寫的程序與它所調(diào)用的系統(tǒng)支撐功能(如標(biāo)準(zhǔn)庫(kù)函數(shù)或類庫(kù))來劃分;二是將自己寫的程序本身按某種原則(例如按功能)來進(jìn)行劃分,等等。

程序劃分是有目的的。將程序劃分成模塊,不僅能使其程序結(jié)構(gòu)更加清晰,而且其模塊更易于被替換與重用。

C++的名字空間(Namespace)和異常處理機(jī)制(ExceptionHanding)就是支持程序模塊化組織的強(qiáng)有力機(jī)制。

本章我們首先以小型桌面計(jì)算器為例,來具體闡述C++用于支持模塊化程序設(shè)計(jì)范型的主要機(jī)制——名字空間及相關(guān)問題。

邏輯上,我們可將小型桌面計(jì)算器按功能劃分為以下五大模塊。

(1)語(yǔ)法分析器(Parser):完成語(yǔ)言的語(yǔ)法分析;

(2)詞法分析器(Lexer):完成語(yǔ)言的詞法分析;

(3)符號(hào)表(SymbolTable):存儲(chǔ)用戶標(biāo)識(shí)符的名-值對(duì);

(4)驅(qū)動(dòng)程序(Driver):main函數(shù);

(5)錯(cuò)誤處理器(ErrorHandler):進(jìn)行程序的錯(cuò)誤處理。

其中,語(yǔ)法分析器、詞法分析器和符號(hào)表是該程序的三大核心部件。

程序中各模塊之間的劃分及模塊間的相互關(guān)系如圖7.1和圖7.2所示。圖中的箭頭表示模塊之間的調(diào)用關(guān)系。

通過前面的學(xué)習(xí)我們已經(jīng)知道,僅需了解一個(gè)函數(shù)接口的具體定義,而無(wú)需了解函數(shù)的具體實(shí)現(xiàn),我們就能夠很好地使用函數(shù)。圖7.1桌面計(jì)算器的程序邏輯劃分圖7.2桌面計(jì)算器的模塊關(guān)系圖類似地,即使程序的一個(gè)部件是由多個(gè)函數(shù)組成的,或者其中既有自定義類型也有全局變量、還有函數(shù),我們都可以這樣來設(shè)想:如果這樣的部件也像函數(shù)那樣有一個(gè)起包裝作用的接口,也同樣可以只需要了解接口而不需要了解實(shí)現(xiàn),就能夠很好地使用它,這正是信息隱藏原理的實(shí)質(zhì)與宗旨。

以此理念,圖7.2中程序各模塊之間調(diào)用的依賴關(guān)系我們可用圖7.3描述。圖7.3程序各模塊調(diào)用依賴圖圖7.3中各個(gè)模塊均調(diào)用錯(cuò)誤處理模塊,考慮圖的清晰性,各模塊對(duì)錯(cuò)誤處理模塊接口的調(diào)用我們暫未畫出。

從圖7.3可看出,各模塊調(diào)用時(shí)直接依賴的僅僅是所調(diào)用模塊的接口,而與其調(diào)用的實(shí)現(xiàn)無(wú)關(guān)。

確切地說,若程序中的一個(gè)部件具有明確的邊界,能夠?qū)崿F(xiàn)接口與實(shí)現(xiàn)的分離,并對(duì)它的用戶而言在使用時(shí)只需關(guān)心其接口而不管其實(shí)現(xiàn),該部件就叫做模塊(Module)。

實(shí)現(xiàn)模塊的接口與實(shí)現(xiàn)的分離,需要程序設(shè)計(jì)語(yǔ)言提供相應(yīng)的支持機(jī)制。C++提供的支持機(jī)制是Namespace和Class。

模塊用接口隱蔽了其中的數(shù)據(jù)和函數(shù)的處理細(xì)節(jié)(這也稱做封裝,Encapsulation),使得模塊可以在保持接口不變的前提下,可改變其數(shù)據(jù)結(jié)構(gòu)和函數(shù)的處理細(xì)節(jié)。

7.2名字空間

7.2.1名字空間的基本概念

C++中的名字空間(Namespace)是一種表現(xiàn)邏輯聚集關(guān)系的機(jī)制。換句話說,如果一些聲明(定義聲明與非定義聲明)在邏輯上都與某個(gè)劃分準(zhǔn)則有關(guān),就可以把這些聲明放入一個(gè)共同的名字空間中,以表現(xiàn)這一事實(shí)。

同一名字空間中的聲明在概念上屬于同一個(gè)邏輯實(shí)體。由于程序中的模塊就屬于這種邏輯實(shí)體,因而我們可用C++的名字空間來封裝模塊,將程序進(jìn)行模塊化組織。

C++的名字空間的語(yǔ)法形式為

namespace名字空間名

{

//邏輯相關(guān)的數(shù)據(jù)、函數(shù)、類或其它等

}

//注意:無(wú)“;”號(hào)

參照?qǐng)D7.1和第6章桌面計(jì)算器的代碼,若以模塊化的組織形式重構(gòu)桌面計(jì)算器,則其語(yǔ)法分析(parser)模塊為

namespaceParser

{

doubleexpr(bool);//非定義聲明

doubleprim(boolget){/*…*/}上面,我們僅僅是把一些邏輯相關(guān)的部分組織到一個(gè)名字空間中(模塊),并未實(shí)現(xiàn)模塊接口與其實(shí)現(xiàn)的分離。如何實(shí)現(xiàn)這種接口與實(shí)現(xiàn)的分離呢?關(guān)鍵是在其模塊實(shí)現(xiàn)部分中出現(xiàn)的成員被該名字空間的名字所約束(Qualified),這樣的約束通過約束符(Qualifier::,C++的作用域解析符)來表示。以桌面計(jì)算器的Parser模塊為例,進(jìn)行界面/接口與實(shí)現(xiàn)分離的程序代碼如下:從上述代碼可看出,接口與實(shí)現(xiàn)分離的要點(diǎn)是在每個(gè)模塊接口中僅需非定義聲明其中成員的接口部分,其模塊成員的實(shí)現(xiàn)(如數(shù)據(jù)、類的定義、函數(shù)的定義等)均應(yīng)以

名字空間名::成員

{

//…

}

的形式進(jìn)行名字空間外的定義聲明。C++語(yǔ)法規(guī)定,利用此形式只能進(jìn)行名字空間內(nèi)成員的定義聲明,而不能利用此語(yǔ)法新定義聲明一個(gè)名字空間成員。7.2.2名字空間中的名字解析

名字空間既是一個(gè)封裝體,也是一種作用域。一個(gè)名字空間中的名字(符號(hào)名)只能在其所定義的名字空間中可見、可用。

名字空間的另一主要用途是將不同的符號(hào)名組織到不同的名字空間中,以避免名字沖突。

名字沖突在大型軟件的開發(fā)過程中是一個(gè)突出問題。為了避免這個(gè)問題,在C++軟件開發(fā)過程中,我們應(yīng)有效地利用名字空間機(jī)制對(duì)軟件中的名字進(jìn)行有效的組織與管理。例如,開發(fā)中,我們可將自己的代碼組織到一個(gè)命名的名字空間中,以避免和系統(tǒng)類庫(kù)、其他程序員編制的程序中的名字發(fā)生沖突。在以上兩個(gè)名字空間mfc和owl中,雖然都有同名的變量名inflag,但由于它們屬于不同的名字空間,因此不產(chǎn)生名字沖突。

在另一個(gè)名字空間中解析其他名字空間的名字,可采取

名字空間名::名字

的方式進(jìn)行。例如:

mfc::inflag=3; //mfc中的inflag

owl::inflag=-256; //owl中的infalg

當(dāng)一個(gè)應(yīng)用程序中有多個(gè)模塊(或名字空間)時(shí),由于各個(gè)namespace之間經(jīng)常會(huì)出現(xiàn)互相使用對(duì)方成員的情況,如果每次使用就需用名字空間名進(jìn)行約束,將會(huì)導(dǎo)致程序書寫既繁瑣又容易出錯(cuò)。例如,在Parser模塊的term實(shí)現(xiàn)中,就用了一系列的名字解析(約束):為了避免這種繁瑣易錯(cuò)的名字解析,C++提供了幾種“有限的統(tǒng)一”約束機(jī)制。

1.using聲明語(yǔ)句

語(yǔ)法:using名字空間名::名字;

語(yǔ)義:將某一名字空間中的名字引入(Introduce)到一個(gè)局部范圍內(nèi),使其名字在該范圍內(nèi)無(wú)需名字空間的約束便可見、可用。

例如,在Parser模塊中,由于使用了若干using聲明語(yǔ)句,則在其namespace中我們就可直接采用其他namespace的名字。示例代碼如下:需要注意的是,若using聲明語(yǔ)句處于某一namespace的界面/接口中,則其using聲明語(yǔ)句有效于(作用于)該namespace的所有實(shí)現(xiàn)。例如,若我們?cè)赑arser名字空間中采用了如下所示的using聲明語(yǔ)句:則在parser模塊中的所有實(shí)現(xiàn)(prim、term和expr)中,get_token、curr_tok和error無(wú)需名字空間名約束便直接可見。我們還是以上述的prim實(shí)現(xiàn)為例:我們可看出,在上述代碼中所出現(xiàn)的其他namespace中的名字一律無(wú)需約束(解析),便能直接采用。

2.using指令語(yǔ)句

語(yǔ)法:usingnamespace名字空間名;

語(yǔ)義:將特定名字空間中的所有名字引入到一作用域內(nèi)。

我們已在前面各章節(jié)的示例程序中,多處見到了usingnamespacestd;語(yǔ)句。該語(yǔ)句的功用即將系統(tǒng)std名字空間的所有名字引入(展現(xiàn))到程序中。

值得注意的是,在一個(gè)namespace接口中使用using指令語(yǔ)句,其作用域?yàn)槠鋘amespace的所有實(shí)現(xiàn)中;若using指令語(yǔ)句用于某一個(gè)namespace的實(shí)現(xiàn)中,則其作用域?yàn)樵搶?shí)現(xiàn)內(nèi)。若我們?cè)趎amespace的Parser中采用了如下using指令語(yǔ)句:

namespaceParser{

doubleprim(bool);

doubleterm(bool);

doubleexpr(bool);

usingnamespaceLexer; //using指令語(yǔ)句

usingnamespaceError; //using指令語(yǔ)句

}

仍以Parser::prim為例,則在prim中出現(xiàn)的所有Lexer、Error模塊中的名字均無(wú)需其名字空間名的約束而直接呈現(xiàn)(顯露)給prim。請(qǐng)看其代碼:實(shí)際應(yīng)用中,是采用using聲明語(yǔ)句只將聲明的名字引入另一區(qū)域,還是用using指令語(yǔ)句將其名字空間中的所有名字引入到另一區(qū)域,應(yīng)具體情況具體分析。

需要切記的是:C++中的namespace是一個(gè)范圍。采用namespace,我們可以對(duì)程序進(jìn)行邏輯上的組織與劃分,程序越大,namespace的功能將愈強(qiáng)。

理想情況下,一個(gè)名字空間應(yīng)該具有以下特性:

(1)是一個(gè)描述了具有邏輯統(tǒng)一性特征的相關(guān)成員的集合;

(2)實(shí)現(xiàn)了接口與實(shí)現(xiàn)的分離,對(duì)用戶隱藏了namespace中的細(xì)節(jié);

(3)采用恰當(dāng)?shù)膗sing聲明語(yǔ)句或using指令語(yǔ)句,讓用戶免去任何明顯的名字描述負(fù)擔(dān)。7.2.3模塊的多重接口

采用C++的名字空間機(jī)制,我們可將程序以模塊化的形式組織起來。每一個(gè)模塊應(yīng)向調(diào)用者只暴露接口,而隱藏其實(shí)現(xiàn)。

一個(gè)模塊通常有不同類別的用戶。對(duì)不同的用戶而言,一個(gè)模塊應(yīng)向他們提供不同的接口。例如,某模塊的開發(fā)者能夠涉及模塊的全部成員,而這個(gè)模塊的普通用戶則只應(yīng)當(dāng)涉及對(duì)應(yīng)于對(duì)外功能的那些成員。因此,如果能夠?yàn)椴煌挠脩籼峁┎煌慕涌?,則該程序既友好又容易控制。又由于面向開發(fā)者的接口變動(dòng)的可能性通常會(huì)比面向普通用戶的接口更大,從這個(gè)意義上來講,為他們提供不同的接口,有利于隔離內(nèi)部變動(dòng)對(duì)外界的影響。因此,軟件開發(fā)中,一個(gè)模塊應(yīng)為不同的用戶提供不同的模塊接口。

我們?nèi)砸宰烂嬗?jì)算器為例來說明此問題。參看7.1節(jié)的圖7.1,對(duì)于Parser模塊而言,面對(duì)開發(fā)者,我們應(yīng)提供該模塊內(nèi)所有成員的接口,而對(duì)于Driver模塊而言,則只需提供Parser模塊中的expr函數(shù)的接口即可,因Driver只需看到expr的接口。

C++語(yǔ)法上允許為一個(gè)名字空間定義多個(gè)同空間名的接口。例如,對(duì)桌面計(jì)算器的Parser模塊而言,為Driver可定義如下接口:

namespaceParser

{

doubleexpr(bool);

}

對(duì)Parser的實(shí)現(xiàn)者(開發(fā)者)而言,亦可定義如下同空間名的接口:

namespaceParser

{

doubleprim(bool);

doubleterm(bool);

doubleexpr(bool);

usingLexer::get_token; //using聲明語(yǔ)句

usingLexer::curr_tok; //using聲明語(yǔ)句

usingError::error; //using聲明語(yǔ)句

}但實(shí)際的軟件開發(fā)中,若需要一個(gè)模塊向外提供多個(gè)接口,最好的實(shí)現(xiàn)方法是為每一個(gè)接口起一個(gè)namespace名,實(shí)際上采用這種實(shí)現(xiàn)方法,可以減少不必要的依賴和名字沖突。因此,就上述問題而言,為Driver和Parser的開發(fā)者所提供的兩個(gè)接口定義分別如下:

namespaceParser_interface //Driver的接口

{

doubleexpr(bool);

}

C++的名字空間機(jī)制還有一些相對(duì)復(fù)雜、高級(jí)的用法,在此我們不作贅述,有興趣的讀者可參閱C++標(biāo)準(zhǔn)和相關(guān)書籍。

7.3異常處理

對(duì)于一個(gè)實(shí)用的程序而言,沒有錯(cuò)誤處理是不可能的。實(shí)際的(大型)程序是由許多人在不同的時(shí)間內(nèi)開發(fā)出來的,許多出錯(cuò)處理任務(wù)并不一定能在(或者不應(yīng)當(dāng)在)發(fā)現(xiàn)出錯(cuò)的地方完成。例如:

intgrandchild(inti)

{

//…

if(出錯(cuò)了)

returnerror_no; //不知如何處理,返回錯(cuò)誤號(hào)上述函數(shù)調(diào)用中多處出現(xiàn)了函數(shù)出錯(cuò)后對(duì)其錯(cuò)誤不知如何(或不能夠)處理的問題。

一個(gè)欠缺良好結(jié)構(gòu)的程序(有時(shí)也可能是由于沒有語(yǔ)言支持機(jī)制造成的),對(duì)其錯(cuò)誤通常采用判斷語(yǔ)句(如C++中的if和switch語(yǔ)句)進(jìn)行判斷與處理。這種判斷處理方式帶來兩個(gè)問題:

(1)大量的錯(cuò)誤處理代碼與程序的功能代碼交織在一起,這不僅造成了程序結(jié)構(gòu)的混亂,而且往往錯(cuò)誤處理代碼遠(yuǎn)大于程序的功能代碼。這使程序不僅很難進(jìn)行正確的錯(cuò)誤處理,而且程序更難以維護(hù)與修改。

(2)對(duì)于某些錯(cuò)誤而言,程序不知如何處理或不能夠處理,因而對(duì)此類錯(cuò)誤只能丟棄,這又往往使程序的可靠性大大下降,甚至在某些極端的情況下,程序退化成不可用。

欲正確處理一個(gè)錯(cuò)誤,亦稱其為異常處理(ExceptionHandling),需要明確知道如下兩類信息:

(1)錯(cuò)誤發(fā)生的地點(diǎn),何種類型的錯(cuò)誤。

(2)怎樣處理錯(cuò)誤,在何處處理錯(cuò)誤。

因此,出錯(cuò)處理任務(wù)應(yīng)當(dāng)被分解成兩部分:錯(cuò)誤處理與錯(cuò)誤的報(bào)告。

(1)在某處(更一般地說是在某一模塊)若發(fā)現(xiàn)錯(cuò)誤(該錯(cuò)誤可能是本模塊中的錯(cuò)誤,亦可能是來自其它模塊中的錯(cuò)誤),能處理,則進(jìn)行處理。

(2)不能處理,則應(yīng)設(shè)置出錯(cuò)報(bào)告的條件,當(dāng)滿足條件時(shí)進(jìn)行報(bào)告,以提供必要的信息,供可能進(jìn)行錯(cuò)誤處理的模塊進(jìn)行錯(cuò)誤處理。

C++用try-catch塊進(jìn)行異常處理,用throw語(yǔ)句進(jìn)行錯(cuò)誤的報(bào)告。

由于C++異常處理涉及類和類的層次等概念,故C++的詳細(xì)異常處理機(jī)制我們放在第二部分第13章講述。下面我們僅給出一進(jìn)行異常處理的程序示例。

7.4綜合示例

C++的名字空間和異常處理機(jī)制是支持模塊化程序設(shè)計(jì)范型最重要的機(jī)制。一個(gè)設(shè)計(jì)良好的軟

溫馨提示

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

評(píng)論

0/150

提交評(píng)論