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

下載本文檔

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

文檔簡(jiǎn)介

第14章C++工具14.1異常處理14.2命名空間14.3使用早期的函數(shù)庫(kù)《C++程序設(shè)計(jì)》_第14章在C++發(fā)展的后期,有時(shí)C++編譯系統(tǒng)根據(jù)實(shí)際工作的需要,增加了一些功能,作為工具來(lái)使用,其中主要有模板(包括函數(shù)模板和類(lèi)模板)、異常處理、命名空間和運(yùn)行時(shí)類(lèi)型識(shí)別,以幫助程序設(shè)計(jì)人員更方便地進(jìn)行程序的設(shè)計(jì)和調(diào)試工作。1997年ANSIC++委員會(huì)將它們納入了ANSIC++標(biāo)準(zhǔn),建議所有的C++編譯系統(tǒng)都能實(shí)現(xiàn)這些功能。這些工具是非常有用的,C++的使用者應(yīng)當(dāng)盡量使用這些工具?!禖++程序設(shè)計(jì)》_第14章程序編制者不僅要考慮程序沒(méi)有錯(cuò)誤的理想情況,更要考慮程序存在錯(cuò)誤時(shí)的情況,應(yīng)該能夠盡快地發(fā)現(xiàn)錯(cuò)誤,消除錯(cuò)誤。程序中常見(jiàn)的錯(cuò)誤有兩大類(lèi):語(yǔ)法錯(cuò)誤和運(yùn)行錯(cuò)誤。在編譯時(shí),編譯系統(tǒng)能發(fā)現(xiàn)程序中的語(yǔ)法錯(cuò)誤。有的程序雖然能通過(guò)編譯,也能投入運(yùn)行。但是在運(yùn)行過(guò)程中會(huì)出現(xiàn)異常,得不到正確的運(yùn)行結(jié)果,甚至導(dǎo)致程序不正常終止,或出現(xiàn)死機(jī)現(xiàn)象。這類(lèi)錯(cuò)誤比較隱蔽,不易被發(fā)現(xiàn),往往耗費(fèi)許多時(shí)間和精力。這成為程序調(diào)試中的一個(gè)難點(diǎn)。14.1異常處理

14.1.1異常處理的任務(wù)《C++程序設(shè)計(jì)》_第14章在設(shè)計(jì)程序時(shí),應(yīng)當(dāng)事先分析程序運(yùn)行時(shí)可能出現(xiàn)的各種意外的情況,并且分別制訂出相應(yīng)的處理方法,這就是程序的異常處理的任務(wù)。在運(yùn)行沒(méi)有異常處理的程序時(shí),如果運(yùn)行情況出現(xiàn)異常,由于程序本身不能處理,程序只能終止運(yùn)行。如果在程序中設(shè)置了異常處理機(jī)制,則在運(yùn)行情況出現(xiàn)異常時(shí),由于程序本身已規(guī)定了處理的方法,于是程序的流程就轉(zhuǎn)到異常處理代碼段處理。用戶可以指定進(jìn)行任何的處理。需要說(shuō)明,只要出現(xiàn)與人們期望的情況不同,都可以認(rèn)為是異常,并對(duì)它進(jìn)行異常處理。因此,所謂異常處理指的是對(duì)運(yùn)行時(shí)出現(xiàn)的差錯(cuò)以及其他例外情況的處理?!禖++程序設(shè)計(jì)》_第14章在一個(gè)小的程序中,可以用比較簡(jiǎn)單的方法處理異常。但是在一個(gè)大的系統(tǒng)中,如果在每一個(gè)函數(shù)中都設(shè)置處理異常的程序段,會(huì)使程序過(guò)于復(fù)雜和龐大。因此,C++采取的辦法是:如果在執(zhí)行一個(gè)函數(shù)過(guò)程中出現(xiàn)異常,可以不在本函數(shù)中立即處理,而是發(fā)出一個(gè)信息,傳給它的上一級(jí)(即調(diào)用它的函數(shù)),它的上級(jí)捕捉到這個(gè)信息后進(jìn)行處理。如果上一級(jí)的函數(shù)也不能處理,就再傳給其上一級(jí),由其上一級(jí)處理。如此逐級(jí)上送,如果到最高一級(jí)還無(wú)法處理,最后只好異常終止程序的執(zhí)行。14.1.2異常處理的方法《C++程序設(shè)計(jì)》_第14章這樣做使異常的發(fā)現(xiàn)與處理不由同一函數(shù)來(lái)完成。好處是使底層的函數(shù)專(zhuān)門(mén)用于解決實(shí)際任務(wù),而不必再承擔(dān)處理異常的任務(wù),以減輕底層函數(shù)的負(fù)擔(dān),而把處理異常的任務(wù)上移到某一層去處理。這樣可以提高效率。C++處理異常的機(jī)制是由3個(gè)部分組成的,即檢查(try)、拋出(throw)和捕捉(catch)。把需要檢查的語(yǔ)句放在try塊中,throw用來(lái)當(dāng)出現(xiàn)異常時(shí)發(fā)出一個(gè)異常信息,而catch則用來(lái)捕捉異常信息,如果捕捉到了異常信息,就處理它?!禖++程序設(shè)計(jì)》_第14章例14.1給出三角形的三邊a,b,c,求三角形的面積。只有a+b>c,b+c>a,c+a>b時(shí)才能構(gòu)成三角形。設(shè)置異常處理,對(duì)不符合三角形條件的輸出警告信息,不予計(jì)算。先寫(xiě)出沒(méi)有異常處理時(shí)的程序:#include<iostream>#include<cmath>usingnamespacestd;intmain(){doubletriangle(double,double,double);doublea,b,c;cin>>a>>b>>c;while(a>0&&b>0&&c>0){cout<<triangle(a,b,c)<<endl;cin>>a>>b>>c;}return0;《C++程序設(shè)計(jì)》_第14章}doubletriangle(doublea,doubleb,doublec){doublearea;doubles=(a+b+c)/2;area=sqrt(s*(s-a)*(s-b)*(s-c));returnarea;}運(yùn)行情況如下:654↙(輸入a,b,c的值)9.92157(輸出三角形的面積)11.52↙(輸入a,b,c的值)0.726184(輸出三角形的面積)121↙(輸入a,b,c的值)0(輸出三角形的面積,此結(jié)果顯然不對(duì),因?yàn)椴皇侨切?106↙(輸入a,b,c的值)(結(jié)束)《C++程序設(shè)計(jì)》_第14章修改程序,在函數(shù)traingle中對(duì)三角形條件進(jìn)行檢查,如果不符合三角形條件,就拋出一個(gè)異常信息,在主函數(shù)中的try-catch塊中調(diào)用traingle函數(shù),檢測(cè)有無(wú)異常信息,并作相應(yīng)處理。修改后的程序如下:#include<iostream>#include<cmath>usingnamespacestd;voidmain(){doubletriangle(double,double,double);doublea,b,c;cin>>a>>b>>c;try//在try塊中包含要檢查的函數(shù){while(a>0&&b>0&&c>0){cout<<triangle(a,b,c)<<endl;cin>>a>>b>>c;}}《C++程序設(shè)計(jì)》_第14章catch(double)//用catch捕捉異常信息并作相應(yīng)處理{cout<<″a=″<<a<<″,b=″<<b<<″,c=″<<c<<″,thatisnotatriangle!″<<endl;}cout<<″e(cuò)nd″<<endl;}doubletriangle(doublea,doubleb,doublec)//計(jì)算三角形的面積的函數(shù){doubles=(a+b+c)/2;if(a+b<=c||b+c<=a||c+a<=b)throwa;//當(dāng)不符合三角形條件拋出異常信息

