版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
第7章名字空間與異常處理7.1模塊與接口的基本概念7.2名字空間7.3異常處理7.4綜合示例小結(jié)練習(xí)題
7.1模塊與接口的基本概念
任何一個設(shè)計、構(gòu)造良好的實用程序均是由若干模塊(Module)/構(gòu)件(Component)組成的,即使它是一個相對簡單的程序。這意味著:
(1)可以將程序劃分成一組部件。
(2)程序模塊的劃分可以從多個角度來進行:一是按自己寫的程序與它所調(diào)用的系統(tǒng)支撐功能(如標(biāo)準(zhǔn)庫函數(shù)或類庫)來劃分;二是將自己寫的程序本身按某種原則(例如按功能)來進行劃分,等等。
程序劃分是有目的的。將程序劃分成模塊,不僅能使其程序結(jié)構(gòu)更加清晰,而且其模塊更易于被替換與重用。
C++的名字空間(Namespace)和異常處理機制(ExceptionHanding)就是支持程序模塊化組織的強有力機制。
本章我們首先以小型桌面計算器為例,來具體闡述C++用于支持模塊化程序設(shè)計范型的主要機制——名字空間及相關(guān)問題。
邏輯上,我們可將小型桌面計算器按功能劃分為以下五大模塊。
(1)語法分析器(Parser):完成語言的語法分析;
(2)詞法分析器(Lexer):完成語言的詞法分析;
(3)符號表(SymbolTable):存儲用戶標(biāo)識符的名-值對;
(4)驅(qū)動程序(Driver):main函數(shù);
(5)錯誤處理器(ErrorHandler):進行程序的錯誤處理。
其中,語法分析器、詞法分析器和符號表是該程序的三大核心部件。
程序中各模塊之間的劃分及模塊間的相互關(guān)系如圖7.1和圖7.2所示。圖中的箭頭表示模塊之間的調(diào)用關(guān)系。
通過前面的學(xué)習(xí)我們已經(jīng)知道,僅需了解一個函數(shù)接口的具體定義,而無需了解函數(shù)的具體實現(xiàn),我們就能夠很好地使用函數(shù)。圖7.1桌面計算器的程序邏輯劃分圖7.2桌面計算器的模塊關(guān)系圖類似地,即使程序的一個部件是由多個函數(shù)組成的,或者其中既有自定義類型也有全局變量、還有函數(shù),我們都可以這樣來設(shè)想:如果這樣的部件也像函數(shù)那樣有一個起包裝作用的接口,也同樣可以只需要了解接口而不需要了解實現(xiàn),就能夠很好地使用它,這正是信息隱藏原理的實質(zhì)與宗旨。
以此理念,圖7.2中程序各模塊之間調(diào)用的依賴關(guān)系我們可用圖7.3描述。圖7.3程序各模塊調(diào)用依賴圖圖7.3中各個模塊均調(diào)用錯誤處理模塊,考慮圖的清晰性,各模塊對錯誤處理模塊接口的調(diào)用我們暫未畫出。
從圖7.3可看出,各模塊調(diào)用時直接依賴的僅僅是所調(diào)用模塊的接口,而與其調(diào)用的實現(xiàn)無關(guān)。
確切地說,若程序中的一個部件具有明確的邊界,能夠?qū)崿F(xiàn)接口與實現(xiàn)的分離,并對它的用戶而言在使用時只需關(guān)心其接口而不管其實現(xiàn),該部件就叫做模塊(Module)。
實現(xiàn)模塊的接口與實現(xiàn)的分離,需要程序設(shè)計語言提供相應(yīng)的支持機制。C++提供的支持機制是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)系的機制。換句話說,如果一些聲明(定義聲明與非定義聲明)在邏輯上都與某個劃分準(zhǔn)則有關(guān),就可以把這些聲明放入一個共同的名字空間中,以表現(xiàn)這一事實。
同一名字空間中的聲明在概念上屬于同一個邏輯實體。由于程序中的模塊就屬于這種邏輯實體,因而我們可用C++的名字空間來封裝模塊,將程序進行模塊化組織。
C++的名字空間的語法形式為
namespace名字空間名
{
//邏輯相關(guān)的數(shù)據(jù)、函數(shù)、類或其它等
}
//注意:無“;”號
參照圖7.1和第6章桌面計算器的代碼,若以模塊化的組織形式重構(gòu)桌面計算器,則其語法分析(parser)模塊為
namespaceParser
{
doubleexpr(bool);//非定義聲明
doubleprim(boolget){/*…*/}上面,我們僅僅是把一些邏輯相關(guān)的部分組織到一個名字空間中(模塊),并未實現(xiàn)模塊接口與其實現(xiàn)的分離。如何實現(xiàn)這種接口與實現(xiàn)的分離呢?關(guān)鍵是在其模塊實現(xiàn)部分中出現(xiàn)的成員被該名字空間的名字所約束(Qualified),這樣的約束通過約束符(Qualifier::,C++的作用域解析符)來表示。以桌面計算器的Parser模塊為例,進行界面/接口與實現(xiàn)分離的程序代碼如下:從上述代碼可看出,接口與實現(xiàn)分離的要點是在每個模塊接口中僅需非定義聲明其中成員的接口部分,其模塊成員的實現(xiàn)(如數(shù)據(jù)、類的定義、函數(shù)的定義等)均應(yīng)以
名字空間名::成員
{
//…
}
的形式進行名字空間外的定義聲明。C++語法規(guī)定,利用此形式只能進行名字空間內(nèi)成員的定義聲明,而不能利用此語法新定義聲明一個名字空間成員。7.2.2名字空間中的名字解析
名字空間既是一個封裝體,也是一種作用域。一個名字空間中的名字(符號名)只能在其所定義的名字空間中可見、可用。
名字空間的另一主要用途是將不同的符號名組織到不同的名字空間中,以避免名字沖突。
名字沖突在大型軟件的開發(fā)過程中是一個突出問題。為了避免這個問題,在C++軟件開發(fā)過程中,我們應(yīng)有效地利用名字空間機制對軟件中的名字進行有效的組織與管理。例如,開發(fā)中,我們可將自己的代碼組織到一個命名的名字空間中,以避免和系統(tǒng)類庫、其他程序員編制的程序中的名字發(fā)生沖突。在以上兩個名字空間mfc和owl中,雖然都有同名的變量名inflag,但由于它們屬于不同的名字空間,因此不產(chǎn)生名字沖突。
在另一個名字空間中解析其他名字空間的名字,可采取
名字空間名::名字
的方式進行。例如:
mfc::inflag=3; //mfc中的inflag
owl::inflag=-256; //owl中的infalg
當(dāng)一個應(yīng)用程序中有多個模塊(或名字空間)時,由于各個namespace之間經(jīng)常會出現(xiàn)互相使用對方成員的情況,如果每次使用就需用名字空間名進行約束,將會導(dǎo)致程序書寫既繁瑣又容易出錯。例如,在Parser模塊的term實現(xiàn)中,就用了一系列的名字解析(約束):為了避免這種繁瑣易錯的名字解析,C++提供了幾種“有限的統(tǒng)一”約束機制。
1.using聲明語句
語法:using名字空間名::名字;
語義:將某一名字空間中的名字引入(Introduce)到一個局部范圍內(nèi),使其名字在該范圍內(nèi)無需名字空間的約束便可見、可用。
例如,在Parser模塊中,由于使用了若干using聲明語句,則在其namespace中我們就可直接采用其他namespace的名字。示例代碼如下:需要注意的是,若using聲明語句處于某一namespace的界面/接口中,則其using聲明語句有效于(作用于)該namespace的所有實現(xiàn)。例如,若我們在Parser名字空間中采用了如下所示的using聲明語句:則在parser模塊中的所有實現(xiàn)(prim、term和expr)中,get_token、curr_tok和error無需名字空間名約束便直接可見。我們還是以上述的prim實現(xiàn)為例:我們可看出,在上述代碼中所出現(xiàn)的其他namespace中的名字一律無需約束(解析),便能直接采用。
2.using指令語句
語法:usingnamespace名字空間名;
語義:將特定名字空間中的所有名字引入到一作用域內(nèi)。
我們已在前面各章節(jié)的示例程序中,多處見到了usingnamespacestd;語句。該語句的功用即將系統(tǒng)std名字空間的所有名字引入(展現(xiàn))到程序中。
值得注意的是,在一個namespace接口中使用using指令語句,其作用域為其namespace的所有實現(xiàn)中;若using指令語句用于某一個namespace的實現(xiàn)中,則其作用域為該實現(xiàn)內(nèi)。若我們在namespace的Parser中采用了如下using指令語句:
namespaceParser{
doubleprim(bool);
doubleterm(bool);
doubleexpr(bool);
usingnamespaceLexer; //using指令語句
usingnamespaceError; //using指令語句
}
仍以Parser::prim為例,則在prim中出現(xiàn)的所有Lexer、Error模塊中的名字均無需其名字空間名的約束而直接呈現(xiàn)(顯露)給prim。請看其代碼:實際應(yīng)用中,是采用using聲明語句只將聲明的名字引入另一區(qū)域,還是用using指令語句將其名字空間中的所有名字引入到另一區(qū)域,應(yīng)具體情況具體分析。
需要切記的是:C++中的namespace是一個范圍。采用namespace,我們可以對程序進行邏輯上的組織與劃分,程序越大,namespace的功能將愈強。
理想情況下,一個名字空間應(yīng)該具有以下特性:
(1)是一個描述了具有邏輯統(tǒng)一性特征的相關(guān)成員的集合;
(2)實現(xiàn)了接口與實現(xiàn)的分離,對用戶隱藏了namespace中的細(xì)節(jié);
(3)采用恰當(dāng)?shù)膗sing聲明語句或using指令語句,讓用戶免去任何明顯的名字描述負(fù)擔(dān)。7.2.3模塊的多重接口
采用C++的名字空間機制,我們可將程序以模塊化的形式組織起來。每一個模塊應(yīng)向調(diào)用者只暴露接口,而隱藏其實現(xiàn)。
一個模塊通常有不同類別的用戶。對不同的用戶而言,一個模塊應(yīng)向他們提供不同的接口。例如,某模塊的開發(fā)者能夠涉及模塊的全部成員,而這個模塊的普通用戶則只應(yīng)當(dāng)涉及對應(yīng)于對外功能的那些成員。因此,如果能夠為不同的用戶提供不同的接口,則該程序既友好又容易控制。又由于面向開發(fā)者的接口變動的可能性通常會比面向普通用戶的接口更大,從這個意義上來講,為他們提供不同的接口,有利于隔離內(nèi)部變動對外界的影響。因此,軟件開發(fā)中,一個模塊應(yīng)為不同的用戶提供不同的模塊接口。
我們?nèi)砸宰烂嬗嬎闫鳛槔齺碚f明此問題。參看7.1節(jié)的圖7.1,對于Parser模塊而言,面對開發(fā)者,我們應(yīng)提供該模塊內(nèi)所有成員的接口,而對于Driver模塊而言,則只需提供Parser模塊中的expr函數(shù)的接口即可,因Driver只需看到expr的接口。
C++語法上允許為一個名字空間定義多個同空間名的接口。例如,對桌面計算器的Parser模塊而言,為Driver可定義如下接口:
namespaceParser
{
doubleexpr(bool);
}
對Parser的實現(xiàn)者(開發(fā)者)而言,亦可定義如下同空間名的接口:
namespaceParser
{
doubleprim(bool);
doubleterm(bool);
doubleexpr(bool);
usingLexer::get_token; //using聲明語句
usingLexer::curr_tok; //using聲明語句
usingError::error; //using聲明語句
}但實際的軟件開發(fā)中,若需要一個模塊向外提供多個接口,最好的實現(xiàn)方法是為每一個接口起一個namespace名,實際上采用這種實現(xiàn)方法,可以減少不必要的依賴和名字沖突。因此,就上述問題而言,為Driver和Parser的開發(fā)者所提供的兩個接口定義分別如下:
namespaceParser_interface //Driver的接口
{
doubleexpr(bool);
}
C++的名字空間機制還有一些相對復(fù)雜、高級的用法,在此我們不作贅述,有興趣的讀者可參閱C++標(biāo)準(zhǔn)和相關(guān)書籍。
7.3異常處理
對于一個實用的程序而言,沒有錯誤處理是不可能的。實際的(大型)程序是由許多人在不同的時間內(nèi)開發(fā)出來的,許多出錯處理任務(wù)并不一定能在(或者不應(yīng)當(dāng)在)發(fā)現(xiàn)出錯的地方完成。例如:
intgrandchild(inti)
{
//…
if(出錯了)
returnerror_no; //不知如何處理,返回錯誤號上述函數(shù)調(diào)用中多處出現(xiàn)了函數(shù)出錯后對其錯誤不知如何(或不能夠)處理的問題。
一個欠缺良好結(jié)構(gòu)的程序(有時也可能是由于沒有語言支持機制造成的),對其錯誤通常采用判斷語句(如C++中的if和switch語句)進行判斷與處理。這種判斷處理方式帶來兩個問題:
(1)大量的錯誤處理代碼與程序的功能代碼交織在一起,這不僅造成了程序結(jié)構(gòu)的混亂,而且往往錯誤處理代碼遠(yuǎn)大于程序的功能代碼。這使程序不僅很難進行正確的錯誤處理,而且程序更難以維護與修改。
(2)對于某些錯誤而言,程序不知如何處理或不能夠處理,因而對此類錯誤只能丟棄,這又往往使程序的可靠性大大下降,甚至在某些極端的情況下,程序退化成不可用。
欲正確處理一個錯誤,亦稱其為異常處理(ExceptionHandling),需要明確知道如下兩類信息:
(1)錯誤發(fā)生的地點,何種類型的錯誤。
(2)怎樣處理錯誤,在何處處理錯誤。
因此,出錯處理任務(wù)應(yīng)當(dāng)被分解成兩部分:錯誤處理與錯誤的報告。
(1)在某處(更一般地說是在某一模塊)若發(fā)現(xiàn)錯誤(該錯誤可能是本模塊中的錯誤,亦可能是來自其它模塊中的錯誤),能處理,則進行處理。
(2)不能處理,則應(yīng)設(shè)置出錯報告的條件,當(dāng)滿足條件時進行報告,以提供必要的信息,供可能進行錯誤處理的模塊進行錯誤處理。
C++用try-catch塊進行異常處理,用throw語句進行錯誤的報告。
由于C++異常處理涉及類和類的層次等概念,故C++的詳細(xì)異常處理機制我們放在第二部分第13章講述。下面我們僅給出一進行異常處理的程序示例。
7.4綜合示例
C++的名字空間和異常處理機制是支持模塊化程序設(shè)計范型最重要的機制。一個設(shè)計良好的軟
溫馨提示
- 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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- GB/T 22371-2024傳真機、多功能復(fù)合型傳真機環(huán)境保護要求
- 2025年度教育儲值卡銷售與教育資源整合合同3篇
- 二零二五版環(huán)保項目環(huán)保宣傳教育分包合同3篇
- 二零二五年度果園租賃附帶果樹修剪與施肥服務(wù)合同3篇
- 二零二五年度賓館能源審計服務(wù)合同范本3篇
- 二零二五版危險化學(xué)品運輸司機安全責(zé)任合同3篇
- 2024年速凍粘玉米購銷合同的支付方式
- 2024鮮魚養(yǎng)殖與市場風(fēng)險防控合作協(xié)議3篇
- 二零二五年度駕校場地租賃與智能語音教學(xué)合同3篇
- 二零二五年度酒店租賃經(jīng)營聯(lián)合運營合同范本3篇
- 2024-2025學(xué)年八年級上學(xué)期1月期末物理試題(含答案)
- 2025年國新國際投資有限公司招聘筆試參考題庫含答案解析
- 制造車間用洗地機安全操作規(guī)程
- 2025河南省建筑安全員-A證考試題庫及答案
- 商場電氣設(shè)備維護勞務(wù)合同
- 油氣田智能優(yōu)化設(shè)計-洞察分析
- 陜西2020-2024年中考英語五年真題匯編學(xué)生版-專題09 閱讀七選五
- 磚混結(jié)構(gòu)基礎(chǔ)加固技術(shù)方案
- 助產(chǎn)專業(yè)的職業(yè)生涯規(guī)劃
- 新《國有企業(yè)管理人員處分條例》知識競賽考試題庫500題(含答案)
- 骨質(zhì)疏松護理
評論
0/150
提交評論