C++primer-3TH詳細答案打印版_第1頁
C++primer-3TH詳細答案打印版_第2頁
C++primer-3TH詳細答案打印版_第3頁
C++primer-3TH詳細答案打印版_第4頁
已閱讀5頁,還剩169頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

練習2.16語言不應該支持所謂的Type-constraint(類型約束)語法。有三個理由:它會使templates更復雜。無論如何用戶總是能夠檢驗Type是否提供了“得以順利將此template實例化”的必要函數;如果他們遺漏了,編譯器會對此實例化行為發出錯誤訊息,指出某個函數有所謂的隱式約朿(implicitconstraint)?而不只是發出錯誤信息說這個template的實例化是錯誤的。請看L&L練習2.16出現的腳注,其中所描述的習慣。練習2.1フ這是正確的語意。另ー種做法是在classtemplate被產生出來時即檢驗此一template的所有可能用法。這會造成編譯時間的嚴重浪費。不過此種語意的缺點是,以某個類型將template實例化,在程序A中也許可以有效運作,在程序B中也許不能成功,視被調用的函數而定。練習2.18以下事情有可能在這個函數中出現錯誤:file_name可能是個空字符串(emptystring)〇ifstreamconstructor可能無法打開文件,即使file_name是ー個有效字符串。文件起始處可能并不內含一個int數值。elem_cnt可能收到ー個不正確的值:太大、或0^或負值。allocate_array()為elem_cnt個元素分配空間,可能失敗。while循環讀到太多元素:這個循環會在遇到EOF(或讀取int失敗)時結束,但它并未考慮pi所指數組的大小。sort_array〇會對pi所指向的數組排序,該數組假想有elem_cnt個元素。但是while循環可能在讀入elem_cnt個元素之前就結束,因為輸入端的ints個數不足,并因而造成數組的剩余部分沒有初值。sort_array()可能會收到ー個無效指針pi,因為此處并未檢查allocate_array()的返回值:這個錯誤可能會造成程序在循環內當掉。練習2.19int*alloc_and_init(stringfile_name)(ifstreaminfile(file_name.c_str());intelem_cnt;infile>>elem_cnt;try(int*pi=allocate_array(elem_cnt);intelem;intindex=0;while(cin>>elem)pi[index++]=elem;sort_array(pi,elem_cnt);register_data(pi);returnpi;)catch(constnoMem&n){cout<<nallocate_array()error"<<n<<endl;}catch(inti){cout<<"sort_array()errorn<<i<<endl;catch(conststring&s){cout<<nregister_data()error"<<s<<endl;練習2.20int*alloc_and_init(stringfile_name)(try(ifstreaminfile(file_name.c_str());if(!infile)throwncannotopenfileM;intelem_cnt;infile>>elem_cnt;if(!infile||elem_cnt<=0)throw"invalidelem_cnt";int*pi=allocate_array(elem_cnt);intelem;intindex=0;while(cin>>elem){if(index>=elem_cnt)throw"toomanyinputelements";pi[index++]=elem;}sort_array(pi,index);register_data(pi);returnpi;)catch(constnoMem&n){cout<<"allocate__array()errorw<<n<<endl;throw;/Z重新丟出(rethrow)exception以便通知用戶)catch(inti){cout<<"sort_array()error"<<i<<endl;throw;)catch(conststring&s){cout<<"register_data()error"<<s<<endl;throw;)catch(constchar*s){cout<<"error:"<<s<<endl;throw;練習2.21a.利用“前置修飾詞表示法”來訪問Exercisenamespace中的類型定義。b.利用usingdeclaration來訪問類型定義。c.利用namespace別名機制(aliasmechanism)〇d.利用usingdirective〇a.利用前置修飾詞表示法來訪問Exercisenamespace中的類型定義。intmain()(//使用修飾詞表示法(*qualifiednamenotation1)constintsize=1024;Exercise::Array<Exercise::String>as(size);Exercise::List<int>il(size);//...Exercise::Array<Exercise::String>*pas=newExercise::Array<Exercise::String>(as);Exercise::List<int>*pil=newExercise::List<int>(il);Exercise::print(*pas);}b.利用usingdeclaration來訪問類型定義。intmain()(//使用,usingdeclaration,usingExercise::String;usingExercise::Array;usingExercise::print;usingExercise::List;constintsize=1024;Array<String>as(size);List<int>il(size);〃…Array<String>*pas=newArray<String>(as);List<int>*pil=newList<int>(il);print(*pas);)c.利用namespace的別名機制(aliasmechanism)〇intmain(){//使用namespacealiasnamespaceE=Exercise;練習2.22constintsize=1024;E::Array<E::String>as(size);E::List<int>il(size);〃…E::Array<E::String>*pas=newE::Array<E::String>(as);E::List<int>*pil=newE::List<int>(il);E::print(*pas);d.利用usingdirective〇intmain()(//使用,usingdirective*usingnamespaceExercise;constintsize=1024;Array<String>as(size);List<int>il(size);//...Array<String>*pas=newArray<String>(as);List<int>*pil=newList<int>(il);print(*pas);)練習2.22(a)vector<string>svecl(pals,pals+5);svecl是ー個由strings組成的vector,并以字符串所組成的數組pals作為初值。(b)vector<int>ivecl(10);ivec!是個vector,擁有!0個ints,每ー個都被初始化為〇〇(c)vector<int>ivec2(10,10);ivec2是一個vector,擁有!0個ints,每ー個都被初始化為10。vector<string>svec2(svecl);svec2是一個vector,并以vectorsvecl作為初值。vector<double>dvec;dvec是ー個空的vector,內部元素的類型是doubles。練習2.23template<classelemType>elemTypemin(constvector<elemType>Svec);下面是完整的程序:#include<iostream>#include<vector>usingnamespacestd;template<classelemType>elemTypemini(constvector<elemType>Svec)(elemTypeminimum;if(vec.size〇>=1)minimum=vec[0];elsethrownEmptyvector-index";for(inti=1;i<vec.size();i++)if(vec[i]<minimum)minimum=vec[i];returnminimum;練習2.23template<classelemType〉

elemTypemin2(constvector<elemType>&vec)vector<elemType>::const_iteratoriter=vec.begin();elemTypeminimum;if(iter<vec.end())minimum=*iter;elsethrowHEmptyvector-iterator";for(++iter;iter<vec.end();++iter)if(*iter<minimum)minimum=*iter;returnminimum;)intmain()intarray[]{9,4,5,intarray[]{9,4,5,6,1,3,7,2,0);vector<int>a(array,array+10);cout<<"shouldbe0:"<<mini(a)<<endl;cout<<"shouldbe0cout<<"shouldbe0:"<<mini(a)<<endl;cout<<"shouldbe0:"<<min2(a)<<endl;vector<int>b(array,array+9);cout<<"shouldbe1:"<<mini(b)<<endl;cout<<"shouldbe1:"<<min2(b)<<cout<<"shouldbe1:"<<mini(b)<<endl;cout<<"shouldbe1:"<<min2(b)<<endl;vector<int>c;try(cout<<"shouldbe1:"<<mini(c)<<endl;cout<<"shouldbe1:"<<min2(c)<<endlcout<<"shouldbe1:"<<mini(c)<<endl;cout<<"shouldbe1:"<<min2(c)<<endl;catch(char*s){<<endl;cerr<<"Exception:"<<sreturn0;)<<endl;其中函數miniO和min2〇十分類似,都使用for循環,都在vector成空的時候丟出?個exception〇mini()使用索引法:for(inti=1;i<vec.size();i++)if(vec[i]<minimum)minimum=vec[i];min2()使用iterator。兩者的參數都是?個constvector<elemType>&vec,所以我們不能使用截至目前我們所見的般性iterator。我們必須改用const_iterator(見L&L,12.4節):for(++iter;iter<vec.end();++iter)if(*iter<minimum)minimum=*iter;我們使用解引用(dereference)operator*來取用vector內的數據。主:程序以數組array作為vectora的初值:intarray[]={9,4,5,6,1,3rフ,8,2,0);vector<int>a(array,array+10);然后調用函數mini()和min2()?獲得的結果相同。我們也試了另一個vectorb?以數組的前九個元素作為初值,并以ー個空的vectorc練習exception處理機制。練習3.1文字常量,a,表示單一字符a,類型為char。L'a,也表示單一字符a,但其類型為wchar一t,因為前導詞L代表“寬字符”。文字常量"a"和L"a"都是字符串,內含單一字符a和一個null字符(或是null寬字符)。"a"的類型是“常量字符所形成的數組”,L"a"的類型則是“常量寬字符所形成的數組”。10,10u,10L,10uL,012,OxC以上全都表示十進制整數常量10。其中10,012和OxA的類型都是int,因為它們沒有任何修飾詞。012的前導詞0表示這是個ハ進位常量,OxA的前導詞Ox則表示它是一個十六進制常量。10u的類型是unsignedint,10L的類型是long,10uL的類型是unsignedlong?3.14,3.14f,3.14L這些浮點數文字常量的類型都不相同。沒有任何修飾詞的3.14,類型為double(雙精度,這是預設類型)。3.14f表現的是單精度浮點數,3.14L表現的是多精度浮點數。第3章練習3.2(〇合法,但是其結果未有定義,因為“將兩個不同類型的字符串連接起來,其行為未有定義”(L&L,p.78;簡體版p.64)。(e)和(f)不合法。3.14UL之所以不合法是因為U(unsigned)被施加于?個浮點數文字常量身上。(f)字符串需要在第一行最后加上一個倒斜線,才能延續到第二行:(f)"multipleline\comment"另ー種做法是把分據兩行的單ー長字符串寫為兩個字符串:(f)"multipleline”"comment”練習3.3(d)和(e)是不合法的。doublesalary=wage=9999.99;此處wage被使用之前未曾先定義過。修正做法之一是把兩個變量分開定義、設初值,像這樣:doublesalary=9999.99,wage=9999.99;cin>>intinput_value;使用cin時并不允許“同時定義變量”。正確的做法是先定義好變量,再將?個值讀入變量之中:intinput_value;cin>>input_value;練習3.4所謂lvalue(左值),是變量的地址,或是某個“代表對象在內存中的位置”的表達式。所謂rvalue(右值),就是變量的值。見L&L,3.2.1節。變量名稱如果出現在賦值(assignment)運算符的左側,它就是ー個lvalue。變量名稱或文字常量如果出現于賦值(assignment)運算符的右側,它就是?個rvalue〇例如:ivar=val+2;此處的ivar是個lvalue,val和2都是rvalues〇練習3.5(a)externstringname;stringname("exercise3.5a");第一個句子,externstringname;是name的聲明,告訴編譯器說,name所代表的對象,其類型為string。此行并未分配內存。第二個句子,stringname("exercise3.5a");是個定義,告訴編譯器說,name所代表的對象的類型為string,并進行內存分配操作,同時設好初值。externvector<string>students;vector<string>students;第一個句子是students的聲明,告訴編譯器說其類型為ー個“由strings組成的vector”。沒有分配任何內存。第二個句子是個定義,告訴編譯器說,students所代表的對象類型為“由strings組成的vectorM〇此行將分配內存,并以vector的defaultconstructor進行初值設定工作。string的defaultconstructor不會被調用,因為vector是空的。練習3.6(a),(c),(d)和(e)無效。(a)和(c)之所以無效,因為它們企圖使用保留字。double和namespace都是保留字(關鍵字)。(d)內含一個無效的?-I符號,(e)的名稱以數字開頭。以ド是修訂后的結果:(a)doublepi=3.14159;stringnamespace_string;stringcatch_22;charone_or__two=*1*;char_l_or_2=11*;/Z另一種做法練習3.7兩個string對象都將藉由stringclass的defaultconstructor加以初始化。global_int會被初始化為0,而local_int不會被初始化(所以其初值可能是任意數值)。Global(全局)變量和對象可被其它函數訪問:local(局部)變量和對象只在它們定義所在的塊內可見。見L&L,3.2.3節。練習3.833練習3.8該語句有誤,因為它企圖將一個int?數值賦值給ー個int對象。另ー個錯誤:pi3是個“指針的指針”,其值為0,而解引用null指針會造成運行時刻錯誤。*pi2=*pi3;該語句也有一個錯誤,理由與(a)相同。ival=pi2;道理相同,企圖將一個int?數值賦值給ー個int對象,是錯誤的行為。pi2=*pil;該語句有誤,因為它將一個int數值賦值給個int?對象。pil=*pi3;此處pil被賦值為pi3所指內容。這個操作是合法的。如果pi3并未指向一個有效地址,該行會發生運行時刻錯誤,但行為不可預期。ival=*pil;由于(e)會在運行時刻失敗,所以pil未被適當地初始化。對ー個未被正確初始化的指針進行解引用操作,會發生錯誤。如果此行之前的那些定義都成立,那么此行會將iva!賦值給ivalo這是因為pil被初始化為ival的地址,所以解引用pil會獲得ival的值,此值再被賦值給ivalopil=ival;此行錯誤,因為pil的類型是int★,而ival的類型是int。pi3=&pi2;此行正確,因為pi3的類型是int**,而pi2的類型是int*,因此&pi2的類型也是int**。練習3.9上述第二行將指針pi前進!,024個位置。如果pi是個int指針,那么在“int為32位”的環境下就是前進了4096個字節。如果pi最初指向ー個有著適當大小的數組(亦即ival2代表該數組內的某元素),那么這就不見得是個錯誤。練習3.10問題在于pi2定義時并未指向ー塊已獲分配的空間,而foobar()卻企圖對其參數所指的內存做寫入操作。本例中的pi2初值為0,所以foobar()試圖將!024寫入地址為0的內存中。除非指針被賦以ー個實際值,否則運行時刻的行為沒有定義。下面是修改方式之一:intfoobar(int*pi){if(pi){*pi=1024;return*pi;練習3.1135}else{return0;))intmain()(intival2=0;intival=foobar(&ival2);return0;)這次我們不再定義ー個int指針,而是定義ー個int對象ival2,并將其地址傳給foobar()。問題真正的解決點是在foobar()中對pi的測試操作。任何人如果事先不知道指針是否有值,都應該先行測試,然后オ對指針做操作.練習3.11由于指針扮演如此突顯的角色,執行時期的任何指針檢驗操作都會導致不可接受的效率成本。編譯時期的指針檢驗工作很困難,因為往往無法決定這些指針在執行時期會有什么值。程序員之間流傳著指針的某些使用準則,包括:總是將指針初始化、不要無視編譯器發出的錯誤信息或警告信息如“強制轉換為指針變量(explicitcaststopointervariables)”等等。練習3.12(a)charch="Thelong,windingroad";這是錯誤的,因為ch是單個字符,卻被初始化為一個字符串。intival=Sch;char*pc=&ival;以上兩行都不正確,因為它們企圖以不兼容的數值作為變量的初值:把char?交給int,把int?交給char?。stringst(&ch);語法上正確。但是如果ch未被正確地初始化,st可能會有不如預期的結果。另ー個重點是,string(constchar*)constructor期望獲得一個以nul!為結束符號的char數組。pc=0;正確。st=pc;語法正確。執行結果視pc的值而定。(g)ch=pc[0];語法正確。執行結果視pc的值而定。pc=st;pc='0*;以上兩行都不正確,因為沒有任何隱式轉換可以將string或constchar轉換為char(j)st=&ival;這一行也不正確,因為沒有任何可接受的轉換行為可以將int?轉換為stringo(k)ch=*pc;語法正確。執行結果視pc的值而定。(1)*pc=ival;以上操作將一個int數值賦值給ー個char。可能會發生上溢(overflow)情況。執行結果視ival和pc的值而定。練習3.13第一個while循環會持續累加ent,直到st的值為0。如果st并不是從〇開始起算,那么循環會一直進行,直到st的值爆掉(超過st的容量)為止。第二個while循環會累加ent,直到st所指的值為〇〇如果st指向ー個C-style字符串,那么循環會持續進行,直到字符串的尾端為止。練習3.14這些程序都進行1,000,000次循環,循環內對字符串做內存分配、復制、比較、內存釋放操作。一開始,有一個基本字符串在循環之外獲得初值。循環之內則決定基本字符串的長度。在C-style版本內,這?長度值用來分配?個新的ehar數組,并將基本字符串復制到該數組。class版本內則定義一個新字符串并以基本字符串作為初值。接下來基本字符串被拿來和新字符串比較,如有差異就將錯誤計數器加1。最后,C-style版明白釋放新字符串的空間,class版則依賴classdestructor完成內存釋放操作。class版所花時間比較少,主要原因是字符串長度系儲存于stringclass內,成為其成員之ー。當C-style版調用strlen時,整個字符串必須被走過一遍,才能知道其長度。注意,在我的系統中,class版比C-style版慢。某些系統在string身上使用referencecounting(引用計數)技巧,那么就會比較快。(譯注:關于“引用計數”技巧,可參考More.c"veC++(ScottMeyere著,侯捷譯,培生2000)的條款29)。練習3.15加上某些可作用于子字符串身上的額外操作,可能會有益處。例如在一個字符串中搜尋子字符串,以及対子字符串進行替換、復制、比較或串接等操作。其它有益的操作還包括大小寫轉換、文字轉為整數(atoi())、文字轉為浮點數(atof())等等。練習3.16?個可被修改的對象,類型為int。constintic;ー個固定不變(不可修改)的對象,類型為int。constint*pic;pic是個指針,指向一個類型為int的常量對象。pic本身可被修改,但是它所指的對象內容不可修改。int*constcpi;epi是常量指針,指向?個類型為int的對象。cpi不可修改,但其所指對象可以修改。constint*constepic;epic是常量指針,指向ー個類型為int的常量對象。不論epic或是其所指對象都不可以修改。(b),(d),ft(e)都是不合法的定義,因為常量對象必須有初值。任何人如果企圖修改常量對象,會引發錯誤信息。練習3.17合法。i不是常量,初值設定為-1。constintic=i;合法。ic是常量,初值設定為i.之后無法修改其值。constint*pic=⁣合法。pic是個指針,指向ー個類型為int的常量對象。pic可被修改,但其所指對象是常量。由于ic已有定義,所以上述初始化行為是合法的。int*constcpi=⁣不合法。cpi是ー個常量指針,指向int,但ic卻是個constintoconstint*constepic=⁣合法。此處epic是ー個常量指針,指向constint,ic的類型也是constinto不論是epic或其所指對象ic,都無法修改。練習3.18(a)i=ic;合法。i不是常量,所以可以被修改。pic=⁣合法。pic不是常量,所以它可以被修改,不過它所指的對象的類型必須是constintocpi=pic;不合法。cpi是常量,所以它不可以被修改。pic=epic;合法。pic不是常量,所以它可以被修改,但是它所指的對象的類型必須是constint.epic=⁣不合法。epic是常量,所以不能被修改。ic=*cpic;不合法。ic是常量,所以不能被修改。練習3.19(b)intSrvall=1.01;無效。能夠儲存文字常量者,必須是ー個constreferenceo可修改如下:constint&rvall=1.01;int&rval3=&ival;無效。rval3的類型是int而非int?〇可修改如下:int&rval3=ival;或int*const&rva13=fitival;(f)int&rval4=pi;無效。rval4的類型是int而非int?〇可修改如下:int*const&rval4=pi;(h)int&*prvall=pi;無效。ー個“指向reference"的指針是不合法的。可修改如下:int*&prvall=pi;(j)constint&*prval2=&ival;無效。?個”指向reference',的指針是不合法的。可修改如下:int*const&prval2=&ival;練習3.20(a)rvall=3.14159;無效,因為文字常量需要一個constreference來容納。prvall=prval2;無效,因為prvall和Prval2都是“指向reference"的指針,而那是不合法的。prval2=rvall;無效,因為prval2是?個“指向reference"的指針,而那是不合法的。*prval2=ival2;無效,因為prval2是ー個“指向referenceM的指針,而那是不合法的。練習3.21第一個語句正確:它將iva1的地址設給指針pi。第二個語句錯誤,因為我們無法將int*(iva!的地址)轉換為constint(referenceri的類型是constint)〇最后ー個語句錯誤,因為rval未曾定義。練習3.22(a)intia[buf_size];不合法,因為buf_size不是一?個常量。(b)intia[get_size()];不合法,因為get_size()不是ー個常量表達式。intia[2*7-14];不合法,因為分配大小為〇的數組是錯誤的行為。charst[11]="fundamental";不合法。沒有足夠的空間來放置字符串初值。應該再多一個元素,用來放置字符串尾端的い。,字符。練習3.23數組的索引從〇開始。因此,ia的元素索引為〇-9。但是for循環內的索引碼卻是"10。下面是修改后的版本:intmain(){constintarray_size=10;intia[array_size];for(intix=0;i<array_size;++ix)ia[ix]=ix;//??.)練習3.24(a)vector<vector<int>>ivec;正確。vector<int>ivec={0,1,1,2,3,5,8};不正確。這種初始化形式無法用于vector對象身上。vector<int>ivec(ia,ia+7);正確。vector<string>svec=ivec;不正確。”int所組成的vector”無法轉換為"string所組成的vector"〇vector<string>svec(10,string("null"));正確。練習3.25boolis_equal(constint*ia,intia_size,constvector<int>&ivec)(inti=0;for(vector<int>::const_iteratorit=ivec.begin();it!=ivec.end();++it){//如果到達數組尾端,不要再繼續前進if(i==ia_size)break;//如果發現?個不同值,立刻返回

if(ia[i++]!=*it)returnfalse;returntrue;卜面是測試程序。intmain()constintasize=7;intia6eq[6]0,ェ,1,3,intia6ne[6]={0,3,3,intia8eq[8]={0,1,2,3,5,8,13);intia8ne[8]={0,1,2,3,5,9,13);intiane[asize]1,1,2,3,5,8};intia[intia6eq[6]0,ェ,1,3,intia6ne[6]={0,3,3,intia8eq[8]={0,1,2,3,5,8,13);intia8ne[8]={0,1,2,3,5,9,13);intiane[asize]1,1,2,3,5,8};intia[asize]={0,1,2,3,5,);vector<int>ivec(ia,ia+asize);cout<<nis_equal(ia6eq,6,ivec):";if(is_equal(ia6eq,6,ivec))cout<<"equal\n";elsecout<<"notequal\n";cout<<"is_equal(ia6ne,6,ivec):";if(is_equal(ia6ne,6,ivec))cout<<"equal\n";elsecout<<"notequal\n";cout<<"is_equal(ia8eq,8,ivec):";if(is_equal(ia8eq,8,ivec))cout<<"equal\n";elsecout<<"notequal\n";cout<<"is_equal(ia8ne,8,ivec):";if(is_equal(ia8ne,8,ivec))cout<<"equal\n";elsecout<<"notequal\n";cout<<"is_equal(ia,asize,ivec):";if(is_equal(ia,asize,ivec))cout<<"equal\n";elsecout<<"notequal\n";cout<<"is_equal(iane,asize,ivec):";if(is_equal(iane,asize,ivec))cout<<"equal\n";elsecout<<"notequal\n";return0;}練習3.26我們知道那些重復的代碼涉及ーsize和一string的初始化行為。我將這些代碼抽取出來放進兩個privateoverloadedmemberfunctionsinit()?并在constructors^assignmentoperators內的適當地點放置適當的函數調用。首先,在Stringclass的private區段內為新函數加上聲明:private:int_size;char*_string;voidinit(constchar*);voidinit(constStrings);共同的初始化程序代碼現在被我放進兩個新函數內:inlinevoidString::init(constchar*s)(if(!s){__size=0;_string=0;}else{_size=strlen(s);_string=newchar[_size+l];strcpy(_string,s);)}inlinevoidString::init(constStringSrhs)(_size=rhs?ーsize;if(!rhs,ーstring)_string=0;else(_string=newchar[_size+l];strcpy(_string,rhs.-string);})接下來更改constructors?以便調用init():inlineString::String()(init(0);inlineString::String(constchar*s)

init(s);inlineString::String(constString&rhs)(init(rhs);)assignmentoperators的修改方式極為類似:inlineStringsString::operator=(constchar*s)delete[]_string;init(s);return*this;inlineStringsString::operator=(constStringSrhs)if(this!=srhs){delete[]__string;init(rhs);return*this;}練習3.27修改方式非常直接易懂。首先為新字母加上對應的計數器:bCnt=0,dCnt=〇,fCnt=0,sCnt=0,tCnt=0接下來在switch語句中為這些輔音加上新的cases。我們還是需要為每?個輔音累加notVowe!的值。'B':'B':++bCnt;'D':++dCnt;'F':++fCnt;'S':++sCnt;'T':++tCnt;case1b*:casecase1d*:casecase*f*:casecase's':casecase't':case++notVowel;break++notVowel;break++notVowel;break++notVowel;break++notVowel;break最后,修改輸出部分以顯示輔音個數。下面是整個程序。?include"string.hHintmain()intaCnt=0,eCnt=0,iCnt=0,oCnt=0,uCnt=0,bCnt=0,dCnt=0,fCnt=0,sCnt=0,tCnt=0,theCnt=0,itCnt=0,wdCnt=0,notVowel=0;Stringbuf,the("the"),it("it")intaCnt=0,while(cin>>buf){++wdCnt;cout<<buf<<'';if(wdCnt%12==0)cout<<endl;if(buftheIIbuf“The”)++theCnt;elseif(bufIIbuf"It")++itCnt;for(intix=0;i<buf.size();++ix){switch(buf[ix])casecaseA*break;casecaseE*:++eCnt;break;casericaseェ?:++iCnt;break;casecasecasecase0fUfcase『bcaseBfcase1dcasecaserfcaseFTcasecaseS'casecaseTTdefault:++notVowel;cout<<"\n\n"<<<<<<<<<<<<<<<<<<<<<<<<<<<<:++oCnt;:++uCnt;:++bCnt;:++dCnt;:++fCnt;:++sCnt;:++tCnt;break;'Wordsread:"<<wdCnt<<break;break;++notVowel;++notVowel;++notVowel;++notVowel;++notVowel;"\n\n""the/The:"<<theCnt<<"\n"it/It:"<<itCnt<<"\n\n"non-vowelsread:"<<notVowel<<"\nTirbTdffTtreturn<<<<<<<<<<<<<<<<<<<<0;aCnteCntiCntoCntuCntbCntdCntfCntsCnttCnt<<<<<<<<<<<<<<<<"\n""\n""\n\n""\n""\n""\n""\n"endl;練習3.28下面是其實現代碼:inlineintString::count(charch)constintchCnt=0;for(intix=0;i<_size;++ix)break;break;break;break;break;\n"if(_string[ix]returnchCnt;ch)++chCnt;我必須修改原先的程序以測試這一新函數。對于計算元音個數而言,這是非常缺乏效率的做法,但這使我們得以拿兩種做法互相比較。#include"string.h"intmain()(intaCnt=0,eCnt=0,iCnt=0,oCnt=0,uCnt=0,theCnt=0,itCnt=0,wdCnt=0;Stringbuf,the("the"),it("it");while(cin>>buf){++wdCnt;cout<<buf<<**;if(wdCnt%12==0)cout<<endl;if(buf==theIIbuf=="The")++theCnt;elseif(buf==itIIbuf=="It")++itCnt;aCnt+=buf.count(1a*);aCnt+=buf.count(*A*);eCnt+=buf.count(*e');eCnt+=buf.count(*E');iCnt+=buf.count(1i');iCnt+=buf.count(*I*);oCnt+=buf.count(*o');oCnt+=buf.count(10');uCnt+=buf.count(1u');uCnt+=buf.count(fU');)cout<<n\n\n"<<,Wordsread:"<<wdCnt<<"\n\n"<<"the/The:"<<theCnt<<"\n"<<"it/It:"?itCnt?"\n\n"<<aCnt<<"\n"<<eCnt?"\n"<<iCnt?w\n"<<oCnt?"\n"<<u:"<<uCnt<<endl;return0;練習3.29ド面是我們的新函數:inlineStringString::operator+(constString&rhs)constStringnewString;if(!rhs.ーstring)newString=*this;elseif(!_string)newString=rhs;else(newString._size=_size+rhs._size;newString._string=newchar[newString._size+1];strcpy(newString?一string,_string);strcat(newString?一string,rhs.-string);)returnnewString;}我們首先檢查是否有任何一個字符串是空字符串。如果有,就返回非空字符串的ー份副本。如果兩者都是空字符串,則返回其中一個空字符串的新副本。如果兩個操作數都不是空字符串,我們就計算新的大小:newString.__size=_size+rhs._size;并分配內存給新字符串使用:newString.__string=newchar[newString,_size+1];利用C標準程序庫所提供的函數,我們復制第一個字符串:strcpy(newString._string,.string);然后為它接上第二個字符串:strcat(newString?ーstring,rhs..string);最后,返回新字符串。下面是?個小型測試程序。#include"string.hMintmain(){Stringempty;Stringstrl("Thecow");Stringstr2("jumpedoverthemoon.");Stringstr3=strl+str2;cout<<"String1:|"<<strl<<"|\n";cout<<"String2:I"<<str2<<"|\n";/Z測試空字符串的連接str3=str3+empty;str3=empty+str3;cout<<"String1+String2:|"<<str3<<"|\n";return0;練習4.1表達式:dvall/dval2;是浮點數除法運算,因為其操作數類型為double。運算結果為double數值:3.33333…表達式:ivall/iva12;是整數除法運算,因為其操作數類型為int。運算結果為int數值:30練習4.2i%2==0我們使用余數操作符考(modulusoperator),將整數i除以2。如果余數為0,則i為偶數。練習4.3MSVisualC++的頭文件通常列于.../DevStudio/VC/include目錄下;UNIX和Linux中,表頭文件通常列于/usr/include目錄下。至于我手上的系統,則是limits內部含入了!imits.h和float.h,以及其它ー些template定義。練習4.4(a)ptr->ival!=0正確。ival!=jval<kval有可能正確。表達式右端檢査jval是否小于kval,如果是,就返回true(1);否則返回false(0)〇然后將此結果與ival做比較,看看兩者是否相等。如果這是程序員所要的,那么此表達式便屬正確。如果jval是int,而kval是unsignedint,此表達式可能不具移植性。(c)ptr!=0&&*ptr++正確。此表達式先檢査ptr是否不為0,如果是,便中止計算并返回false。如果不是,則表達式右半部再檢査ptr所指的對象是否不為0,然后將指針ptr累加。如果“ptr不為〇”,而且“ptr所指的對象不為〇”,則整個表達式的結果為true,否則結果為false。ival++&&ivalC++語言保證,凡表達式中含有logicalAND(&&)和logicalOR(||)操作符,其計算方式必定由左至右(譯注:MoreEffectiveC++條款7有更詳細的討論)。因此ival++首先被計算;當ival不為0,ivalオ會在累加之后再被計算一次(于&&右側)。以下是一些可能的結果:ival=-1resultisfalse:-1&&0ival=0resultisfalse:0ival=1resultistrue:1&&2不論結果如何,變量iva!都會被累加。vec[ival++]<=vec[ival];該式無法移植,因為C++并不保證表達式的左端會先被計算。是的,即使此表達式在你的系統中奏效,一旦將它移植至另一個系統,不能保證正常運作。練習4.5這是可以接受的利益交換。因為“效率”和“潛在最佳化”的優點,比潛藏的風險更重要。?旦我們意識到二元操作符對操作數計算次序并無定義,我們應該將這?認知牢牢記住,永銘心頭。練習4.6賦值操作(assignments)不正確。因為C++并未提供ー個“由指針轉為int”的自動類型轉換操作。我們可以將上述賦值操作改為:fval=ival=0;pi=0;或其它形式,只要不將指針類型和非指針類型混在ー起即可。練習4.7(a)if(ptr=retrieve_pointer〇!=0)由于inequality操作符(!=)btassignment操作符(=)有較髙的優先級,所以retrieve_pointer()返回的值會先被拿來和〇相比,比較結果(true或false)再賦值給ptr。下面是修訂版:if((ptr=retrieve_pointer())!=0)(b)if(ival=1024)assignment操作符(=)會將右側表達式的運算結果指定給左側的-個左值(lvalue)〇該表達式似乎應該是ー個比較操作而不是ー個賦值操作。下面是修訂版:if(ival==1024)ival+=ival+1;compoundassignment(復合賦值)操作符:aop=expression的意義是:a=aop(expression)因此,原表達式的意思是:ival=ival+(ival+1);推測撰寫者的實際意圖是要將ival累加1。改寫后的版本為:ival+=1;或者采用L&L4.5節的做法:ival++;練習4.9以下是operator-=:inlinecomplex<double>&operator-=(complexedouble>&cval,doubledval)(returneval-=complex<double>(dval);}以下是operator*=:inlinecomplex<double>&operator*=(complex<double>&cval,doubledval)(returneval*=complex<double>(dval);)以下是operator/=:inlinecomplex<double>&operator/=(complex<double>&cval,doubledval){returneval/=complex<double>(dval);)練習4.10下面是前置(prefix)increment操作符:inlinecomplex<double>&operator++(complex<double>&cval)(returneval+=complex<double>(1);)后置(postfix)increment操作符極為類似,但它是以by-value方式(而非by-reference方式)返回原本的eomplex值。該式多了一個額外的int參數,以便與前置區別。inlinecomplex<double>operator++(complex<double>&cval,int)complex<double>oldcval=eval;eval+=complex<double>(1);returnoldcval;}下面是main()修改后的版本,其中調用前置和后置兩種increment操作符:intmain()(complex<double>eval(4.0r1.0);cout<<eval<<"\n";++cval;cout<<eval<<endl;cout<<cval++<<endl;cout<<eval<<endl;return0;}練習4.11(a)vector<string>svec(10);正確。svec是由10個strings所組成的vector。vector<string>*pvecl=newvector<string>(10);正確。pvecl是個指針,指向ー個由10個strings所組成的vector。vector<string>**pvec2=newvector<string>[10];錯誤。表達式右端產生?個指針,指向,個stringvector;發達式左端則是?個“指向stringvector的指針”的指針,兩者不一致。vector<string>*pvl=&svec;正確。pvl指冋stringvectorsvecovector<string>*pv2=pvecl;正確。pv2是ー個指向stringvector的指針。經過初始化,pv2所指的對象與pvecl所指的對象相同.deletesvec;不合法。我們不能對ー個對象(而非指針)施以delete的操作。(譯注:svec并非以new產生)(g)deletepvecl;正確。它會釋放pvecl所指的空間。delete[]pvec2;語法正確?執行期的行為則視pvec2的值而定。deletepvl;這是合法的,編譯器無法偵測出任何錯誤。但它其實是錯誤的,因為我們不能對ー個“并非以new產生出來的指針”施以delete操作。deletepv2;也對,也不對.對的原因是,pv2所指對象與pvec!所指對象相同,而該對象系由new產生.不對的原因是,Pv2所指的對象已經在(g)被釋放過了。將同?個對象釋放?次以上是不合法的,會在執行期產生?個未定義的行為。我想,沒有任何人會對所謂未定義的行為有追根究底的興趣.練習4.12(b)uil&&ui2(d)uil||ui2以下數字所呈現的是二進制數一盡管它們看起來像是C++語言中的ハ進制數(譯注:因為都以〇開頭)。uil&ui20101&0111==>0101我們以二進制方式表現uil和Ui2?以四個位表現二進制的數值5I其值為0101,7則為0111.由于bitwiseAND操作符(&)只在相同位置上的位都是1時才返回!.所以結果為0101.uil&&ui2true&&true==>true其結果為true,因為logicalAND操作符(&&)只有在兩個操作數都不為〇(或說都是true)時ォ返回trueo此外,logicalAND操作符保證其計算次序由左至右;運算結果ー經確立,立刻中止計算。(c)uil|ui20101I0111==>0111只要相同位置I:的位有一個是!,bitwiseOR操作符(I)便返回1.本題結果是0111.(d)uilIIui2trueIItrue==>true本題結果是true,因為只要兩個操作數之中有一個不是〇(或說它為true),logicalOR操作符(II)便返回true。此外,logicalOR操作符保證其計算次序由左至右,運算結果ー經確立,立刻中止計算。練習4.13以前述的bit_on()inline函數為模型,請提供bit_turn_on〇(打開某位)、bit_turn_off()(關閉某位)、flip_bit()(將某位反相),以及bit_off()(偵測某位是否關閉)等inline函數,它們可以作用在ー個以unsignedint呈現出來的bitvector身上。然后,寫一個小程序練習這些inline函數inlinevoidbit_turn__on(unsignedint&ui,intpos)(uiI=(1<<pos);)inlinevoidbit__turn_off(unsignedint&ui,intpos)(ui&=?(1<<pos);}inlinevoidflip_bit(unsignedint&ui,intpos)(uiハ=(1<<pos);)inlineboolbit_off(unsignedintui,intpos)(return!bit_on(ui,pos);}以下的main()可用來測試上述函數:intmain()(unsignedintui=0xd3;//11010011inbinary//bitsarenumberedfromtheright//startingatposition0cout<<"uiinhex:"<hex<<ui<<*\n*;//turnonthe4thbitfromtherightbit_turn_on(ui,3);cout<<"resultshouldbe*db*,itis"cout<<"result<<hex<<ui<<'\n*;<<hex<<ui<<'\n*;//turnoffthe4thbitfromtherightbit_turn_off(ui,3);cout<<"resultshouldbe'd3',itis<hex<<ui<<'\n1;//flipthe4thbitfromtherightflip_bit(ui,3);cout<<"resultshouldbe*db*,itis<hex<<ui<<'\n1;//flipthe4thbitfromtherightflip_bit(ui,3);cout<<"resultshouldbe*d3*,itis<<hex<<ui<<,Xn<<hex<<ui<<,Xn1;cout<<"4thbitshouldbe0,itis"<<bit_on(ui,3)cout<<"1stbitshouldbe1cout<<"1stbitshouldbe1,itis<<bit_on(ui,0)return0;練習4.14下面是bit_on()的typedef版木:typedefunsignedintbitvec;inlinevoidbit_turn_on_typedef(bitvec&ui,intpos)(uiI=(1<<pos);}typedef版本的ー個缺點是,typedefー經定義后,在同一個編譯單元中便無法改變。另ー個缺點是,我們無法將那些位轉換成一個unsignedchar,例如我們無法將一個unsignedchar&轉換成unsigned&。下面是bit_on()的template版本:template<classType>inlinevoidbit_turn_on(Type&ui,intpos){uiI=(1<<pos);}template版本的優點是我們可以在同一個編譯單元中產生出不同類型的對象。以下這個main()是練習4.13的?點小改變,可用來測試上述函數。intmain()(unsignedintui=0xd3;//11010011inbinary//bitsarenumberedfromtheright//startingatposition0cout<<"uiinhex:"<<hex<<ui<<'\n*;//turnonthe4th//turnonthe4thbitfromtherightbit_turn_on_typedef(ui,3);cout<<"resultshouldbe1db*,itis<hex<<ui<<1\n*;//turnoffthe4thbitfromtherightbit_turn_off(ui,3);cout<<"resultshoulditcout<<"resultshoulditis"<hex<<ui<<,\n1;//turnon//turnonthe4thbitfromtherightbit_turn_on(ui,3);cout<<"resultshouldbe*bit_turn_on(ui,3);cout<<"resultshouldbe*db1,itis"<<hex<<ui<<'\n,;//turnoffthe//turnoffthe4thbitfromtherightbit_turn_off(ui,3);cout<<"resultshouldbe*d3*,itis<hex<<ui<<'\n*;//flipthe4thbitfromtherightflip_bit(ui,3);cout<<"resultshouldbe'db',itis<hex<<ui<<'\n';//flipthe4thbitfromtherightflip_bit(ui,3);cout<<"resultshouldbe'd3',itis<<hex<<ui<<'\n';cout<<"4thbitshouldbe0,itis"<bit_off(ui,3)<<'\n';cout<<"1stbitshouldbe1,itis"<<bit_off(ui,0)<<'\n';return0;)練習4.15(a)bitset<64>bitvec(32);正確。bitsettemplate以size_t作為參數類型。64是size_t的ー個合法值。bitvec將使用64個位中的32個。bitset<32>bv(1010101);不正確。bv并未以位1010101作為初值,因為其constructor接受的參數是ー個卜進制的unsignedlong。此處表現的卻是位(二進制表示法)。由于bitset有一個constructor接受string參數,所以我們可采用以下方式將bv初始化為位!010101:bitset<32>bv("1010101");stringbstr;cin>>bstr;bitset<8>bv(bstr);正確。字符串bstr成為bitsetconstructor的參數。bitset<32>bv;bitset<16>bvl6(bv);不正確°bv被拿來當做構造bvl6所需的constructor參數。我們期望該參數是ー個unsignedlong,但bitset并未提供,個轉換操作符可將bitset轉換為unsignedlong。練習4.16(a)bitstring(bitvec.to_string().c_str());正確。bitset提供有一個轉換函數to_string()?可將其值轉換為string。string則提供有ー個c_str()函數,可將其本身轉換為C-style字符串。(b)if(bit_on(bitvec.to_long(),64))...正確〇bitset提供有一個轉換函數to_ulong()?可將其內容轉換為unsigned10ng。(c)bitvec.flip(bitvec.count());正確,但結果也許并非我們所想象。count〇會返回bitset中狀態為on的位個數,本例假設為3:flip()于是會將第3個位反相,而不是將狀態為on的那3個位反相。練習4.17本題數列所呈現的是Fibonacci數列,我們可以寫出ー個函數,以該數列的前7項作為bitset的初值0voidbuildFibonacci(intfib[],intn)(fib[O]=1;fibロ]=2;for(inti=2;i<n;i++)fib[i]=fib[i-l]+fib[i-2];)voidinitFibonacci(bitset<32>&bv,intn)(assert(n>06&n<=32);intfib[32];buildFibonacci(fib,n);for(inti=0;i<n;i++){bv.set(fib[i]);)}以下程序測試initFibonacci()://output:1,2,3,5,8,13,21intmain(){bitset<32>bv;initFibonacci(bv,7);for(inti=0;i<bv.size();i++){if(bv.test(i))cout<<"bit"<<setw(2)<<i<<"on\n,T;)return0;)練習4.18(a)!ptr==ptr->nextptr->next!ptr!ptr==ptr->next(b)?uc人03775ui?4.?uc.ui<<4.0377&ui?4.?uc人0377&ui<<4(c)ch=buf[bp++]!='\n*1.buf[bp]2?bp++3.buf[bp]!='\n14.ch=buf[bp)!=*\n'練習4.19(a)!ptr==ptr->next!(ptr==ptr->next)(b)

溫馨提示

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

評論

0/150

提交評論