returnsqrt(s*(s-a)*(s-b)*(s-c));}程序運(yùn)行結(jié)果如下:654↙(輸入a,b,c的值)9.92157(計(jì)算出三角形的面積)11.52↙(輸入a,b,c的值)0.726184(計(jì)算出三角形的面積)121↙(輸入a,b,c的值)a=1,b=2,c=1,thatisnotatriangle!(異常處理)end《C++程序設(shè)計(jì)》_第14章現(xiàn)在結(jié)合程序分析怎樣進(jìn)行異常處理。(1)首先把可能出現(xiàn)異常的、需要檢查的語(yǔ)句或程序段放在try后面的花括號(hào)中。(2)程序開(kāi)始運(yùn)行后,按正常的順序執(zhí)行到try塊,開(kāi)始執(zhí)行try塊中花括號(hào)內(nèi)的語(yǔ)句。如果在執(zhí)行try塊內(nèi)的語(yǔ)句過(guò)程中沒(méi)有發(fā)生異常,則catch子句不起作用,流程轉(zhuǎn)到catch子句后面的語(yǔ)句繼續(xù)執(zhí)行。(3)如果在執(zhí)行try塊內(nèi)的語(yǔ)句(包括其所調(diào)用的函數(shù))過(guò)程中發(fā)生異常,則throw運(yùn)算符拋出一個(gè)異常信息。throw拋出異常信息后,流程立即離開(kāi)本函數(shù),轉(zhuǎn)到其上一級(jí)的函數(shù)(main函數(shù))。throw拋出什么樣的數(shù)據(jù)由程序設(shè)計(jì)者自定,可以是任何類(lèi)型的數(shù)據(jù)?!禖++程序設(shè)計(jì)》_第14章(4)這個(gè)異常信息提供給try-catch結(jié)構(gòu),系統(tǒng)會(huì)尋找與之匹配的catch子句。(5)在進(jìn)行異常處理后,程序并不會(huì)自動(dòng)終止,繼續(xù)執(zhí)行catch子句后面的語(yǔ)句。由于catch子句是用來(lái)處理異常信息的,往往被稱(chēng)為catch異常處理塊或catch異常處理器?!禖++程序設(shè)計(jì)》_第14章下面講述異常處理的語(yǔ)法。throw語(yǔ)句一般是由throw運(yùn)算符和一個(gè)數(shù)據(jù)組成的,其形式為throw表達(dá)式;try-catch的結(jié)構(gòu)為try{被檢查的語(yǔ)句}catch(異常信息類(lèi)型[變量名]){進(jìn)行異常處理的語(yǔ)句}《C++程序設(shè)計(jì)》_第14章說(shuō)明:(1)被檢測(cè)的函數(shù)必須放在try塊中,否則不起作用。(2)try塊和catch塊作為一個(gè)整體出現(xiàn),catch塊是try-catch結(jié)構(gòu)中的一部分,必須緊跟在try塊之后,不能單獨(dú)使用,在二者之間也不能插入其他語(yǔ)句。但是在一個(gè)try-catch結(jié)構(gòu)中,可以只有try塊而無(wú)catch塊。即在本函數(shù)中只檢查而不處理,把catch處理塊放在其他函數(shù)中。(3)try和catch塊中必須有用花括號(hào)括起來(lái)的復(fù)合語(yǔ)句,即使花括號(hào)內(nèi)只有一個(gè)語(yǔ)句,也不能省略花括號(hào)。(4)一個(gè)try-catch結(jié)構(gòu)中只能有一個(gè)try塊,但卻可以有多個(gè)catch塊,以便與不同的異常信息匹配。《C++程序設(shè)計(jì)》_第14章(5)catch后面的圓括號(hào)中,一般只寫(xiě)異常信息的類(lèi)型名,如catch(double)catch只檢查所捕獲異常信息的類(lèi)型,而不檢查它們的值。因此如果需要檢測(cè)多個(gè)不同的異常信息,應(yīng)當(dāng)由throw拋出不同類(lèi)型的異常信息。異常信息可以是C++系統(tǒng)預(yù)定義的標(biāo)準(zhǔn)類(lèi)型,也可以是用戶自定義的類(lèi)型(如結(jié)構(gòu)體或類(lèi))。如果由throw拋出的信息屬于該類(lèi)型或其子類(lèi)型,則catch與throw二者匹配,catch捕獲該異常信息。catch還可以有另外一種寫(xiě)法,即除了指定類(lèi)型名外,還指定變量名,如catch(doubled)《C++程序設(shè)計(jì)》_第14章此時(shí)如果throw拋出的異常信息是double型的變量a,則catch在捕獲異常信息a的同時(shí),還使d獲得a的值,或者說(shuō)d得到a的一個(gè)拷貝。什么時(shí)候需要這樣做呢?有時(shí)希望在捕獲異常信息時(shí),還能利用throw拋出的值,如catch(doubled){cout<<″throw″<<d;}這時(shí)會(huì)輸出d的值(也就是a值)。當(dāng)拋出的是類(lèi)對(duì)象時(shí),有時(shí)希望在catch塊中顯示該對(duì)象中的某些信息。這時(shí)就需要在catch的參數(shù)中寫(xiě)出變量名(類(lèi)對(duì)象名)。(6)如果在catch子句中沒(méi)有指定異常信息的類(lèi)型,而用了刪節(jié)號(hào)“…”,則表示它可以捕捉任何類(lèi)型的異常信息,如《C++程序設(shè)計(jì)》_第14章catch(…){cout<<″OK″<<endl;}它能捕捉所有類(lèi)型的異常信息,并輸出″OK″。這種catch子句應(yīng)放在trycatch結(jié)構(gòu)中的最后,相當(dāng)于“其他”。如果把它作為第一個(gè)catch子句,則后面的catch子句都不起作用。(7)trycatch結(jié)構(gòu)可以與throw出現(xiàn)在同一個(gè)函數(shù)中,也可以不在同一函數(shù)中。當(dāng)throw拋出異常信息后,首先在本函數(shù)中尋找與之匹配的catch,如果在本函數(shù)中無(wú)trycatch結(jié)構(gòu)或找不到與之匹配的catch,就轉(zhuǎn)到離開(kāi)出現(xiàn)異常最近的trycatch結(jié)構(gòu)去處理。《C++程序設(shè)計(jì)》_第14章(8)在某些情況下,在throw語(yǔ)句中可以不包括表達(dá)式,如throw;表示“我不處理這個(gè)異常,請(qǐng)上級(jí)處理”。(9)如果throw拋出的異常信息找不到與之匹配的catch塊,那么系統(tǒng)就會(huì)調(diào)用一個(gè)系統(tǒng)函數(shù)terminate,使程序終止運(yùn)行。例14.2在函數(shù)嵌套的情況下檢測(cè)異常處理。這是一個(gè)簡(jiǎn)單的例子,用來(lái)說(shuō)明在try塊中有函數(shù)嵌套調(diào)用的情況下拋出異常和捕捉異常的情況。請(qǐng)自己先分析以下程序?!禖++程序設(shè)計(jì)》_第14章#include<iostream>usingnamespacestd;intmain(){voidf1();try{f1();}//調(diào)用f1()catch(double){cout<<″OK0!″<<endl;}cout<<″e(cuò)nd0″<<endl;return0;}voidf1(){voidf2();try{f2();}//調(diào)用f2()catch(char){cout<<″OK1!″;}cout<<″e(cuò)nd1″<<endl;}《C++程序設(shè)計(jì)》_第14章voidf2(){voidf3();try{f3();}//調(diào)用f3()catch(int){cout<<″Ok2!″<<endl;}cout<<″e(cuò)nd2″<<endl;}voidf3(){doublea=0;try{throwa;}//拋出double類(lèi)型異常信息

