![C++C試題的答案與評分標準_第1頁](http://file4.renrendoc.com/view/88b05f2d74604241759a3b82ac37b51c/88b05f2d74604241759a3b82ac37b51c1.gif)
![C++C試題的答案與評分標準_第2頁](http://file4.renrendoc.com/view/88b05f2d74604241759a3b82ac37b51c/88b05f2d74604241759a3b82ac37b51c2.gif)
![C++C試題的答案與評分標準_第3頁](http://file4.renrendoc.com/view/88b05f2d74604241759a3b82ac37b51c/88b05f2d74604241759a3b82ac37b51c3.gif)
![C++C試題的答案與評分標準_第4頁](http://file4.renrendoc.com/view/88b05f2d74604241759a3b82ac37b51c/88b05f2d74604241759a3b82ac37b51c4.gif)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
附錄C:C++/C試題的答案與評分標準ー、請?zhí)顚態(tài)OOL,float,指針變量與“零值”比較的if語句。(10分)請寫出BOOLflag與“零值”比較的if語句。(3分)標準答案:if(flag)if(!flag)如下寫法均屬不良風格,不得分。if(flag==TRUE)if(flag==1)if(flag==FALSE)if(flag=0)請寫出floatx與“零值”比較的if語句。(4分)標準答案示例:constfloatEPSINON=0.00001;if((x>=-EPSINON)&&(x<=EPSINON)不可將浮點變量用“=”或<*!=”與數(shù)字比較,應(yīng)該設(shè)法轉(zhuǎn)化成“>=”或“<=”此類形式。如下是錯誤的寫法,不得分。if(x==0.0)if(x!=0.0)請寫出char*p與“零值”比較的if語句。(3分)標準答案:if(p=NULL)if(p!=NULL)如下寫法均屬不良風格,不得分。if(p=o)if(p!=0)if(p)if(!)二、以下為WindowsNT下的32位C++程序,請計算sizeof的值(10分)charstr[]="Hello”;char*p=str;intn=10;請計算TOC\o"1-5"\h\zsizeof(str)=6 (2分)sizeof(p)=4 (2分)sizeof(n)=4 (2分)voidFunc(charstr[100])(請計算sizeof(str)=4 (2分))void*p=malloc(100);請計算sizeof(p)=4 (2分)三、簡答題(25分)1、頭文件中的ifndef/define/endif干什么用?(5分)答:防止該頭文件被重復(fù)引用。2-,#include和#include"filename.h”有什么區(qū)別?(5分)答:對于#include ,編譯器從標準庫路徑開始搜索filename.h對于#include"filename.h”,編譯器從用戶的工作路徑開始搜索filename.h3、const有什么用途?(請至少說明兩種)(5分)答:(1)可以定義const常量(2)const可以修飾函數(shù)的參數(shù)、返回值,甚至函數(shù)的定義體。被const修飾的東西都受到強制保護,可以預(yù)防意外的變動,能提高程序的健壯性。4、在C++程序中調(diào)用被C編譯器編譯后的函數(shù),為什么要加extern"C"? (5分)答:C++語言支持函數(shù)重載,C語言不支持函數(shù)重載。函數(shù)被C++編譯后在庫中的名字與C語言的不同。假設(shè)某個函數(shù)的原型為:voidfoo(intx,inty);該函數(shù)被C編譯器編譯后在庫中的名字為ーfoo,而C++編譯器則會產(chǎn)生像_foo_int_int之類的名字。C++提供了C連接交換指定符號extern"C"來解決名字匹配問題。5、請簡述以下兩個for循環(huán)的優(yōu)缺點(5分)for(i=0;i<N;i++){if(condition)DoSomething();elseDoOtherthingO;)優(yōu)點:程序簡潔缺點:多執(zhí)行了N-1次邏輯判斷,并且打斷了循環(huán)“流水線”作業(yè),使得編譯器不能對循環(huán)進行優(yōu)化處理,降低了效率。if(condition)(for(i=0;i<N;i++)DoSomethingO;)else(for(i=0;i<N;i++)DoOtherthingO;}優(yōu)點:循環(huán)的效率高缺點:程序不簡潔四、有關(guān)內(nèi)存的思考題(每小題5分,共20分)voidGetMemory(char*p)(p=(char*)malloc(100);}voidTest(void)(char*str=NULL;GetMemory(str);strcpy(str,"helloworld'*);printf(str);請問運行Tesl函數(shù)會有什么樣的結(jié)果?答:程序崩潰。因為GetMemory并不能傳遞動態(tài)內(nèi)存,Test函數(shù)中的str一直都是NULし。修改:Definechar*PCvoidGetMemory(PC&p)|p=(char*)malloc(100);}正確strcpy(str,"helloworld");將使程序崩潰。char*GetMemory(void){charp[]="helloworld";returnp;)voidTest(void)(char*str=NULL;str=GetMemoryO;printf(str);)請問運行Test函數(shù)會有什么樣的結(jié)果?答:可能是亂碼。因為GetMemory返回的是指向“棧內(nèi)存’’的指針,該指針的地址不是NULL,但其原現(xiàn)的內(nèi)容已經(jīng)被清除,新內(nèi)容不可知。voidGetMemory2(char**p,intnum){*p=(char*)malloc(num);)voidTest(void){char*str=NULL;GetMemory(&str,100);strcpy(str,"hello");printf(str);)請問運行Test函數(shù)會有什么樣的結(jié)果?答:(1)能夠輸出hello(2)內(nèi)存泄漏voidTest(void)char*str=(char*)malloc(lOO);strcpy(str,"hello”);free(str);if(str!=NULL)(strcpy(str,"world");printf(str);}}請問運行Test函數(shù)會有什么樣的結(jié)果?答:篡改動態(tài)內(nèi)存區(qū)的內(nèi)容,后果難以預(yù)料,非常危險。因為free(str);之后,str成為野指針,if(str!=NULL)語句不起作用。五、編寫strcpy函數(shù)(10分)已知strcpy函數(shù)的原型是char*strcpy(char*strDest,constchar*strSrc);其中strDest是目的字符串,strSrc是源字符串。(1)不調(diào)用C++/C的字符串庫函數(shù),請編寫函數(shù)strcpychar*strcpy(char*strDest,constchar*strSrc);{assert((strDest!=NULL)&&(strSrc!=NULL));〃2分TOC\o"1-5"\h\zchar*address=strDesti 〃2分while((*strDest++=*strSrc++)!=‘、。') 〃2分NULL;returnaddress; 〃2分)(2)strcpy能把strSrc的內(nèi)容復(fù)制到strDest?為什么還要char?類型的返回值?答:為了實現(xiàn)鏈式表達式。 〃2分例如inilength=strlen(strcpy(strDest,"helloworld"));六、編寫類String的構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值函數(shù)(25分)已知類String的原型為:classString(public:String(constchar*str=NULL);//普通構(gòu)造函數(shù)String(constString&other);/Z拷貝構(gòu)造函數(shù)~String(void); /Z析構(gòu)函數(shù)String&operate=(constString&other);/Z賦值函數(shù)private:char*m_data; /Z用于保存字符串};請編寫String的上述4個函數(shù)。標準答案://String的析構(gòu)函數(shù)String:>String(void) 〃3分(delete[]m_data;//山于m_data是內(nèi)部數(shù)據(jù)類型,也可以寫成deletem_data;//String的普通構(gòu)造函數(shù)String::String(constchar*str)〃6分if(str==NULL)|m_data=newcharf1]; //若能加NULL判斷則更好*m_data=ヘ〇';)else{intlength=strlen(str);m_data=newchar[length+lJ;//若能加NULL判斷則更好strcpy(m_data,str);)}/Z拷貝構(gòu)造函數(shù)String::String(constString&other)〃3分{intlength=strlen(other.m_data);m_data=newchar[length+1]; //若能加NULL判斷則更好strcpy(m_data,other.m_data);}〃賦值函數(shù)String&String::operate=(constString&other)〃13分(//(l)檢查自賦值 〃4分if(this==&other)return*this;//(2)釋放原有的內(nèi)存資源 〃3分delete[]m_data;//(3)分配新的內(nèi)存資源,并復(fù)制內(nèi)容〃3分intlength=strlen(other.m_data);m_data=newchar[length+l];//若能加NULL判斷則更好strcpy(m_data,other.m_data);//(4)返回本對象的引用 〃3分return*this;}第1章文件結(jié)構(gòu)每個C++/C程序通常分為兩個文件。ー個文件用于保存程序的聲明(declaration),稱為頭文件。另ー個文件用于保存程序的實現(xiàn)(implementation),稱為定義(deHnition)文件。C++/C程序的頭文件以“.h”為后綴,C程序的定義文件以“.c”為后綴,C++程序的定義文件通常以“.cpp”為后綴(也有一些系統(tǒng)以“.cc”或“.cxx”為后綴)。1.I版權(quán)和版本的聲明版權(quán)和版本的聲明位于頭文件和定義文件的開頭(參見示例1-1),主要內(nèi)容有:(1)版權(quán)信息。(2)文件名稱,標識符,摘要。(3)當前版本號,作者/修改者,完成H期。(4)版本歷史信息。Copyright(c)2001,上海貝爾有限公司網(wǎng)絡(luò)應(yīng)用事、業(yè)部Allrightsreserved.*文件名稱:filename.h文件標識:見配置管理計劃書摘要:簡要描述本文件的內(nèi)容*當前版本:1.1作者:輸入作者(或修改者)名字完成日期:2001年7月20日*取代版本:1.0原作者:輸入原作者(或修改者)名字完成日期:2001年5月10日*/示例1-1版權(quán)和版本的聲明1.2頭文件的結(jié)構(gòu)頭文件由三部分內(nèi)容組成:(1)頭文件開頭處的版權(quán)和版本聲明(參見示例1-1)。(2)預(yù)處理塊。(3)函數(shù)和類結(jié)構(gòu)聲明等。假設(shè)頭文件名稱為graphics.h?頭文件的結(jié)構(gòu)參見示例1-2。【規(guī)則1-2-1】為了防止頭文件被重復(fù)引用,應(yīng)當用ifndef/define/endif結(jié)構(gòu)產(chǎn)生預(yù)處理塊?!疽?guī)則1-2-2】用include格式來引用標準庫的頭文件(編譯器將從標準庫目錄開始搜索)?!疽?guī)則1-2-3】用include“filename.h”格式來引用非標準庫的頭文件(編譯器將從用戶的工作目錄開始搜索)?!窘ㄗh1-2-1]頭文件中只存放“聲明”而不存放“定義”在C++語法中,類的成員函數(shù)可以在聲明的同時被定義,并且自動成為內(nèi)聯(lián)函數(shù)。這雖然會帶來書寫上的方便,但卻造成了風格不一致,弊大于利。建議將成員函數(shù)的定義與聲明分開,不論該函數(shù)體有多么小?!窘ㄗh1-2-2】不提倡使用全局變量,盡量不要在頭文件中出現(xiàn)象externintvalue這類聲明。/Z版權(quán)和版本聲明見示例!-1,此處省略。#ifndefGRAPHICS_H〃防止graphics.h被重復(fù)引用#defineGRAPHICS.Hinclude//引用標準庫的頭文件#include“myheader.h”//引用非標準庫的頭文件voidFunctionl(?,,);//全局函數(shù)聲明classBox 〃類結(jié)構(gòu)聲明);#endif示例1-2C++/C頭文件的結(jié)構(gòu)定義文件的結(jié)構(gòu)定義文件有三部分內(nèi)容:(1)定義文件開頭處的版權(quán)和版本聲明(參見示例1-1)。(2)對一些頭文件的引用。(3)程序的實現(xiàn)體(包括數(shù)據(jù)和代碼)。假設(shè)定義文件的名稱為gr叩hics.cpp,定義文件的結(jié)構(gòu)參見示例1-3〇/Z版權(quán)和版本聲明見示例!-1,此處省略。#include“graphics.y//引用頭文件//全局函數(shù)的實現(xiàn)體voidFunction!(?,,)/Z類成員函數(shù)的實現(xiàn)體voidBox::Draw(…){)示例1-3C++/C定義文件的結(jié)構(gòu)頭文件的作用早期的編程語言如Basic、Fortran沒有頭文件的概念,C++/C語言的初學(xué)者雖然會用使用頭文件,但常常不明其理。這里對頭文件的作用略作解釋:(1)通過頭文件來調(diào)用庫功能。在很多場合,源代碼不便(或不準)向用戶公布,只要向用戶提供頭文件和二進制的庫即可。用戶只需要按照頭文件中的接口聲明來調(diào)用庫功能,而不必關(guān)心接口怎么實現(xiàn)的。編譯器會從庫中提取相應(yīng)的代碼。(2)頭文件能加強類型安全檢查。如果某個接口被實現(xiàn)或被使用時,其方式與頭文件中的聲明不一致,編譯器就會指出錯誤,這ー簡單的規(guī)則能大大減輕程序員調(diào)試、改錯的負擔。目錄結(jié)構(gòu)如果ー個軟件的頭文件數(shù)目比較多(如超過十個),通常應(yīng)將頭文件和定義文件分別保存于不同的目錄,以便于維護。例如可將頭文件保存于include目錄,將定義文件保存于source目錄(可以是多級目錄)。如果某些頭文件是私有的,它不會被用戶的程序直接引用,則沒有必要公開其“聲明”。為了加強信息隱藏,這些私有的頭文件可以和定義文件存放于同一個目錄。第2章程序的版式版式雖然不會影響程序的功能,但會影響可讀性。程序的版式追求清晰、美觀,是程序風格的重要構(gòu)成因素。可以把程序的版式比喻為“書法”。好的“書法”可讓人對程序一目了然,看得興致勃勃。差的程序“書法”如螃蟹爬行,讓人看得索然無味,更令維護者煩惱有加。請程序員們學(xué)習(xí)程序的“書法”,彌補大學(xué)計算機教育的漏洞,實在很有必要??招锌招衅鹬指舫绦蚨温涞淖饔???招械皿w(不過多也不過少)將使程序的布局更加清晰??招胁粫速M內(nèi)存,雖然打印含有空行的程序是會多消耗一些紙張,但是值得。所以不要舍不得用空行?!疽?guī)則2-1-1]在每個類聲明之后、每個函數(shù)定義結(jié)束之后都要加空行。參見示例2-1(a)【規(guī)則2-1-2]在ー個函數(shù)體內(nèi),邏揖上密切相關(guān)的語句之間不加空行,其它地方應(yīng)加空行分隔。參見示例2-1(b)/Z空行voidFunction!(??,)()/Z空行voidFunction2("'))/Z空行
voidFunction3(,*,)示例2-1(a)函數(shù)之間的空行/Z空行while(condition){statement1;/Z空行if(condition)(statement2;)else(statement?;ー丁空ー丁空〃statement4;}示例2-l(b)函數(shù)內(nèi)部的空行代碼行【規(guī)則2-2-11一行代碼只做一件事情,如只定義ー個變量,或只寫一條語句。這樣的代碼容易閱讀,并且方便于寫注釋?!疽?guī)則2-2-2】if、for,while,do等語句自占一行,執(zhí)行語句不得緊跟其后。不論執(zhí)行語句有多少都要加{}。這樣可以防止書寫失誤。示例2-2(a)為風格良好的代碼行,示例2-2(b)為風格不良的代碼行。intwidth;/Z寬度intheight;//高度intdepth;//深度x=a+b;y=c+d;z=e+f;if(width<height)(dosomethingO;1for(initialization;condition;update){dosomethingO;)/Z空行other();示例2-2(a)風格良好的代碼行intwidth,height,depth;//寬度高度深度X=a+b;y=c+d;z=e+f;if(width<height)dosomething();for(initialization;condition;update)dosomething();other();示例2-2(b)風格不良的代碼行【建議2-2-1]盡可能在定義變量的同時初始化該變量(就近原則)如果變量的引用處和其定義處相隔比較遠,變量的初始化很容易被忘記。如果引用了未被初始化的變量,可能會導(dǎo)致程序錯誤。本建議可以減少隱患。例如intwidth=10; 〃定義并初紿化widthintheight=10;//定義并初紿化heightintdepth=10; //定義并初紿化depth代碼行內(nèi)的空格【規(guī)則2-3-1】關(guān)鍵字之后要留空格。象const、virtual、inline、case等關(guān)鍵字之后至少要留一個空格,否則無法辨析關(guān)鍵字。象if、for、while等關(guān)鍵字之后應(yīng)留一個空格再跟左括號以突出關(guān)鍵字。【規(guī)則2-3-2】函數(shù)名之后不要留空格,緊跟左括號‘(’,以與關(guān)鍵字區(qū)別。【規(guī)則2-3-3】‘(’向后緊跟,,)‘、,,‘、,;’向前緊跟,緊跟處不留空格?!疽?guī)則2-3-4】,,,之后耍留空格,如Function(x,y,z)。如果,;'不是一行的結(jié)束符號,其后要留空格,如for(initialization;condition;update)〇【規(guī)則2-3-5】賦值操作符、比較操作符、算術(shù)操作符、邏輯操作符、位域操作符,如,,="、,,+=",,>="、“<="、,,+”、,,*”、“%”、“&&”、,,1'、,,《",,,人”等二元操作符的前后應(yīng)當加空格?!疽?guī)則2-3-6】一元操作符如,,!”、,,?"、,,++”、,,--"、,,&”(地址運算符)等前后不加空格?!疽?guī)則2-3-7】象,,ロ”、,,.”、,,ー〉”這類操作符前后不加空格。【建議2-3-1】對于表達式比較長的for語句和if語句,為了緊湊起見可以適當?shù)厝サ舂`些空格,如for(i=0;i<10;i++)和if((a<=b)&&(c<=d))voidFund(intx,inty,intz); //良好的風格voidFunc1(intx,inty,intz); 〃不良的風格if(year>=2000) /Z良好的風格if(yeai>=2000) //不良的風格if((a>=b)&&(c<=d)) /Z良好的風格if(a>=b&&c<=d) //不良的風格for(i=0;i<10;i++) /Z良好的風格for(i=0;i<10;i++) /Z不良的風格for(i=0;I<10;i++) 〃過多的空格x=a<b?a:b; /Z良好的風格x=a<B?A:B;不好的風格int*x=&y; //良好的風格int*x=&y: 〃不良的風格array[5]=0; //不要寫成array[5]=0;a.Function(); //不要寫成a.Function();b->Function(); //不要寫成b->Function();示例2-3代碼行內(nèi)的空格對齊【規(guī)則2-4ー”程序的分界符'{'和,}'應(yīng)獨占一行并且位于同一列,同時與引用它們的語句左對齊。????【規(guī)則2-4-2】{}之內(nèi)的代碼塊在,{'右邊數(shù)格處左對齊。示例2-4(a)為風格良好的對齊,示例24(b)為風格不良的對齊。voidFunction(intx)(…//programcodeif(condition)(…//programcode}else(…//programcode)for(initialization;condition;update){???//programcode)While(condition){…//programcode}如果出現(xiàn)嵌套的{},則使用縮進對齊,如:}示例2?4(a)風格良好的對齊voidFunction(intx){…//programcode)if(condition)(…//programcode}else{…//programcode)for(initialization;condition;update){…//programcode}while(condition)(…//programcode)示例2-4(b)風格不良的對齊長行拆分【規(guī)則2-5-1】代碼行最大長度宜控制在70至80個字符以內(nèi)。代碼行不要過長,否則眼睛看不過來,也不便于打印?!疽?guī)則2-5-2]長表達式要在低優(yōu)先級操作符處拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要進行適當?shù)目s進,使排版整齊,語句可讀。if((very_longer_variable1>=very_longer_variable12)&&(very_longer_variable3<=very_longer_variable14)&&(very_longer_variable5<=very_longer_variable16))(dosomething();virtualCMatrixCMultiplyMatrix(CMatrixleftMatrix,CMatrixrightMatrix);for(very_longer_initialization;very_longer_condition;very_longer_update)(dosomethingO;)示例2-5長行的拆分修飾符的位置修飾符?和&應(yīng)該靠近數(shù)據(jù)類型還是該靠近變量名,是個有爭議的活題。若將修飾符?靠近數(shù)據(jù)類型,例如:int*x;從語義上講此寫法比較直觀,即x是int類型的指針。上述寫法的弊端是容易引起誤解,例如:int*X,y;此處y容易被誤解為指針變量。雖然將x和y分行定義可以避免誤解,但并不是人人都愿意這樣做?!疽?guī)則2-6-1】應(yīng)當將修飾符?和&緊靠變量名!!?。。。。。。。?!例如:char*name;int*x,y:〃此處y不會被誤解為指針注釋C語言的注釋符為‘ソ?…*/”。C++語言中,程序塊的注釋常采用"/*…*/",行注釋一般采用“〃…L注釋通常用于:(1)版本、版權(quán)聲明;(2)函數(shù)接口說明;(3)重要的代碼行或段落提示。雖然注釋有助于理解代碼,但注意不可過多地使用注釋。參見示例2-6。【規(guī)則2-7-1]注釋是對代碼的“提示”,而不是文檔。程序中的注釋不可喧賓奪主,注釋太多了會讓人眼花繚亂。注釋的花樣要少。【規(guī)則2-7-2】如果代碼本來就是清楚的,則不必加注釋。否則多此ー舉,令人厭煩。例如i++;〃i加!,多余的注釋【規(guī)則2-7-3】邊寫代碼邊注釋,修改代碼同時修改相應(yīng)的注釋,以保證注釋與代碼的一致性。不再有用的注釋要刪除。【規(guī)則2-7W】注釋應(yīng)當準確、易懂,防止注釋有二義性。錯誤的注釋不但無益反而有害?!疽?guī)則2-7-5】盡量避免在注釋中使用縮寫,特別是不常用縮寫?!疽?guī)則2-7-6】注釋的位置應(yīng)與被描述的代碼相鄰,可以放在代碼的上方或右方,不可放在下方?!疽?guī)則2-7-8】當代碼比較長,特別是有多重嵌套時,應(yīng)當在ー些段落的結(jié)束處加注釋,便于閱讀。/*函數(shù)介紹:輸入?yún)?shù):輸出參數(shù):*返回值:voidFunction(floatx,floaty,floatz)if(—)}//endofwhile}//endofif示例2-6程序的注釋2.8類的版式類可以將數(shù)據(jù)和函數(shù)封裝在ー起,其中函數(shù)表示了類的行為(或稱服務(wù))。類提供關(guān)鍵字public、protected和private,分別用于聲明哪些數(shù)據(jù)和函數(shù)是公有的、受保護的或者是私有的。這樣可以達到信息隱藏的目的,即讓類僅僅公開必須要讓外界知道的內(nèi)容,而隱藏其它一切內(nèi)容。我們不可以濫用類的封裝功能,不要把它當成火鍋,什么東西都往里扔。類的版式主要有兩種方式:(1)將private類型的數(shù)據(jù)寫在前面,而將public類型的函數(shù)寫在后面,如示例8-3(a)。采用這種版式的程序員主張類的設(shè)計“以數(shù)據(jù)為中心”,重點關(guān)注類的內(nèi)部結(jié)構(gòu)。(2)將public類型的函數(shù)寫在前面,而將private類型的數(shù)據(jù)寫在后面,如示例8.3(b)采用這種版式的程序員主張類的設(shè)計’‘以行為為中心”,重點關(guān)注的是類應(yīng)該提供什么樣的接口(或服務(wù))。很多C++教課書受到BiarneStroustrup第一本著作的影響,不知不覺地采用了“以數(shù)據(jù)為中心”的書寫方式,并不見得有多少道理。我建議讀者采用“以行為為中心”的書寫方式,即首先考慮類應(yīng)該提供什么樣的函數(shù)。這是很多人的經(jīng)驗——“這樣做不僅讓自己在設(shè)計類時思路淸晰,而且方便別人閱讀。因為用戶最關(guān)心的是接口,誰愿意先看到ー堆私有數(shù)據(jù)成員!”classA{private:inti,j;floatx,y;public:voidFund(void);voidFunc2(void);)示例8.3(a)以數(shù)據(jù)為中心版式classApublic:voidFund(void);voidFunc2(void);private:inti,j;floatx,y;示例8.3(b)以行為為中心的版式第3章命名規(guī)則比較著名的命名規(guī)則當推Microsoft公司的“匈牙利”法,該命名規(guī)則的主要思想是“在變量和函數(shù)名中加入前綴以增進人們対程序的理解”。例如所有的字符變量均以ch為前綴,若是指針變量則追加前綴p。如果ー個變量由ppch開頭,則表明它是指向字符指針的指針?!靶傺览狈ㄗ畲蟮娜秉c是煩瑣,例如inti,j,k;floatx,y,z;倘若采用“匈牙利”命名規(guī)則,則應(yīng)當寫成intil,iJ,ik;//前綴i表示int類型floatfX,fY.億;//前綴f表示float類型如此煩瑣的程序會讓絕大多數(shù)程序員無法忍受。據(jù)考察,沒有一種命名規(guī)則可以讓所有的程序員贊同,程序設(shè)計教科書一般都不指定命名規(guī)則。命名規(guī)則對軟件產(chǎn)品而言并不是“成敗悠關(guān)”的事,我們不要化太多精力試圖發(fā)明世界上最好的命名規(guī)則,而應(yīng)當制定一種令大多數(shù)項目成員滿意的命名規(guī)則,并在項目中貫徹實施。共性規(guī)則本節(jié)論述的共性規(guī)則是被大多數(shù)程序員采納的,我們應(yīng)當在遵循這些共性規(guī)則的前提下,再擴充特定的規(guī)則,如3.2節(jié)。【規(guī)則3-1-1】標識符應(yīng)當直觀且可以拼讀,可望文知意,不必進行“解碼”。標識符最好采用英文單詞或其組合,便于記憶和閱讀。切忌使用漢語拼音來命名。程序中的英文單詞一般不會太復(fù)雜,用詞應(yīng)當準確。例如不要把Currentvalue寫成Nowvalue。【規(guī)則3-1-2】標識符的長度應(yīng)當符合“min-length&&max-information”原則。幾十年前老ANSIC規(guī)定名字不準超過6個字符,現(xiàn)今的C++/C不再有此限制。一般來說,長名字能更好地表達含義,所以函數(shù)名、變量名、類名長達十幾個字符不足為怪。那么名字是否越長約好?不見得!例如變星名maxval就比maxvalueUntilOverflow好用。單字符的名字也是有用的,常見的如i,j,k,m,n,x,y,z等,它們通??捎米骱瘮?shù)內(nèi)的局部變量?!疽?guī)則3-1-3]命名規(guī)則盡量與所采用的操作系統(tǒng)或開發(fā)工具的風格保持一致。例如Windows應(yīng)用程序的標識符通常采用“大小寫”混排的方式,如AddChild。而Unix應(yīng)用程序的標識符通常采用“小寫加下劃線”的方式,如add_child。別把這兩類風格混在ー起用。【規(guī)則3-14】程序中不要出現(xiàn)僅靠大小寫區(qū)分的相似的標識符。例如:intx,X;//變量x與X容易混淆voidfoo(intx);〃函數(shù)foo與FOO容易混淆voidFOO(floatx);【規(guī)則3-1-5】程序中不要出現(xiàn)標識符完全相同的局部變量和全局變量,盡管兩者的作用域不同而不會發(fā)生語法錯誤,但會使大誤解?!疽?guī)則3-1-6】變量的名字應(yīng)當使用“名詞”或者“形容詞+名詞”。例如:floatvalue;floatoldvalue;floatnewvalue;【規(guī)則3-1-7]全局函數(shù)的名字應(yīng)當使用“動詞”或者“動詞+名詞”(動賓詞組)。類的成員函數(shù)應(yīng)當只使用“動詞”,被省略掉的名詞就是對象本身。例如:DrawBox(); /Z全局函數(shù)box->Draw();/Z類的成員函數(shù)【規(guī)則3-1-8]用正確的反義詞組命名具有互斥意義的變量或相反動作的函數(shù)等。例如:int minvalue;int maxvalue;int Setvalue(…);int Getvalue(??り;【建議3-1-1】盡量避免名字中出現(xiàn)數(shù)字編號,如valuel,value2等,除非邏輯上的確需要編號。這是為了防止程序員偷懶,不肯為命名動腦筋而導(dǎo)致產(chǎn)生無意義的名字(因為用數(shù)字編號最省事)。簡單的Windows應(yīng)用程序命名規(guī)則作者対“匈牙利”命名規(guī)則做了合理的簡化,下述的命名規(guī)則簡單易用,比較適合于Windows應(yīng)用軟件的開發(fā)。【規(guī)則3-2-1】類名和函數(shù)名用大寫字母開頭的單詞組合而成。例如:classNode; /Z類名classLeafNode; /Z類名voidDraw(void);〃函數(shù)名voidSetvalue(intvalue);/Z函數(shù)名【規(guī)則3-2-2]變量和參數(shù)用小寫字母開頭的単詞組合而成。例如:BOOLflag;intdrawMode;【規(guī)則3-2-3】常量全用大寫的字母,用下劃線分割單詞。例如:constintMAX=100;constintMAX_LENGTH=100;【規(guī)則3-24】靜態(tài)變量加前綴s_(表示static)。例如:voidInit(…)(staticints_initvalue;/Z靜態(tài)變量)【規(guī)則3-2-5】如果不得已需要全局變量,則使全局變量加前綴8_(表示global)。例如:intg_howManyPeople; /Z全局變量intg_howMuchMoney; /Z全局變量【規(guī)則3-2-6】類的數(shù)據(jù)成員加前綴m_(表示member),這樣可以避免數(shù)據(jù)成員與成員函數(shù)的參數(shù)同名。例如:voidObject::Setvalue(intwidth,intheight){m_width=width;m_height=height;)【規(guī)則3-2-フ】為了防止某ー軟件庫中的一些標識符和其它軟件庫中的沖突,可以為各種標識符加上能反映軟件性質(zhì)的前綴。例如三維圖形標準OpenGL的所有庫函數(shù)均以gl開頭,所有常量(或宏定義)均以GL開頭。簡單的Unix應(yīng)用程序命名規(guī)則第4章表達式和基本語句讀者可能懷疑:連if、for、while,goto、switch這樣簡單的東西也要探討編程風格,是不是小題大做?我真的發(fā)覺很多程序員用隱含錯誤的方式寫表達式和基本語句,我自己也犯過類似的錯誤。表達式和語句都屬于C++/C的短語結(jié)構(gòu)語法。它們看似簡單,但使用時隱患比較多。本章歸納了正確使用表達式和語句的ー些規(guī)則與建議。運算符的優(yōu)先級C++/C語言的運算符有數(shù)十個,運算符的優(yōu)先級與結(jié)合律如表4-1所示。注意一元運算符+-?的優(yōu)先級高于對應(yīng)的二元運算符。從高到低排 運算符 結(jié)合律〇[]->, 從左至右!~++-(類型)sizeof+-*& 從右至左*/% 從左至右+- 從左至右?? 從左至右<<=>>= 從左至右=!= 從左至右& 從左至右ハ 從左至右I 從左至右&& 從左至右I! 從右至左?: 從右至左=+=-=*=/=%=&=』(=?=?= 從左至右表4-1運算符的優(yōu)先級與結(jié)合律【規(guī)則4-1-1】如果代碼行中的運算符比較多,用括號確定表達式的操作順序,避免使用默認的優(yōu)先級。由于將表4-1熟記是比較困難的,為了防止產(chǎn)生歧義并提高可讀性,應(yīng)當用括號確定表達式的操作順序。例如:if((alb)&&(a&c))復(fù)合表達式如a=b=c=0這樣的表達式稱為復(fù)合表達式。允許復(fù)合表達式存在的理由是:(1)書寫簡潔;(2)可以提高編譯效率。但要防止濫用復(fù)合表達式?!疽?guī)則4-2-1】不要編寫太復(fù)雜的復(fù)合表達式。例如:i=a>=b&&c<d&&c+f<=g+h;/Z復(fù)合表達式過于復(fù)雜【規(guī)則4-2-2】不要有多用途的復(fù)合表達式。例如:d=(a=b+c)+r;該表達式既求a值又求d值。應(yīng)該拆分為兩個獨立的語句:a=b4-c;d=a+r;【規(guī)則4-2-3】不要把程序中的復(fù)合表達式與“真正的數(shù)學(xué)表達式”混淆。例如:if(a<b<c) 〃a<b<c是數(shù)學(xué)表達式而不是程序表達式并不表示if((a<c))而是成了令人費解的if((a<B)if語句if語句是C++/C語言中最簡單.、最常用的語句,然而很多程序員用隱含錯誤的方式寫if語句。本節(jié)以‘’與零值比較”為例,展開討論。布爾變量與零值比較【規(guī)則4-3-1]不可將布爾變量直接與TRUE、FALSE或者1、〇進行比較。根據(jù)布爾類型的語義,零值為“假”(記為FALSE),任何非零值都是“真"(記為TRUE)。TRUE的值究竟是什么并沒有統(tǒng)ー的標準。例如VisualC++將TRUE定義為1,而VisualBasic則將TRUE定義為ー1。假設(shè)布爾變量名字為flag,它與零值比較的標準if語句如下:if(flag) 〃表示flag為真if(!flag)//表示flag為假其它的用法都屬于不良風格,例如:if(flag==TRUE)if(flag==1)if(flag=FALSE)if(flag==0)整型變量與零值比較【規(guī)貝リ4-3-2]應(yīng)當將整型變量用“=”或“!=”直接與〇比較。假設(shè)整型變量的名字為value,它與零值比較的標準if語句如下:if(value==0)if(value!=0)不可模仿布爾變量的風格而寫成if(value)/Z會讓人誤解value是布爾變量if(lvalue)浮點變量與零值比較【規(guī)則4-3-3]不可將浮點變量用“=”或“!=”與任何數(shù)字比較。千萬要留意,無論是float還是double類型的變量,都有精度限制。所以一定要避免將浮點變量用“=”或“!=”與數(shù)字比較,應(yīng)該設(shè)法轉(zhuǎn)化成“>=”或“0”形式。假設(shè)浮點變量的名字為x,應(yīng)當將if(x==0.0)/Z隱含錯誤的比較轉(zhuǎn)化為if((x>=-EPSINON)&&(x<=EPSINON))其中EPSINON是允許的誤差(即精度)。指針變量與零值比較【規(guī)則4-3-4】應(yīng)當將指針變量用“=”或“!=”與NULL比較。指針變量的零值是“空”(記為NULL)。盡管NULL的值與〇相同,但是兩者意義不同。假設(shè)指針變量的名字為p,它與零值比較的標準if語句如下:if(p==NULL)〃p與NULL顯式比較,強調(diào)p是指針變量if(p!=NULL)不要寫成if(p==0)/Z容易讓人誤解p是整型變量if(p!=0)或者if(p) /Z容易讓人誤解p是布爾變量if(!p)對if語句的補充說明有時候我們可能會看到if(NULL==p)這樣古怪的格式。不是程序?qū)戝e了,是程序員為了防止將if(p==NULL)誤寫成if(p=NULL),而有意把p和NULL顔倒。編譯器認為if(p=NULL)是合法的,但是會指出if(NULL=p)是錯誤的,因為NULL不能被賦值。程序中有時會遇到if/else/return的組合,應(yīng)該將如下不良風格的程序if(condition)returnx;returny;改寫為if(condition)(returnx;)else{returny;)或者改寫成更加簡練的return(condition?x:y);循環(huán)語句的效率C++/C循環(huán)語句中,for語句使用頻率最高,while語句其次,do語句很少用。本節(jié)重點論述循環(huán)體的效率。提髙循環(huán)體效率的基本辦法是降低循環(huán)體的復(fù)雜性?!窘ㄗh4-4-1】在多重循環(huán)中,如果有可能,應(yīng)當將最長的循環(huán)放在最內(nèi)層,最短的循環(huán)放在最外層,以減少CPU跨切循環(huán)層的次數(shù)。例如示例4-4(b)的效率比示例4-4(a)的高。for(row=0;row<100;row++){for(col=0;col<5;col++)(sum=sum+a[row][col];})示例4-4(a)低效率:長循環(huán)在最外層for(col=0;col<5;col++)(for(row=0;rowvlOO;row++)(sum=sum+a[row][col];))示例4-4(b)高效率:長循環(huán)在最內(nèi)層【建議4-4-2]如果循環(huán)體內(nèi)存在邏輯判斷,并且循環(huán)次數(shù)很大,宜將邏輯判斷移到循環(huán)體的外面。示例44(c)的程序比示例4-4(d)多執(zhí)行了N-1次邏輯判斷。并且由于前者老要進行邏輯判斷,打斷了循環(huán)“流水線”作業(yè),使得編譯器不能對循環(huán)進行優(yōu)化處理,降低了效率。如果N非常大,最好采用示例44(d)的寫法,可以提高效率。如果N非常小,兩者效率差別并不明顯,采用示例44(c)的寫法比較好,因為程序更加簡潔。for(i=0;i<N;i++)(if(condition)DoSomethingO;elseDoOtherthingO;)表4-4(c)效率低但程序簡潔if(condition)for(i=0;i<N;i++)DoSomethingO;}else(for(i=0;i<N;i++)DoOtherthingO;}表4-4(d)效率高但程序不簡潔for語句的循環(huán)控制變量【規(guī)則4-5-1]不可在for循環(huán)體內(nèi)修改循環(huán)變量,防止for循環(huán)失去控制?!窘ㄗh4-5-1]建議for語句的循環(huán)控制變量的取值采用“半開半閉區(qū)間”寫法。示例4-5(a)中的x值屬于半開半閉區(qū)間“0=<x<N",起點到終點的間隔為N,循環(huán)次數(shù)為N。示例4-5(b)中的x值屬于閉區(qū)間"0=<x<=N4”,起點到終點的間隔為N-1,循環(huán)次數(shù)為N。相比之下,示例4-5(a)的寫法更加直觀,盡管兩者的功能是相同的。for(intx=0;x<N;x++){}示例4-5(a)循環(huán)變量屬于半開半閉區(qū)間for(intx=0;x<=N-l;x++)示例4-5(b)循環(huán)變量屬于閉區(qū)間switch語句有了if語句為什么還要switch語句?switch是多分支選擇語句,而if語句只有兩個分支可供選擇。雖然可以用嵌套的if語句來實現(xiàn)多分支選擇,但那樣的程序冗長難讀。這是switch語句存在的理由。switch語句的基本格式是:switch(variable)(TOC\o"1-5"\h\zcasevalue1: …break;casevalue2: …break;default: …break;)【規(guī)則4-6-1】每個case語句的結(jié)尾不要忘了加break,否則將導(dǎo)致多個分支重疊(除非有意使多個分支重疊)?!疽?guī)則4-6-2】不要忘記最后那個default分支。即使程序真的不需要default處理,也應(yīng)該保留語句default:break;這樣做并非多此ー舉,而是為了防止別人誤以為你忘了default處理。goto語句自從提倡結(jié)構(gòu)化設(shè)計以來,goto就成了有爭議的語句。首先,由于goto語句可以靈活跳轉(zhuǎn),如果不加限制,它的確會破壞結(jié)構(gòu)化設(shè)計風格。其次,goto語句經(jīng)常帶來錯誤或隱患。它可能跳過了某些對象的構(gòu)造、變量的初始化、重要的計算等語句,例如:gotostate;Stringsi,s2;//被goto跳過intsum=0;〃被goto跳過
state:如果編譯器不能發(fā)覺此類錯誤,每用一次goto語句都可能留下隱患。很多人建議廢除C++/C的got。語句,以絕后患。但實事求是地說,錯誤是程序員自己造成的,不是goto的過錯。goto語句至少有一處可顯神通,它能從多重循環(huán)體中咻地ー下子跳到外面,用不著寫很多次的break語句;例如gotoerror;)error:就象樓房著火了,來不及從樓梯ー級?級往下走,可從窗口跳出火坑。所以我們主張少用、慎用goto語句,而不是禁用。第5章常量常量是ー種標識符,它的值在運行期間恒定不變。C語言用#define來定義常量(稱為宏常量)。C++語言除了#define外還可以用const來定義常量(稱為const常量)。為什么需要常量?????????如果不使用常量,直接在程序中填寫數(shù)字或字符串,將會有什么麻煩?(1)程序的可讀性(可理解性)變差。程序員自己會忘記那些數(shù)字或字符串是什么意思,用戶則更加不知它們從何處來、表示什么。(2)在程序的很多地方輸入同樣的數(shù)字或字符串,難保不發(fā)生書寫錯誤。(3)如果要修改數(shù)字或字符串,則會在很多地方改動,既麻煩又容易出錯?!疽?guī)則5-1-1]盡量使用含義直觀的常量來表示那些將在程序中多次出現(xiàn)的數(shù)字或字符串。例如:#define MAX100 /*C語言的宏常量*/constintMAX=100; 〃C++語言的const常量constfloatPI=3.14159; 〃C++語言的const常量const與#define的比較C++語言可以用const來定義常量,也可以用#define來定義常量。但是前者比后者有更多的優(yōu)點:const常量有數(shù)據(jù)類型,而宏常量沒有數(shù)據(jù)類型。編譯器可以對前者進行類型安全檢查。而對后者只進行字符替換,沒有類型安全檢查,并且在字符替換可能會產(chǎn)生意料不到的錯誤(邊際效應(yīng))。(2)有些集成化的調(diào)試工具可以對const常量進行調(diào)試,但是不能對宏常量進行調(diào)試?!疽?guī)則5-2-1】在C++程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量。常量定義規(guī)則【規(guī)則5-3-1】需要對外公開的常量放在頭文件中,不需要對外公開的常量放在定義文件的頭部。為便于管理,可以把不同模塊的常量集中存放在ー個公共的頭文件中。【規(guī)則5-3-2】如果某ー常量與其它常量密切相關(guān),應(yīng)在定義中包含這種關(guān)系,而不應(yīng)給出一些孤立的值。例如:constfloatRADIUS=100;constfloatDIAMETER=RADIUS*2;類中的常量有時我們希望某些常量只在類中有效。由于#define定義的宏常量是全局的,不能達到目的,于是想當然地覺得應(yīng)該用const修飾數(shù)據(jù)成員來實現(xiàn)。const數(shù)據(jù)成員的確是存在的,但其含義卻不是我們所期望的。const數(shù)據(jù)成員只在某個對象生存期內(nèi)是常量,而對于整個類而言卻是可變的,因為類可以創(chuàng)建多個對象,不同的對象其const數(shù)據(jù)成員的值可以不同。不能在類聲明中初始化const數(shù)據(jù)成員。以卜用法是錯誤的,因為類的對象未被創(chuàng)建時,編譯器不知道SIZE的值是
什么。classA{,,,constintSIZE=100: 〃錯誤,企圖在類聲明中初始化const數(shù)據(jù)成員intarraylSIZE]; 〃錯誤,未知的SIZE};const數(shù)據(jù)成員的初始化只能在類構(gòu)造函數(shù)的初始化表中進行,例如classA{,"A(intsize);/Z構(gòu)造函數(shù)constintSIZE;);A::A(intsize):SIZE(size)/Z構(gòu)造函數(shù)的初始化表)Aa(100);/Z對象a的SIZE值為100Ab(200);/Z對象b的SIZE值為200怎樣才能建立在整個類中都恒定的常量呢?別指望const數(shù)據(jù)成員了,應(yīng)該用類中的枚舉常量來實現(xiàn)。例如classA{,11enum{SIZE1=100,SIZE2=200};/Z枚舉常量intarrayIfSIZEl];intarray2[SIZE2];);枚舉常量不會占用對象的存儲空間,它們在編譯時被全部求值。枚舉常量的缺點是:它的隱含數(shù)據(jù)類型是整數(shù),其最大值有限,且不能表示浮點數(shù)(如PI=3.14159)。第6章函數(shù)設(shè)計函數(shù)是C++/C程序的基本功能單元,其重要性不言而喻。函數(shù)設(shè)計的細微缺點很容易導(dǎo)致該函數(shù)被錯用,所以光使函數(shù)的功能正確是不夠的。本章重點論述函數(shù)的接口設(shè)計和內(nèi)部實現(xiàn)的ー些規(guī)則。函數(shù)接口的兩個要素是參數(shù)和返回值。C語言中,函數(shù)的參數(shù)和返回值的傳遞方式有兩種:值傳遞(passbyvalue)和指針傳遞(passbypointer)。C++語言中多了引用傳遞(passbyreference)。由于引用傳遞的性質(zhì)象指針傳遞,而使用方式卻象值傳遞,初學(xué)者常常迷惑不解,容易引起混亂,請先閱讀6.6節(jié)“引用與指針的比較”。參數(shù)的規(guī)則【規(guī)則6-1-1]參數(shù)的書寫要完整,不要貪圖省事只寫參數(shù)的類型而省略參數(shù)名字。如果函數(shù)沒有參數(shù),則用void填充。例如:voidSetvalue(intwidth,intheight);//良好的風格voidSetvalue(int,int); //不良的風格floatGetvalue(void);//良好的風格floatGetvalue();//不良的風格【規(guī)則6-1-2]參數(shù)命名要恰當,順序要合理。例如編寫字符串拷貝函數(shù)StringCopy,它有兩個參數(shù)。如果把參數(shù)名字起為strl和str2,例如voidStringCopy(char*str1,char*str2);那么我們很難搞清楚究竟是把strl拷貝到str2中,還是剛好倒過來??梢园褏?shù)名字起得更有意義,如叫strSource和strDestination。這樣從名字上就可以看出應(yīng)該把strSource拷貝到strDestination?還有一個問題,這兩個參數(shù)那一個該在前那一個該在后?參數(shù)的順序要遵循程序員的習(xí)慣。一般地,應(yīng)將目的參數(shù)放在前面,源參數(shù)放在后面。如果將函數(shù)聲明為:voidStringCopy(char*strSource,char*strDestination);別人在使用時可能會不假思索地寫成如下形式:charstr[20];StringCopy(str,uHelloWorldM);/Z參數(shù)順序顛倒【規(guī)則6-1-3]如果參數(shù)是指針,且僅作輸入用,則應(yīng)在類型前加const,以防止該指針在函數(shù)體內(nèi)被意外修改。!!!!例如:voidStringCopy(char*strDestination,constchar*strSource);【規(guī)則6-1-4]如果輸入?yún)?shù)以值傳遞的方式傳遞對象,則宜改用"const&M方式來傳遞,這樣可以省去臨時對象的構(gòu)造和析構(gòu)過程,從而提高效率。!!!!!!【建議6-1-1】避免函數(shù)有太多的參數(shù),參數(shù)個數(shù)盡量控制在5個以內(nèi)。如果參數(shù)太多,在使用時容易將參數(shù)類型或順序搞錯?!窘ㄗh6-1-2】盡量不要使用類型和數(shù)目不確定的參數(shù)。C標準庫函數(shù)printf是采用不確定參數(shù)的典型代表,其原型為:intprintf(constchat*fbrmat[,argument]這種風格的函數(shù)在編譯時喪失了嚴格的類型安全檢查。返回值的規(guī)則【規(guī)則6-2-1】不要省略返回值的類型。C語言中,凡不加類型說明的函數(shù),一律自動按整型處理。這樣做不會有什么好處,卻容易被誤解為void類型。C++語言有很嚴格的類型安全檢查,不允許上述情況發(fā)生。由于C++程序可以調(diào)用C函數(shù),為了避免混亂,規(guī)定任何C++/C函數(shù)都必須有類型。如果函數(shù)沒有返回值,那么應(yīng)聲明為void類型?!疽?guī)則6-2-2】函數(shù)名字與返回值類型在語義上不可沖突。違反這條規(guī)則的典型代表是C標準庫函數(shù)getchar,例如:charc;c=getchar();if(c==EOF)按照getchar名字的意思,將變量c聲明為char類型是很自然的事情。但不幸的是getchar的確不是char類型,而是血類型,其原型如下:intgetchar(void);由于c是char類型,取值范圍是[-128,127],如果宏EOF的值在char的取值范圍之外(可見EOF可能是自定義的符號,并無一定的標準),那么if語句將總是失敗,這種“危險”人們一般哪里料得到!導(dǎo)致本例錯誤的責任并不在用戶,是函數(shù)getchar誤導(dǎo)了使用者?!疽?guī)則6-2-3】不要將正常值和錯誤標志混在ー起返回。正常值用輸出參數(shù)獲得,而錯誤標志用return語句返回。回顧上例,C標準庫函數(shù)的設(shè)計者為什么要將getchar聲明為令人迷糊的int類型呢?他會那么傻嗎?在正常情況下,getchar的確返回單個字符。但如果getchar碰到文件結(jié)束標志或發(fā)生讀錯誤,它必須返回一個標志EOF?為了區(qū)別于正常的字符,只好將EOF定義為負數(shù)(通常為負1)。因此函數(shù)getchar就成了int類型。我們在實際工作中,經(jīng)常會碰到上述令人為難的問題。為了避免出現(xiàn)誤解,我們應(yīng)該將正常值和錯誤標志分開。即:正常值用輸出參數(shù)獲得,而錯誤標志用return語句返回。函數(shù)getchar可以改寫成BOOLGetChar(char*c);雖然gechar比GetChar靈活,例如putchar(getchar());但是如果getchar用錯了,它的靈活性又有什么用呢?【建議6-2-1】有時候函數(shù)原本不需要返回值,但為了增加靈活性如支持鏈式表達,可以附加返回值。例如字符串拷貝函數(shù)strcpy的原型:char*strcpy(char*strDest,constchar*strSrc);strcpy函數(shù)將strSrc拷貝至輸出參數(shù)strDest中,同時函數(shù)的返回值又是strDest。這樣做并非多此ー舉,可以獲得如下靈活性:charstr[20];intlength=strlen(strcpy(str,"HelloWorldM));【建議6-2-2]如果函數(shù)的返回值是ー個對象,有些場合用“引用傳遞”替換“值傳遞”可以提髙效率。而有些場合只能用“值傳遞”而不能用“引用傳遞”,否則會出錯。例如:classString/Z賦值函數(shù)String&operate=(constString&other);〃相加函數(shù),如果沒有friend修飾則只許有一個右側(cè)參數(shù)friendStringoperate+(constString&s1,constString&s2);private:char*m_data;)String的賦值函數(shù)operate=的實現(xiàn)如下:String&String::operate=(constString&other){if(this==&other)return*this;deletem_data;m_data=newchar[strlen(other.data)+l];strcpy(m_data,other.data);return*this;//返回的是?this的引用,無需拷貝過程)對丁賦值函數(shù),應(yīng)當用“引用傳遞”的方式返回String對象。如果用“值傳遞”的方式,雖然功能仍然正確,但由于return語句要把九his拷貝到保存返回值的外部存儲單元之中,增加了不必要的開銷,降低了賦值函數(shù)的效率。例如:Stringa,b,c;a=b;//如果用“值傳遞”,將產(chǎn)生一次?this拷貝a=b=c;//如果用“值傳遞”,將產(chǎn)生兩次?this拷貝String的相加函數(shù)operate+的實現(xiàn)如下:StringString::operate+(constString&sl,constSiring&s2){Stringtemp;deletetemp.data;〃temp.data是僅含、〇’的字符串temp.data=newchar[strlen(sl.data)+strlen(s2.data)+1];strcpy(temp.data,si.data);strcat(temp.data,s2.data);returntemp;}對于相加函數(shù),應(yīng)當用“值傳遞”的方式返回String對象。如果改用’‘引用傳遞”,那么函數(shù)返回值是一個指向局部對象temp的“弓|用"。由于temp在函數(shù)結(jié)束時被自動銷毀,將導(dǎo)致返回的“引用”無效。例如:c=a+b;此時a+b并不返回期望值,c什么也得不到,流下了隱患。函數(shù)內(nèi)部實現(xiàn)的規(guī)則不同功能的函數(shù)其內(nèi)部實現(xiàn)各不相同,看起來似乎無法就“內(nèi)部實現(xiàn)”達成一致的觀點。但根據(jù)經(jīng)驗,我們可以在函數(shù)體的''入口處”和“出口處”從嚴把關(guān),從而提高函數(shù)的質(zhì)量。【規(guī)則6-3-1】在函數(shù)體的“入口處”,對參數(shù)的有效性進行檢査。很多程序錯誤是由非法參數(shù)引起的,我們應(yīng)該充分理解并正確使用“斷言”(assert)來防止此類錯誤。詳見6.5節(jié)“使用斷言”。【規(guī)則6-3-2]在函數(shù)體的“出口處”,對return語句的正確性和效率進行檢査。如果函數(shù)有返冋值,那么函數(shù)的“出口處”是return語句。我們不要輕視return語句。如果return語句寫得不好,函數(shù)要么出錯,要么效率低下。注意事項如下:(1)return語句不可返回指向“棧內(nèi)存”的“指針”或者“引用”,因為該內(nèi)存在函數(shù)體結(jié)束時被自動銷毀。例如char*Func(void)charstr[]="helloworld”; 〃str的內(nèi)存位于棧上returnstr;/Z將導(dǎo)致錯誤)(2)要搞清楚返回的究竟是“值”、“指針”還是“引用”。(3)如果函數(shù)返回值是ー個對象,??紤]return語句的效率。例如returnString(sl+s2);這是臨時對象的語法,表示“創(chuàng)建一個臨時對象并返回它”。不要以為它與“先創(chuàng)建一個局部對象temp并返回它的結(jié)果”是等價的,如Stringtemp(sl+s2);returntemp;實質(zhì)不然,上述代碼將發(fā)生三件事。首先,temp對象被創(chuàng)建,同時完成初始化;然后拷貝構(gòu)造函數(shù)把temp拷貝到保存返回值的外部存儲單元中;最后,temp在函數(shù)結(jié)束時被銷毀(調(diào)用析構(gòu)函數(shù))。然而“創(chuàng)建一個臨時對象并返回它”的過程是不同的,編譯器直接把臨時對象創(chuàng)建并初始化在外部存儲單元中,省去了拷貝和析構(gòu)的化費,提高了效率。類似地,我們不要將returnint(x+y);/Z創(chuàng)建■?個臨時變量并返回它寫成inttemp=x+y;returntemp;由于內(nèi)部數(shù)據(jù)類型如int,float,double的變量不存在構(gòu)造函數(shù)與析構(gòu)函數(shù),雖然該“臨時變量的語法”不會提高多少效率,但是程序更加簡潔易讀。其它建議【建議64-1】函數(shù)的功能要單ー,不要設(shè)計多用途的函數(shù)?!窘ㄗh64-2】函數(shù)體的規(guī)模要小,盡量控制在50行代碼之內(nèi)?!窘ㄗh64-3】盡量避免函數(shù)帶有“記憶”功能。相同的輸入應(yīng)當產(chǎn)生相同的輸出。帶有“記憶”功能的函數(shù),其行為可能是不可預(yù)測的,因為它的行為可能取決于某種“記憶狀態(tài)”。這樣的函數(shù)既不易理解又不利于測試和維護。在C/C++語言中,函數(shù)的static局部變量是函數(shù)的“記憶”存儲器。建議盡量少用static局部變量,除非必需。【建議6-4-4]不僅要檢查輸入?yún)?shù)的有效性,還要檢査通過其它途徑進入函數(shù)體內(nèi)的變量的有效性,例如全局變量、文件句柄等。【建議64-5】用于出錯處理的返回值一定要清楚,讓使用者不容易忽視或誤解錯誤情況。使用斷言程序一般分為Debug版本和Release版本,Debug版本用于內(nèi)部調(diào)試,Release版本發(fā)行給用戶使用。斷言assert是僅在Debug版本起作用的宏,它用于檢查“不應(yīng)該”發(fā)生的情況。示例6-5是ー個內(nèi)存復(fù)制函數(shù)。在運行過程中,如果assert的參數(shù)為假,那么程序就會中止(一般地還會出現(xiàn)提示對話,說明在什么地方引發(fā)了assert)。void*memcpy(void*pvTo,constvoid*pvFrom,size_tsize){assert((pvTo!=NULL)&&(pvFrom!=NULL));//使用斷言byte*pbTo=(byte*)pvTo;//防止改變pvTo的地址byte*pbFrom=(byte*)pvFrom;//防止改變pvFrom的地址while(size—>0)*pbTo++=*pbFrom++;returnpvTo;)示例6-5復(fù)制不重疊的內(nèi)存塊assert不是ー個倉促拼湊起來的宏。為了不在程序的Debug版本和Release版本引起差別,assert不應(yīng)該產(chǎn)生任何副作用。所以assert不是函數(shù),而是宏。程序員可以把assert看成一個在任何系統(tǒng)狀態(tài)下都可以安全使用的無害測試手段。如果程序在assert處終止了,并不是說含有該assert的函數(shù)有錯誤,而是調(diào)用者出了差錯,assert可以幫助我們找到發(fā)生錯誤的原因。很少有比跟蹤到程序的斷言,卻不知道該斷言的作用更讓人沮喪的事了。你化了很多時間,不是為了排除錯誤,而只是為了弄清楚這個錯誤到底是什么。有的時候,程序員偶爾還會設(shè)計出有錯誤的斷言。所以如果搞不清楚斷言檢查的是什么,就很難判斷錯誤是出現(xiàn)在程序中,還是出現(xiàn)在斷言中。幸運的是這個問題很好解決,只要加上清晰的注釋即可。這本是顯而易見的事情,可是很少有程序員這樣做。這好比ー一個人在森林里,看到樹上釘著ー塊“危險”的大牌子。但危險到底是什么?樹要倒?有廢井?有野獸?除非告訴人們“危險”是什么,否則這個警告牌難以起到積極有效的作用。難以理解的斷言常常被程序員忽略,甚至被刪除。[Maguire,p8-p30]【規(guī)則6-5-1]使用斷言捕捉不應(yīng)該發(fā)生的非法情況。不要混淆非法情況與錯誤情況之間的區(qū)別,后者是必然存在的并且是一定要作出處理的?!疽?guī)則6-5-2]在函數(shù)的入U處,使用斷言檢查參數(shù)的有效性(合法性)?!窘ㄗh6-5-1】在編寫函數(shù)時,要進行反復(fù)的考査,并且自問:‘‘我打算做哪些假定?”一旦確定了的假定,就要使用斷言對假定進行檢查?!窘ㄗh6-5-2】一般教科書都鼓勵程序員們進行防錯設(shè)計,但要記住這種編程風格可能會隱瞞錯誤。當進行防錯設(shè)計時,如果“不可能發(fā)生”的事情的確發(fā)生了,則要使用斷言進行報警。引用與指針的比較引用是C++中的概念,初學(xué)者容易把引用和指針混淆一起。一下程序中,n是m的ー個引用(reference),m是被引用物(referent)〇intm;int&n=m;n相當于m的別名(綽號),對n的任何操作就是對m的操作。例如有人名叫王小毛,他的綽號是“三毛”。說“三毛”怎么怎么的,其實就是對王小毛說三道四。所以n既不是m的拷貝,也不是指向m的指針,其實n就是m它自己。引用的ー些規(guī)則如下:(1)引用被創(chuàng)建的同時必須被初始化(指針則可以在任何時候被初始化)。(2)不能有NULL引用,引用必須與合法的存儲單元關(guān)聯(lián)(指針則可以是NULL)。(3)一旦引用被初始化,就不能改變引用的關(guān)系(指針則可以隨時改變所指的對象)。以下示例程序中,k被初始化為i的引用。語句k=j并不能將k修改成為j的引用,只是把k的值改變成為6。由于k是i的引用,所以i的值也變成了6。inti=5;intj=6;int&k=i;k=j;〃k和i的值都變成了6;上面的程序看起來象在玩文字游戲,沒有體現(xiàn)出引用的價值。引用的主要功能是傳遞函數(shù)的參數(shù)和返回值。C++語言中,函數(shù)的參數(shù)和返回值的傳遞方式有三種:值傳遞、指針傳遞和引用傳遞。以下是“值傳遞”的示例程序。由于Funcl函數(shù)體內(nèi)的x是外部變量n的ー份拷貝,改變x的值不會影響n,所以n的值仍然是〇〇voidFuncl(intx){x=x+10;)intn=0;Funcl(n);cout?“n="?n?endl;//n=0以下是“指針傳遞”的示例程序。由于Func2函數(shù)體內(nèi)的x是指向外部變量n的指針,改變該指針的內(nèi)容將導(dǎo)致n的值改變,所以n的值成為10ovoidFunc2(int*x)(*x)=(*x)+10;intn=0;Func2(&n);cout?“n="?n?endl;//n=10以下是“引用傳遞”的示例程序。由于Func3函數(shù)體內(nèi)的x是外部變量n的引用,x和n是同一個東西,改變x等于改變n,所以n的值成為10ovoidFunc3(int&x){x=x+10;intn=0;Func3(n);cout?"n="?n?endl;//n=10對比上述三個示例程序,會發(fā)現(xiàn)“引用傳遞”的性質(zhì)象“指針傳遞”,而書寫方式象“值傳遞”。實際上“引用”可以做的任何事情’’指針”也都能夠做,為什么還要“引用”這東西?答案是“用適當?shù)墓ぞ咦銮∪缙浞值墓ぷ鳌?。指針能夠毫無約束地操作內(nèi)存中的如何東西,盡管指針功能強大,但是非常危險。就象一把刀,它可以用來砍樹、裁紙、修指甲、理發(fā)等等,誰敢這樣用?如果的確只需要借用一下某個對象的“別名”,那么就用“引用”,而不要用“指針”,以免發(fā)生意外。比如說,某人需要一份證明,本來在文件上蓋上公章的印子就行了,如果把取公章的鑰匙交給他,那么他就獲得了不該有的權(quán)利。第7章內(nèi)存管理歡迎進入內(nèi)存這片雷區(qū)。偉大的BillGates曾經(jīng)失言:640Koughttobeenoughforeverybody—BillGates1981程序員們經(jīng)常編寫內(nèi)存管理程序,往往提心吊膽。如果不想觸雷,唯一的解決辦法就是發(fā)現(xiàn)所有潛伏的地雷并且排除它們,躲是躲不了的。本章的內(nèi)容比一般教科書的要深入得多,讀者需細心閱讀,做到真正地通曉內(nèi)存管理。7.!內(nèi)存分配方式內(nèi)存分配方式有三種:(1)從靜態(tài)存儲區(qū)域分配。內(nèi)存在程序編譯的時候就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間都存在。例如全局變量,static變量。(2)在棧上創(chuàng)建。在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些存儲單元自動被釋放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容置有限。(3)從堆上分配,亦稱動態(tài)內(nèi)存分配。程序在運行的時候用malloc或new申請任意多少的內(nèi)存,程序員自己負責在何時用free或delete釋放內(nèi)存。動態(tài)內(nèi)存的生存期由我們決定,使用非常靈活,但問題也最多。常見的內(nèi)存錯誤及其對策發(fā)生內(nèi)存錯誤是件非常麻煩的事情。編譯器不能自動發(fā)現(xiàn)這些錯誤,通常是在程序運行時才能捕捉到。而這些錯誤大多沒有明顯的癥狀,時隱時現(xiàn),增加了改錯的難度。有時用戶怒氣沖沖地把你找來,程序卻沒有發(fā)生任何問題,你一走,錯誤又發(fā)作了。常見的內(nèi)存錯誤及其對策如下:u 內(nèi)存分配未成功,卻使用了它。編程新手常犯這種錯誤,因為他們沒有意識到內(nèi)存分配會不成功。常用解決辦法是,在使用內(nèi)存之前檢查指針是否為NULLo如果指針p是函數(shù)的參數(shù),那么在函數(shù)的入口處用assert(p!=NULL)進行檢査。如果是用malloc或new來申請內(nèi)存,應(yīng)該用if(p==NULL)或if(p!=NULL)進行防錯處理。內(nèi)存分配雖然成功,但是尚未初始化就引用它。犯這種錯誤主要有兩個起因:ー是沒有初始化的觀念;二是誤以為內(nèi)存的缺省初值全為考,導(dǎo)致引用初值錯誤(例如數(shù)組)。內(nèi)存的缺省初值究竟是什么并沒有統(tǒng)ー的標準,盡管有些時候為零值,我們寧可信其無不可信其有。所以無論用何種方式創(chuàng)建數(shù)組,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。內(nèi)存分配成功并且已經(jīng)初始化,但操作越過了內(nèi)存的邊界。例如在使用數(shù)組時經(jīng)常發(fā)生下標“多1”或者“少ド的操作。特別是在for循環(huán)語句中,循環(huán)次數(shù)很容易搞錯,導(dǎo)致數(shù)組操作越界。忘記了釋放內(nèi)存,造成內(nèi)存泄露。含有這種錯誤的函數(shù)每被調(diào)用
溫馨提示
- 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)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度光伏發(fā)電項目施工合同范本(含售后服務(wù))
- 2025年度區(qū)塊鏈技術(shù)應(yīng)用融資借款合同
- 2025年度養(yǎng)老地產(chǎn)合作開發(fā)合同風險評估與管理
- 2025年度保險代理合法委托代理合同范本
- 2025年度體育場館場地租賃與健身活動服務(wù)合同
- 2025年度人力資源外包合同翻譯與合規(guī)性審核合同
- 2025年度會議室租賃合同(含會議設(shè)備租賃)
- 2025年度商業(yè)保理合同延期及調(diào)整協(xié)議范本
- 2025年度化妝品品牌授權(quán)加盟店管理合同
- 2025年光伏發(fā)電項目環(huán)境影響評價與監(jiān)測服務(wù)合同
- 2024北京海淀高三一模英語試卷(含參考答案)
- 三高疾病之中醫(yī)辨證施治
- 全科醫(yī)學(xué)的基本原則和人文精神(人衛(wèi)第五版全科醫(yī)學(xué)概論)
- 船員健康知識課件
- 成人住院患者靜脈血栓栓塞癥預(yù)防護理
- 《揚州東關(guān)街掠影》課件
- 《3-6歲兒童學(xué)習(xí)與發(fā)展指南》健康領(lǐng)域內(nèi)容目標與指導(dǎo)
- GB/T 10739-2023紙、紙板和紙漿試樣處理和試驗的標準大氣條件
- 環(huán)保行業(yè)研究報告
- 孩子撫養(yǎng)費起訴狀范本:免修版模板范本
- 物流服務(wù)項目的投標書
評論
0/150
提交評論