catch(float){cout<<″OK3!″<<endl;}cout<<″e(cuò)nd3″<<endl;}《C++程序設(shè)計(jì)》_第14章分3種情況分析運(yùn)行情況:(1)執(zhí)行上面的程序。圖14.1為有函數(shù)嵌套時(shí)異常處理示意圖。圖14.1程序運(yùn)行結(jié)果如下:OK0!(在主函數(shù)中捕獲異常)end0(執(zhí)行主函數(shù)中最后一個(gè)語(yǔ)句時(shí)的輸出)《C++程序設(shè)計(jì)》_第14章(2)如果將f3函數(shù)中的catch子句改為catch(double),而程序中其他部分不變,則程序運(yùn)行結(jié)果如下:OK3!(在f3函數(shù)中捕獲異常)end3(執(zhí)行f3函數(shù)中最后一個(gè)語(yǔ)句時(shí)的輸出)end2(執(zhí)行f2函數(shù)中最后一個(gè)語(yǔ)句時(shí)的輸出)end1(執(zhí)行f1函數(shù)中最后一個(gè)語(yǔ)句時(shí)的輸出)end0(執(zhí)行主函數(shù)中最后一個(gè)語(yǔ)句時(shí)的輸出)(3)如果在此基礎(chǔ)上再將f3函數(shù)中的catch塊改為catch(double){cout<<″OK3!″<<endl;throw;}程序運(yùn)行結(jié)果如下:OK3!(在f3函數(shù)中捕獲異常)OK0!(在主函數(shù)中捕獲異常)end0(執(zhí)行主函數(shù)中最后一個(gè)語(yǔ)句時(shí)的輸出)《C++程序設(shè)計(jì)》_第14章為便于閱讀程序,使用戶在看程序時(shí)能夠知道所用的函數(shù)是否會(huì)拋出異常信息以及異常信息可能的類(lèi)型,C++允許在聲明函數(shù)時(shí)列出可能拋出的異常類(lèi)型,如可以將例14.1中第二個(gè)程序的第3行改寫(xiě)為doubletriangle(double,double,double)throw(double);表示triangle函數(shù)只能拋出double類(lèi)型的異常信息。如果寫(xiě)成doubletriangle(double,double,double)throw(int,double,float,char);則表示triangle函數(shù)可以拋出int,double,float或char類(lèi)型的異常信息。異常指定是函數(shù)聲明的一部分,必須同時(shí)出現(xiàn)在函數(shù)聲明和函數(shù)定義的首行中,否則在進(jìn)行函數(shù)的另一次聲明時(shí),編譯系統(tǒng)會(huì)報(bào)告“類(lèi)型不匹配”。14.1.3在函數(shù)聲明中進(jìn)行異常情況指定《C++程序設(shè)計(jì)》_第14章如果在聲明函數(shù)時(shí)未列出可能拋出的異常類(lèi)型,則該函數(shù)可以拋出任何類(lèi)型的異常信息。如例14.1中第2個(gè)程序中所表示的那樣。如果想聲明一個(gè)不能拋出異常的函數(shù),可以寫(xiě)成以下形式:doubletriangle(double,double,double)throw();//throw無(wú)參數(shù)這時(shí)即使在函數(shù)執(zhí)行過(guò)程中出現(xiàn)了throw語(yǔ)句,實(shí)際上也并不執(zhí)行throw語(yǔ)句,并不拋出任何異常信息,程序?qū)⒎钦=K止?!禖++程序設(shè)計(jì)》_第14章如果在try塊(或try塊中調(diào)用的函數(shù))中定義了類(lèi)對(duì)象,在建立該對(duì)象時(shí)要調(diào)用構(gòu)造函數(shù)。在執(zhí)行try塊(包括在try塊中調(diào)用其他函數(shù))的過(guò)程中如果發(fā)生了異常,此時(shí)流程立即離開(kāi)try塊。這樣流程就有可能離開(kāi)該對(duì)象的作用域而轉(zhuǎn)到其他函數(shù),因而應(yīng)當(dāng)事先做好結(jié)束對(duì)象前的清理工作,C++的異常處理機(jī)制會(huì)在throw拋出異常信息被catch捕獲時(shí),對(duì)有關(guān)的局部對(duì)象進(jìn)行析構(gòu)(調(diào)用類(lèi)對(duì)象的析構(gòu)函數(shù)),析構(gòu)對(duì)象的順序與構(gòu)造的順序相反,然后執(zhí)行與異常信息匹配的catch塊中的語(yǔ)句。14.1.4在異常處理中處理析構(gòu)函數(shù)《C++程序設(shè)計(jì)》_第14章例14.3在異常處理中處理析構(gòu)函數(shù)。這是一個(gè)為說(shuō)明在異常處理中調(diào)用析構(gòu)函數(shù)的示例,為了清晰地表示流程,程序中加入了一些cout語(yǔ)句,輸出有關(guān)的信息,以便對(duì)照結(jié)果分析程序。#include<iostream>#include<string>usingnamespacestd;classStudent{public:Student(intn,stringnam)//定義構(gòu)造函數(shù){cout<<″constructor-″<<n<<endl;num=n;name=nam;}~Student(){cout<<″destructor-″<<num<<endl;}//定義析構(gòu)函數(shù)

voidget_data();//成員函數(shù)聲明private:intnum;stringname;};《C++程序設(shè)計(jì)》_第14章voidStudent::get_data()//定義成員函數(shù){if(num==0)thrownum;//如num=0,拋出int型變量numelsecout<<num<<″″<<name<<endl;//若num≠0,輸出num,namecout<<″inget_data()″<<endl;//輸出信息,表示目前在get_data函數(shù)中}voidfun(){Studentstud1(1101,″Tan″);//建立對(duì)象stud1stud1.get_data();//調(diào)用stud1的get_data函數(shù)Studentstud2(0,″Li″);//建立對(duì)象stud2stud2.get_data();//調(diào)用stud2的get_data函數(shù)}intmain(){cout<<″mainbegin″<<endl;//表示主函數(shù)開(kāi)始了cout<<″callfun()″<<endl;//表示調(diào)用fun函數(shù)try{fun();}//調(diào)用fun函數(shù)《C++程序設(shè)計(jì)》_第14章catch(intn){cout<<″num=″<<n<<″,error!″<<endl;}//表示num=0出錯(cuò)cout<<″mainend″<<endl;//表示主函數(shù)結(jié)束return0;}程序運(yùn)行結(jié)果如下:mainbegincallfun()constructor-11011101taninget_data()constructor-0destructor-0destructor-1101num=0,error!mainend《C++程序設(shè)計(jì)》_第14章在學(xué)習(xí)本書(shū)前面各章時(shí),已經(jīng)多次看到在程序中用了以下語(yǔ)句:usingnamespacestd;這就是使用了命名空間std。在本節(jié)中將對(duì)它作較詳細(xì)的介紹。14.2命名空間《C++程序設(shè)計(jì)》_第14章命名空間是ANSIC++引入的可以由用戶命名的作用域,用來(lái)處理程序中常見(jiàn)的同名沖突。在C語(yǔ)言中定義了3個(gè)層次的作用域,即文件(編譯單元)、函數(shù)和復(fù)合語(yǔ)句。C++又引入了類(lèi)作用域,類(lèi)是出現(xiàn)在文件內(nèi)的。在不同的作用域中可以定義相同名字的變量,互不干擾,系統(tǒng)能夠區(qū)別它們。下面先簡(jiǎn)單分析一下作用域的作用,然后討論命名空間的作用。如果在文件中定義了兩個(gè)類(lèi),在這兩個(gè)類(lèi)中可以有同名的函數(shù)。在引用時(shí),為了區(qū)別,應(yīng)該加上類(lèi)名作為限定,如14.2.1為什么需要命名空間《C++程序設(shè)計(jì)》_第14章classA//聲明A類(lèi){public:voidfun1();//聲明A類(lèi)中的fun1函數(shù)

private:inti;};voidA::fun1()//定義A類(lèi)中的fun1函數(shù){//}classB//聲明B類(lèi){public:voidfun1();//B類(lèi)中也有fun1函數(shù)voidfun2();};voidB::fun1()//定義B類(lèi)中的fun1函數(shù){//}《C++程序設(shè)計(jì)》_第14章這樣不會(huì)發(fā)生混淆。在文件中可以定義全局變量(globalvariable),它的作用域是整個(gè)程序。如果在文件A中定義了一個(gè)變量ainta=3;在文件B中可以再定義一個(gè)變量ainta=5;在分別對(duì)文件A和文件B進(jìn)行編譯時(shí)不會(huì)有問(wèn)題。但是,如果一個(gè)程序包括文件A和文件B,那么在進(jìn)行連接時(shí),會(huì)報(bào)告出錯(cuò),因?yàn)樵谕粋€(gè)程序中有兩個(gè)同名的變量,認(rèn)為是對(duì)變量的重復(fù)定義。問(wèn)題在于全局變量的作用域是整個(gè)程序,在同一作用域中不應(yīng)有兩個(gè)或多個(gè)同名的實(shí)體(entity),包括變量、函數(shù)和類(lèi)等?!禖++程序設(shè)計(jì)》_第14章可以通過(guò)extern聲明同一程序中的兩個(gè)文件中的同名變量是同一個(gè)變量。如果在文件B中有以下聲明:externinta;表示文件B中的變量a是在其他文件中已定義的變量。由于有此聲明,在程序編譯和連接后,文件A的變量a的作用域擴(kuò)展到了文件B。如果在文件B中不再對(duì)a賦值,則在文件B中用以下語(yǔ)句輸出的是文件A中變量a的值:cout<<a;//得到a的值為3在簡(jiǎn)單的程序設(shè)計(jì)中,只要人們小心注意,可以爭(zhēng)取不發(fā)生錯(cuò)誤。但是,一個(gè)大型的應(yīng)用軟件,往往不是由一個(gè)人獨(dú)立完成的,假如不同的人分別定義了類(lèi),放在不同的頭文件中,在主文件(包含主函數(shù)的文件)需要用這些類(lèi)時(shí),《C++程序設(shè)計(jì)》_第14章就用#include命令行將這些頭文件包含進(jìn)來(lái)。由于各頭文件是由不同的人設(shè)計(jì)的,有可能在不同的頭文件中用了相同的名字來(lái)命名所定義的類(lèi)或函數(shù)。這樣在程序中就會(huì)出現(xiàn)名字沖突。例14.4名字沖突。程序員甲在頭文件header1.h中定義了類(lèi)Student和函數(shù)fun。//header1.h(頭文件1,設(shè)其文件名為cc14-4-h1.h)#include<string>#include<cmath>usingnamespacestd;classStudent//聲明Student類(lèi){public:Student(intn,stringnam,chars){num=n;name=nam;sex=s;}voidget_data();private:《C++程序設(shè)計(jì)》_第14章intnum;stringname;charsex;};voidStudent::get_data()//成員函數(shù)定義{cout<<num<<″″<<name<<″″<<sex<<endl;}doublefun(doublea,doubleb)//定義全局函數(shù)(即外部函數(shù)){returnsqrt(a+b);}在main函數(shù)所在的文件中包含頭文件header1.h:#include<iostream>#include″cc14-4-h1.h″//注意要用雙引號(hào),因?yàn)槲募话闶欠旁谟脩裟夸浿械膗singnamespacestd;intmain(){Studentstud1(101,″Wang″,18);//定義類(lèi)對(duì)象stud1stud1.get_data();cout<<fun(5,3)<<endl;return0;}《C++程序設(shè)計(jì)》_第14章程序能正常運(yùn)行,輸出為101Wang182.82843如果程序員乙寫(xiě)了頭文件header2.h,在其中除了定義其他類(lèi)以外,還定義了類(lèi)Student和函數(shù)fun,但其內(nèi)容與頭文件header1.h中的Student和函數(shù)fun有所不同。//header2.h(頭文件2,設(shè)其文件名為cc14-4-h2.h)#include<string>#include<cmath>usingnamespacestd;classStudent//聲明Student類(lèi){public:Student(intn,stringnam,chars)//參數(shù)與header1中的student不同{num=n;name=nam;sex=s;}voidget_data();《C++程序設(shè)計(jì)》_第14章private:intnum;stringname;charsex;//此項(xiàng)與header1不同};voidStudent::get_data()//成員函數(shù)定義{cout<<num<<″″<<name<<″″<<sex<<endl;}doublefun(doublea,doubleb)//定義全局函數(shù){returnsqrt(a-b);}//返回值與header1中的fun函數(shù)不同//頭文件2中可能還有其他內(nèi)容假如主程序員在其程序中要用到header1.h中的Student和函數(shù)fun,因而在程序中包含了頭文件header1.h,同時(shí)要用到頭文件header2.h中的一些內(nèi)容,因而在程序中又包含了頭文件header2.h。如果主文件(包含主函數(shù)的文件)如下:《C++程序設(shè)計(jì)》_第14章//mainfile#include<iostream>#include″cc14-4-h1.h″//包含頭文件1#include″cc14-4-h2.h″//包含頭文件2usingnamespacestd;intmain(){Studentstud1(101,″Wang″,18);stud1.get_data();cout<<fun(5,3)<<endl;return0;}這時(shí)程序編譯就會(huì)出錯(cuò)。因?yàn)樵陬A(yù)編譯后,頭文件中的內(nèi)容取代了對(duì)應(yīng)的#include命令行,這樣就在同一個(gè)程序文件中出現(xiàn)了兩個(gè)Student類(lèi)和兩個(gè)fun函數(shù),顯然是重復(fù)定義,這就是名字沖突,即在同一個(gè)作用域中有兩個(gè)或多個(gè)同名的實(shí)體?!禖++程序設(shè)計(jì)》_第14章不僅如此,在程序中還往往需要引用一些庫(kù),為此需要包含有關(guān)的頭文件。如果在這些庫(kù)中包含有與程序的全局實(shí)體同名的實(shí)體,或者不同的庫(kù)中有相同的實(shí)體名,則在編譯時(shí)就會(huì)出現(xiàn)名字沖突。為了避免這類(lèi)問(wèn)題的出現(xiàn),人們提出了許多方法,例如:將實(shí)體的名字寫(xiě)得長(zhǎng)一些;把名字起得特殊一些,包括一些特殊的字符;由編譯系統(tǒng)提供的內(nèi)部全局標(biāo)識(shí)符都用下劃線作為前綴,如_complex(),以避免與用戶命名的實(shí)體同名;由軟件開(kāi)發(fā)商提供的實(shí)體的名字用特定的字符作為前綴。但是這樣的效果并不理想,而且增加了閱讀程序的難度,可讀性降低了?!禖++程序設(shè)計(jì)》_第14章C語(yǔ)言和早期的C++語(yǔ)言沒(méi)有提供有效的機(jī)制來(lái)解決這個(gè)問(wèn)題,沒(méi)有使庫(kù)的提供者能夠建立自己的命名空間的工具。人們希望ANSIC++標(biāo)準(zhǔn)能夠解決這個(gè)問(wèn)題,提供一種機(jī)制、一種工具,使由庫(kù)的設(shè)計(jì)者命名的全局標(biāo)識(shí)符能夠和程序的全局實(shí)體名以及其他庫(kù)的全局標(biāo)識(shí)符區(qū)別開(kāi)來(lái)?!禖++程序設(shè)計(jì)》_第14章為了解決上面這個(gè)問(wèn)題,ANSIC++增加了命名空間(namespace)。所謂命名空間,實(shí)際上就是一個(gè)由程序設(shè)計(jì)者命名的內(nèi)存區(qū)域。程序設(shè)計(jì)者可以根據(jù)需要指定一些有名字的空間域,把一些全局實(shí)體分別放在各個(gè)命名空間中,從而與其他全局實(shí)體分隔開(kāi)來(lái)。如namespacens1//指定命名空間ns1{inta;doubleb;}現(xiàn)在命名空間成員包括變量a和b,注意a和b仍然是全局變量,僅僅是把它們隱藏在指定的命名空間中而已。14.2.2什么是命名空間《C++程序設(shè)計(jì)》_第14章如果在程序中要使用變量a和b,必須加上命名空間名和作用域分辨符“::”,如ns1::a,ns1::b。這種用法稱(chēng)為命名空間限定(qualified),這些名字(如ns1::a)稱(chēng)為被限定名(qualifiedname)。C++中命名空間的作用類(lèi)似于操作系統(tǒng)中的目錄和文件的關(guān)系。命名空間的作用是建立一些互相分隔的作用域,把一些全局實(shí)體分隔開(kāi)來(lái),以免產(chǎn)生名字沖突??梢愿鶕?jù)需要設(shè)置許多個(gè)命名空間,每個(gè)命名空間名代表一個(gè)不同的命名空間域,不同的命名空間不能同名。這樣,可以把不同的庫(kù)中的實(shí)體放到不同的命名空間中。過(guò)去我們用的全局變量可以理解為全局命名空間,獨(dú)立于所有有名的命名空間之外,它是不需要用namespace聲明的,實(shí)際上是由系統(tǒng)隱式聲明的,存在于每個(gè)程序之中?!禖++程序設(shè)計(jì)》_第14章在聲明一個(gè)命名空間時(shí),花括號(hào)內(nèi)不僅可以包括變量,而且還可以包括以下類(lèi)型:變量(可以帶有初始化);常量;函數(shù)(可以是定義或聲明);結(jié)構(gòu)體;類(lèi);模板;命名空間(在一個(gè)命名空間中又定義一個(gè)命名空間,即嵌套的命名空間)。例如《C++程序設(shè)計(jì)》_第14章namespacens1{constintRATE=0.08;//常量doublepay;//變量doubletax()//函數(shù){returna*RATE;}namespacens2//嵌套的命名空間{intage;}}如果想輸出命名空間ns1中成員的數(shù)據(jù),可以采用下面的方法:cout<<ns1::RATE<<endl;cout<<ns1::pay<<endl;cout<<ns1::tax()<<endl;cout<<ns1::ns2::age<<endl;//需要指定外層的和內(nèi)層的命名空間名《C++程序設(shè)計(jì)》_第14章現(xiàn)在,對(duì)例14.4程序進(jìn)行修改,使之能正確運(yùn)行。例14.5利用命名空間來(lái)解決例14.4程序名字沖突問(wèn)題。修改兩個(gè)頭文件,把在頭文件中聲明的類(lèi)分別放在兩個(gè)不同的命名空間中。//header1.h(頭文件1)#include<string>#include<cmath>usingnamespacestd;namespacens1//聲明命名空間ns1{classStudent//在命名空間ns1內(nèi)聲明Student類(lèi){public:Student(intn,stringnam,inta)14.2.3使用命名空間解決名字沖突《C++程序設(shè)計(jì)》_第14章{num=n;name=nam;age=a;}voidget_data();private:intnum;stringname;intage;};voidStudent::get_data()//定義成員函數(shù){cout<<num<<″″<<name<<″″<<age<<endl;}doublefun(doublea,doubleb)//在命名空間ns1內(nèi)定義fun函數(shù){returnsqrt(a+b);}}//header2.h((頭文件2)#include<string>#include<cmath>《C++程序設(shè)計(jì)》_第14章usingnamespacestd;namespacens2//聲明命名空間ns2{classStudent{public:Student(intn,stringnam,chars){num=n;name=nam;sex=s;}voidget_data();private:intnum;charname[20];charsex;};voidStudent::get_data(){cout<<num<<″″<<name<<″″<<sex<<endl;}doublefun(doublea,doubleb){returnsqrt(a-b);}}《C++程序設(shè)計(jì)》_第14章//mainfile(主文件)#include<iostream>#include″cc14-5-h1.h″//包含頭文件1#include″cc14-5-h2.h″//包含頭文件2usingnamespacestd;intmain(){ns1::Studentstud1(101,″Wang″,18);//用命名空間ns1中聲明的Student類(lèi)定義stud1stud1.get_data();//不要寫(xiě)成ns1::stud1.get_data();cout<<ns1::fun(5,3)<<endl;//調(diào)用命名空間ns1中的fun函數(shù)ns2::Studentstud2(102,″Li″,′f′);//用命名空間ns2中聲明的Student類(lèi)定義stud2stud2.get_data();cout<<ns2::fun(5,3)<<endl;//調(diào)用命名空間ns1中的fun函數(shù)return0;}《C++程序設(shè)計(jì)》_第14章程序能順利通過(guò)編譯,并得到以下運(yùn)行結(jié)果:101Wang18(對(duì)象stud1中的數(shù)據(jù))2.82843(5+3的開(kāi)方值)102Lif(對(duì)象stud2中的數(shù)據(jù))1.41421(5-3的開(kāi)方值)《C++程序設(shè)計(jì)》_第14章在引用命名空間成員時(shí),要用命名空間名和作用域分辨符對(duì)命名空間成員進(jìn)行限定,以區(qū)別不同的命名空間中的同名標(biāo)識(shí)符。即命名空間名::命名空間成員名這種方法是有效的,能保證所引用的實(shí)體有惟一的名字。但是如果命名空間名字比較長(zhǎng),尤其在有命名空間嵌套的情況下,為引用一個(gè)實(shí)體,需要寫(xiě)很長(zhǎng)的名字。在一個(gè)程序中可能要多次引用命名空間成員,就會(huì)感到很不方便。為此,C++提供了一些機(jī)制,能簡(jiǎn)化使用命名空間成員的手續(xù)。14.2.4使用命名空間成員的方法《C++程序設(shè)計(jì)》_第14章(1)使用命名空間別名可以為命名空間起一個(gè)別名(namespacealias),用來(lái)代替較長(zhǎng)的命名空間名。如namespaceTelevision//聲明命名空間,名為T(mén)elevision{…}可以用一個(gè)較短而易記的別名代替它。如namespaceTV=Television;//別名TV與原名Television等價(jià)(2)使用using命名空間成員名using后面的命名空間成員名必須是由命名空間限定的名字。例如usingns1::Student;using聲明的有效范圍是從using語(yǔ)句開(kāi)始到using所在的作用域結(jié)束。如果在以上的using語(yǔ)句之后有以下語(yǔ)句:《C++程序設(shè)計(jì)》_第14章Studentstud1(101,″Wang″,18);//此處的Student相當(dāng)于ns1::Student上面的語(yǔ)句相當(dāng)于ns1::Studentstud1(101,″Wang″,18);又如usingns1::fun;//聲明其后出現(xiàn)的fun是屬于命名空間ns1中的funcout<<fun(5,3)<<endl;//此處的fun函數(shù)相當(dāng)于ns1::fun(5,3)顯然,這可以避免在每一次引用命名空間成員時(shí)都用命名空間限定,使得引用命名空間成員方便易用。但是要注意:在同一作用域中用using聲明的不同命名空間的成員中不能有同名的成員。例如usingns1::Student;//聲明其后出現(xiàn)的Student是命名空間ns1中的Studentusingns2::Student;//聲明其后出現(xiàn)的Student是命名空間ns2中的StudentStudentstud1;//請(qǐng)問(wèn)此處的Student是哪個(gè)命名空間中的Student?產(chǎn)生了二義性,編譯出錯(cuò)?!禖++程序設(shè)計(jì)》_第14章(3)使用usingnamespace命名空間名能否在程序中用一個(gè)語(yǔ)句就能一次聲明一個(gè)命名空間中的全部成員呢?C++提供了usingnamespace語(yǔ)句來(lái)實(shí)現(xiàn)這一目的。usingnamespace語(yǔ)句的一般格式為usingnamespace命名空間名;例如usingnamespacens1;聲明了在本作用域中要用到命名空間ns1中的成員,在使用該命名空間的任何成員時(shí)都不必用命名空間限定。如果在作了上面的聲明后有以下語(yǔ)句:Studentstud1(101,″Wang″,18);//Student隱含指命名空間ns1中的Studentcout<<fun(5,3)<<endl;//這里的fun函數(shù)是命名空間ns1中的fun函數(shù)《C++程序設(shè)計(jì)》_第14章在用usingnamespace聲明的作用域中,命名空間ns1的成員就好像在全局域聲明的一樣。因此可以不必用命名空間限定。顯然這樣的處理對(duì)寫(xiě)程序比較方便。但是如果同時(shí)用usingnamespace聲明多個(gè)命名空間時(shí),往往容易出錯(cuò)。因此只有在使用命名空間數(shù)量很少,以及確保這些命名空間中沒(méi)有同名成員時(shí)才用usingnamespace語(yǔ)句。《C++程序設(shè)計(jì)》_第14章以上介紹的是有名字的命名空間,C++還允許使用沒(méi)有名字的命名空間,如在文件A中聲明了以下的無(wú)名命名空間:namespace//命名空間沒(méi)有名字{voidfun()//定義命名空間成員{cout<<″OK.″<<endl;}}由于命名空間沒(méi)有名字,在其他文件中顯然無(wú)法引用,它只在本文件的作用域內(nèi)有效。無(wú)名命名空間的成員fun函數(shù)的作用域?yàn)槲募嗀。在文件A中使用無(wú)名命名空間的成員,不必

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝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ù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 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)論