![(完整word版)C++代碼規(guī)范_第1頁](http://file4.renrendoc.com/view/0c79f3e5d55e3f522942ad57815285d8/0c79f3e5d55e3f522942ad57815285d81.gif)
![(完整word版)C++代碼規(guī)范_第2頁](http://file4.renrendoc.com/view/0c79f3e5d55e3f522942ad57815285d8/0c79f3e5d55e3f522942ad57815285d82.gif)
![(完整word版)C++代碼規(guī)范_第3頁](http://file4.renrendoc.com/view/0c79f3e5d55e3f522942ad57815285d8/0c79f3e5d55e3f522942ad57815285d83.gif)
![(完整word版)C++代碼規(guī)范_第4頁](http://file4.renrendoc.com/view/0c79f3e5d55e3f522942ad57815285d8/0c79f3e5d55e3f522942ad57815285d84.gif)
![(完整word版)C++代碼規(guī)范_第5頁](http://file4.renrendoc.com/view/0c79f3e5d55e3f522942ad57815285d8/0c79f3e5d55e3f522942ad57815285d85.gif)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
CopyrightXXXCopyrightXXX技術(shù)有限公司C++代碼規(guī)范版本歷史版本/狀態(tài)作者參與者起止日期備注V1.0周國飛2003-4-15至2004-22周國飛起草目錄TOC\o"1-5"\h\z1.介紹12.編碼規(guī)范22.1文件結(jié)構(gòu)22.1.1版權(quán)和版本的聲明2頭文件的結(jié)構(gòu)2定義文件的結(jié)構(gòu)32.1.4目錄結(jié)構(gòu)4結(jié)構(gòu)化程序設(shè)計4功能模塊抽取4功能模塊編碼原則5編程標準6源代碼層次6命名約定72.3.1綜述72.3.2變量命名82.3.3函數(shù)及數(shù)組的命名92.3.4結(jié)構(gòu)類型命名102.3.5命名長度10Windows應(yīng)用程序命名規(guī)則10程序規(guī)則12變量聲明和定義122.4.2數(shù)組、字符串13函數(shù)聲明和定義14語句16排版格式規(guī)則162.5.1源代碼文件17空行172.5.3代碼行17代碼行內(nèi)的空格18對齊19分行202.5.7表達式202.5.8函數(shù)22語句22變量、類型聲明232.5.11修飾符的位置232.5.12類的版式242.6注釋格式242.6.1介紹242.6.2注釋基本規(guī)則25程序注釋25模塊注釋26
273.代碼管理錯誤!未定義書簽。2.6.5273.代碼管理錯誤!未定義書簽。3.1版本管理613.2代碼更新61C++語言編程規(guī)范C++語言編程規(guī)范CopyrightCopyright中興信息技術(shù)有限公司第頁共71頁2.4.3函數(shù)聲明和定義注意要點函數(shù)一定要先聲明后使用。函數(shù)原型話頭文件中。函數(shù)沒有參數(shù)的用void說明。盡量保持函數(shù)只有一個出口。不要寫超過三層的嵌套邏輯(if,while,for…),這樣會犧牲程序的易讀性,如果有這樣的情況,需要拆分為多個函數(shù)。盡量簡化main()函數(shù)的邏輯盡量隱藏函數(shù):區(qū)分公有函數(shù)、私有函數(shù)、保護函數(shù)、某個模塊的函數(shù)。對以上所有函數(shù)在適當位置進行聲明。不要編寫多種功能集于一身的函數(shù),為了對參數(shù)更強的確認,請編寫功能單一的函數(shù)。不同層次、不同功能的函數(shù)盡量放在不同的文件中。如果一個函數(shù)有返回值,則調(diào)用它的函數(shù)必須檢驗這個返回值。參數(shù)傳遞對內(nèi)部數(shù)據(jù)類型參數(shù),如函數(shù)內(nèi)部不對其修改應(yīng)使用值傳遞方式。對非內(nèi)部數(shù)據(jù)類型參數(shù)應(yīng)采用引用傳遞或指針傳遞方式盡量少用參數(shù)不確定的函數(shù)。在函數(shù)體的“入口處”,對參數(shù)的有效性進行檢查。如果參數(shù)是指針,且僅作輸入用,則應(yīng)在類型前加const,以防止該指針在函數(shù)體內(nèi)被意外修改。例如:char顯式定義函數(shù)返回值的類型,如果函數(shù)沒有返回值,那么應(yīng)聲明為void類型。除了void返回值類型函數(shù),函數(shù)都要有返回值。顯式定義函數(shù)返回值的類型,如果函數(shù)沒有返回值,那么應(yīng)聲明為void類型。除了void返回值類型函數(shù),函數(shù)都要有返回值。返回指針類型的函數(shù)用NULL表示失敗。如果輸入?yún)?shù)以值傳遞的方式傳遞對象,則宜改用“const&”方式來傳遞,這樣可以省去臨時對象的構(gòu)造和析構(gòu)過程,從而提高效率。例如:BOOLFindUser(constCString&strUserName);參數(shù)不超過6個。盡量少寫5、6個參數(shù)的函數(shù)。參數(shù)過多時可以考慮定義結(jié)構(gòu),把參數(shù)包含在其中。盡量保持函數(shù)的返回值為int型,而且保持返回0為成功。小于0表示錯誤。函數(shù)返回值函數(shù)返回時盡量采用“返回引用”而不是“返回值”。例如:classPerson{public:Person();~Person();conststring&GetName()const{returnstrName;}string&GetAddress()const{returnstrAddress;}//不良用法private:stringstrName;stringstrAddress;};避免這樣的成員函數(shù):其返回值是指向成員的非const指針或引用,如上面Person類中的GetAddress成員函數(shù),因為這個操作可以改變strAddress的值,破壞了類的封裝性。千萬不要返回局部變量或局部對象的引用,因為當函數(shù)返回時它就不存在了;也不要返回函數(shù)內(nèi)部用new初始化的指針的引用,因為容易發(fā)生內(nèi)存泄漏或異常。函數(shù)必須返回一個對象時不要試圖返回一個引用。在函數(shù)中使用斷言程序一般分為Debug版本和Release版本,Debug版本用于內(nèi)部調(diào)試,Release版本發(fā)行給用戶使用。斷言assert是僅在Debug版本起作用的宏,它用于檢查“不應(yīng)該”發(fā)生的情況。下面示例是一個內(nèi)存復(fù)制函數(shù)。在運行過程中,如果assert的參數(shù)為假,那么程序就會中止(一般地還會出現(xiàn)提示對話,說明在什么地方引發(fā)了assert)。void*memcpy(void*pvTo,constvoid*pvFrom,size_tsize){assert((pvTo!=NULL)&&(pvFrom!=NULL));//使用斷言assert(pbTo〉=pbFrom+size||pbFrom〉二pbTo+size);//防止內(nèi)存塊重疊byte*pbTo=(byte*)pvTo;//防止改變pvTo的地址byte*pbFrom=(byte*)pvFrom;//防止改變pvFrom的地址while(size—>0){*pbTo++=*pbFrom++;}returnpvTo;}示例復(fù)制不重疊的內(nèi)存塊assert不是一個倉促拼湊起來的宏。為了不在程序的Debug版本和Release版本引起差別‘a(chǎn)ssert不應(yīng)該產(chǎn)生任何副作用。所以assert不是函數(shù),而是宏。程序員可以把assert看成一個在任何系統(tǒng)狀態(tài)下都可以安全使用的無害測試手段。如果程序在assert處終止了,并不是說含有該assert的函數(shù)有錯誤,而是調(diào)用者出了差錯,assert可以幫助我們找到發(fā)生錯誤的原因。很少有比跟蹤到程序的斷言,卻不知道該斷言的作用更讓人沮喪的事了。你化了很多時間,不是為了排除錯誤,而只是為了弄清楚這個錯誤到底是什么。有的時候,程序員偶爾還會設(shè)計出有錯誤的斷言。所以如果搞不清楚斷言檢查的是什么,就很難判斷錯誤是出現(xiàn)在程序中,還是出現(xiàn)在斷言中。幸運的是這個問題很好解決,只要加上清晰的注釋即可。這本是顯而易見的事情,可是很少有程序員這樣做。這好比一個人在森林里,看到樹上釘著一塊“危險”的大牌子。但危險到底是什么?樹要倒?有廢井?有野獸?除非告訴人們“危險”是什么,否則這個警告牌難以起到積極有效的作用。難以理解的斷言常常被程序員忽略,甚至被刪除。斷言不是用來檢查錯誤的,使用斷言捕捉不應(yīng)該發(fā)生的非法情況。不要混淆非法情況與錯誤情況之間的區(qū)別,后者是必然存在的并且是一定要作出處理的。在函數(shù)的入口處,使用斷言檢查參數(shù)的有效性(合法性),并且在程序員使用了無定義的特性時向程序員報警。函數(shù)定義得越嚴格,確認其參數(shù)就越容易。在編寫函數(shù)時,要進行反復(fù)的考查,并且自問:“我打算做哪些假定?”一旦確定了相應(yīng)的假定,就要使用斷言對所做的假定進行檢驗,或者重新編寫代碼去掉相應(yīng)的假定。另外,還要問:“這個程序中最可能出錯的是什么,怎樣才能自動地查出相應(yīng)的錯誤?”努力編寫出能夠盡早查出錯誤的測試程序。一般教科書都鼓勵程序員們進行防錯設(shè)計,但要記住這種編程風(fēng)格可能會隱瞞錯誤。當進行防錯設(shè)計時,如果“不可能發(fā)生”的事情的確發(fā)生了,則要使用斷言進行報警。2.4.4語句嵌套限制禁止超過三層的嵌套。禁止語句一般的,盡是少用goto語句。用if語句來強調(diào)只執(zhí)行兩組語句中的一組。禁止elsegoto和elsereturn。2.5排版格式規(guī)則良好的代碼排版格式會使程序結(jié)構(gòu)清晰,利于理解,方便查錯。對于C++語言的排版格式可以遵循幾種事實標準。以下提供一種排版格式的建議。最終目的要達到,開發(fā)出的代碼具有美觀、易讀、風(fēng)格統(tǒng)一的特點。2.5.1源代碼文件一個項目的源代碼必須保證風(fēng)格的一致性,這不僅對于項目開發(fā)者、項目集成者、管理者非常重要,而且利于代碼的維護及再使用。分頁清晰:源代碼應(yīng)保證每頁少于60行;在明顯的邏輯分界區(qū)要顯式分頁。2.5.2空行空行起著分隔程序段落的作用??招械皿w(不過多也不過少)將使程序的布局更加清晰。空行不會浪費內(nèi)存,雖然打印含有空行的程序是會多消耗一些紙張,但是值得所以不要舍不得用空行。在每個類聲明之后、每個函數(shù)定義結(jié)束之后都要加空行。參見示例2-3(a)在一個函數(shù)體內(nèi),邏揖上密切相關(guān)的語句之間不加空行,其它地方應(yīng)加空行分隔。參見示例2-3(b)//空行voidFunctionl(…){???}//空行voidFunction2(…){???}//空行voidFunction3(…){???}//空行while(condition){statement"http://空行if(condition){statement2;}else{statement3;}//空行statement4;}示例2-3(a)函數(shù)之間的空行示例2-3(b)函數(shù)內(nèi)部的空行2.5.3代碼行一行代碼只做一件事情,如只定義一個變量,或只寫一條語句。這樣的代碼容易閱讀,并且方便于寫注釋。if、for、while、do等語句自占一行,執(zhí)行語句不得緊跟其后。不論執(zhí)行語句有多少都要加{}。這樣可以防止書寫失誤。示例2-4(a)為風(fēng)格良好的代碼行,示例2-4(b)為風(fēng)格不良的代碼行。intnWidth;//寬度intnHeight;//高度intnDepth;//深度intnWidth,nHeight,nDepth;//寬度高度深度x=a+b;y=c+d;z=e+f;X=a+b;y=c+d;z=e+f;if(nWidth<nHeight){dosomething();}if(nWidth<nHeight)dosomething();for(initialization;condition;update){dosomething();}//空行other();for(initialization;condition;update)dosomething();other();示例2-4(a)風(fēng)格良好的代碼行示例2-4(b)風(fēng)格不良的代碼行?盡可能在定義變量的同時初始化該變量(就近原則)如果變量的引用處和其定義處相隔比較遠,變量的初始化很容易被忘記。如果引用了未被初始化的變量,可能會導(dǎo)致程序錯誤。本建議可以減少隱患。例如intnWidth=10;//定義并初紿化nWidthintnHeight=10;//定義并初紿化nHeightintnDdepth=10;//定義并初紿化nDepth2.5.4代碼行內(nèi)的空格?關(guān)鍵字之后要留空格。象const、virtual、inline、case等關(guān)鍵字之后至少要留一個空格,否則無法辨析關(guān)鍵字。象if、for、while等關(guān)鍵字之后應(yīng)留一個空格再跟左括號‘(',以突出關(guān)鍵字。?函數(shù)名之后不要留空格,緊跟左括號‘(',以與關(guān)鍵字區(qū)別。?‘('向后緊跟,‘)'、‘,'、‘;'向前緊跟,緊跟處不留空格?!笠艨崭?,如Function(x,y,z)。如果'不是一行的結(jié)束符號,其后要留空格,如for(initialization;condition;update)。賦值操作符、比較操作符、算術(shù)操作符、邏輯操作符、位域操作符,如“=”、“+=”“>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“人”等二元操作符的前后應(yīng)當加空格。一元操作符如“!”、“~”、“++”、“--”、“&”(地址運算符)等前后不加空格。象“[]”、“.”、“->”這類操作符前后不加空格。對于表達式比較長的for語句和if語句,為了緊湊起見可以適當?shù)厝サ粢恍┛崭?,如for(i=0;i〈10;i++)和if((a<=b)&&(c<=d))voidFunc1(intx,inty,intz);//良好的風(fēng)格voidFunci(intx,inty,intz);//不良的風(fēng)格
f(nYear>=2000)//良好的風(fēng)格f(year〉=2000)//不良的風(fēng)格f((a〉二b)&&(c<=d))//良好的風(fēng)格if(a>=b&&c<=d)//不良的風(fēng)格for(i=0;i〈10;i++)//良好的風(fēng)格for(i=0;i〈10;i++)//不良的風(fēng)格for(i=0;i<10;i++)//過多的空格x=a<b?a:b;//良好的風(fēng)格x=a〈b?a:b;//不好的風(fēng)格int*x=&y;//良好的風(fēng)格int*x=&y;//不良的風(fēng)格array[5]=0;//不要寫成array]5]=0;a.Function();//不要寫成a.Function();b—〉Function();//不要寫成b-〉Function();示例2-5代碼行內(nèi)的空格2.5.5對齊程序的分界符'{'和'}'應(yīng)獨占一行并且位于同一列,同時與引用它們的語句左對齊。{}之內(nèi)的代碼塊在‘{'右邊數(shù)格處左對齊。示例2-4(a)為風(fēng)格良好的對齊,示例2-4(b)為風(fēng)格不良的對齊。voidFunction(intx){…//programcode}voidFunction(intx){…//programcode}if(condition){…//programcode}else{…//programcode}if(condition){…//programcode}else{…//programcode}for(initialization;condition;update){…//programcode}for(initialization;condition;update){…//programcode}While(condition)1〃programcodewhile(condition){…//programcode如果出現(xiàn)嵌套的{},則使用縮進對齊,如:………示例2-6(a)風(fēng)格良好的對齊示例2-6(b)風(fēng)格不良的對齊2.5.6分行代碼行最大長度宜控制在70至80個字符以內(nèi)。代碼行不要過長,否則眼睛看不過來,也不便于打印。當表達式分為多行時,注意分行位置必須有邏輯意義.盡量在操作符,并且是低優(yōu)先級的操作符時分行。操作符放在新行之首(以便突出操作符).拆分出的新行要進行適當?shù)目s進,使排版整齊,語句可讀。不要在單目運算處分行。if((very_longer_variablel>=very_longer_variablel2)&&(very_longer_variable3<=very_longer_variable14)&&(very_longer_variable5<=very_longer_variablel6)){dosomething();}virtualCPointCMultiplyCPoint(CPointleftCPoint,CPointrightCPoint);for(very_longer_initialization;very_longer_condition;very_longer_update){dosomething();}示例2-7長行的拆分2.5.7表達式C++表達式需要符合以下規(guī)則,每個規(guī)則都給出了實例。注意,盡量多用括號,防止表達式的歧意?!鰞稍?、多重運算和邏輯操作:規(guī)定在運算符的前后應(yīng)插入空格鍵,以利于閱讀。nHeight=nHeight*3.9+10;例外:為利于閱讀,在子表達式、語句中的表達式、參數(shù)中的表達式運算符前后加一個空格for(nNCount=0,nStart=1;nCount<MacMaxNumber;nCount++)單目運算和邏輯操作:單目運算符之間不加空格nCount-=nCount;nMask=nMask&(~nError;)■強制類型轉(zhuǎn)化:類型強制轉(zhuǎn)換是盡量少用空格,如下例所示:nHeight=(float)nLength/10;■復(fù)雜數(shù)據(jù)引用:如下組織標識符,不加空格:[],.,chStartPoint=sCircle[3];需要判斷優(yōu)先級的表達式,要用"()"來標明不同的優(yōu)先級,以便程序的閱讀。不查運算符優(yōu)先級表,如果必須通過查表才能確定優(yōu)先順序的話,那就太復(fù)雜了,簡單一點,可以在不需要括號的地方插入括號。這樣做不僅正確,而且顯然可使任何人不經(jīng)查表就可判斷優(yōu)先級了。不要編寫太復(fù)雜的復(fù)合表達式。例如:i=a>=b&&c<d&&c+f<=g+h;//復(fù)合表達式過于復(fù)雜不要有多用途的復(fù)合表達式。例如:d=(a=b+c)+r;該表達式既求a值又求d值。應(yīng)該拆分為兩個獨立的語句:a=b+c;d=a+r;讓表達式直觀,避免在表達式中用賦值語句if(strlen(chBuffer))//不直觀if(strlen(chBuffer)!=0)//直觀if(UserName.IsEmpty())//直觀if(!quene.IsNotFull())//不直觀if(quene.IsFull())//直觀//表達式中用賦值語句不直觀fArea=(fWidth*=SCALE_FACTOR)*(fHeight*=SCALE_FACTOR);不要把程序中的復(fù)合表達式與“真正的數(shù)學(xué)表達式”混淆。例如:if(a<b<c)//a<b<c是數(shù)學(xué)表達式而不是程序表達式并不表示if((a<b)&&(b<c))而是成了令人費解的if((a<b)<c)2.5.8函數(shù)函數(shù)名和左括號之間不加空格.如果函數(shù)不包含參數(shù),括號之間、函數(shù)名和括號之間均不加空格。如果包含參數(shù),參數(shù)之間、參數(shù)和右括號之間至少要有一個空格。參數(shù)和逗號之間不要有空格。.chInChar=getchar()fprintf(fpInputFile,“%d%s”,nNumber,sName);函數(shù)聲明使用以下規(guī)則:必須首先聲明函數(shù)原型.函數(shù)名和左括號之間不要插入空格。參數(shù)之后緊接著逗號,逗號后至少跟一個空格.參數(shù)變量盡量有意義.使用空行隔開邏輯塊。局部變量生命作為一個邏輯塊。recNoderNewNode();stringsGetUserName(stringsUserName,stringsPasswd,stringsCity);2.5.9語句縮進、空格、排列須遵守以下規(guī)則,使代碼具有一致性、易讀性在一行中不要寫多個語句。嵌套語句縮進TAB。排列本身不縮格;排列內(nèi)部的語句縮TAB。大括號上下對齊。If((nReturnCode=nQueryLine(sStatement,param1,param2,))!=NULL){}空語句獨占一行,并且使用NULL語句表示之。While(getchar()!=‘\n'){NULL;}2.5.10變量、類型聲明當進行變量、類型聲明時使用以下規(guī)則變量聲明每一行聲明一個變量,緊跟分號,不要加空格.當要立即初始化是,使用易讀的縮格.對齊公用同一類型聲明的變量,逗號、分號之前都不要加空格.intnLength;intnHeight;intnWidth;doubledfLast;doubledfFirst;結(jié)構(gòu)聲明結(jié)構(gòu)的類型聲明全部采用類型定義的方式。類型聲明本身共占一行,前括號獨占一行,后括號獨占一行;類型的元素各占一行,左對齊.class_recAccount{char*sName;char*sPassword;};2.5.11修飾符的位置修飾符*和&應(yīng)該靠近數(shù)據(jù)類型還是該靠近變量名,是個有爭議的活題。若將修飾符*靠近數(shù)據(jù)類型,例如:int*n;從語義上講此寫法比較直觀,即n是int類型的指針。上述寫法的弊端是容易引起誤解,例如:int*m,n;此處n容易被誤解為指針變量。雖然將m和n分行定義可以避免誤解,但并不是人人都愿意這樣做。應(yīng)當將修飾符*和&緊靠變量名例如:char*chName;int*m,n;//此處y不會被誤解為指針2.5.12類的版式類可以將數(shù)據(jù)和函數(shù)封裝在一起,其中函數(shù)表示了類的行為(或稱服務(wù))。類提供關(guān)鍵字public、protected和private,分別用于聲明哪些數(shù)據(jù)和函數(shù)是公有的、受保護的或者是私有的。這樣可以達到信息隱藏的目的,即讓類僅僅公開必須要讓外界知道的內(nèi)容,而隱藏其它一切內(nèi)容。我們不可以濫用類的封裝功能,不要把它當成火鍋,什么東西都往里扔。類的版式主要有兩種方式:(1)將private類型的數(shù)據(jù)寫在前面,而將public類型的函數(shù)寫在后面,如示例2-8(a)。采用這種版式的程序員主張類的設(shè)計“以數(shù)據(jù)為中心”,重點關(guān)注類的內(nèi)部結(jié)構(gòu)。(2)將public類型的函數(shù)寫在前面,而將private類型的數(shù)據(jù)寫在后面,如示例2-8(b)采用這種版式的程序員主張類的設(shè)計“以行為為中心”重點關(guān)注的是類應(yīng)該提供什么樣的接口(或服務(wù))。建議讀者采用“以行為為中心”的書寫方式,即首先考慮類應(yīng)該提供什么樣的函數(shù)。這是很多人的經(jīng)驗——“這樣做不僅讓自己在設(shè)計類時思路清晰,而且方便別人閱讀。因為用戶最關(guān)心的是接口,誰愿意先看到一堆私有數(shù)據(jù)成員!”classAclassA{{private:public:intm,n;voidFuncl(void);floatx,y;voidFunc2(void);protected:protected:intmnValue;intmnValue;public:private:voidFuncl(void);intm,n;voidFunc2(void);floatx,y;}}示例2-8(a)以數(shù)據(jù)為中心版式示例2-8(b)以行為為中心的版式2.6注釋格式2.6.1介紹好的程序注釋可以為程序邏輯的清晰性提供極大的幫助,對于程序的維護、修改更具有不可估量的作用。C語言的注釋符為“/*???*/”C++語言中,程序塊的注釋常采用“/*???*/”,行注釋一般采用“//…”。注釋通常用于:(1)版本、版權(quán)聲明;函數(shù)接口說明;重要的代碼行或段落提示。雖然注釋有助于理解代碼,但注意不可過多地使用注釋。源代碼中的注釋可以分為三類:?程序級注釋?模塊級注釋函數(shù)級注釋編程中必須添加以上三種注釋。另外特殊邏輯的行也要注釋說明;當長的if,while和其他類似語句出現(xiàn)時,要象如下格式注釋:while(nRc==MacNotFound){/*Linesofcode*/}/*endwhile(nRc=MacNotFound)*/注意注釋必須是必要的,間接、明確的,避免不必要的、冗長的注釋。當程序作重大邏輯、功能修改時,必須添加修改日志。2.6.2注釋基本規(guī)則注釋是對代碼的“提示”,而不是文檔。程序中的注釋不可喧賓奪主,注釋太多了會讓人眼花繚亂。注釋的花樣要少。如果代碼本來就是清楚的,則不必加注釋。否則多此一舉,令人厭煩。例如i++;//i加1,多余的注釋邊寫代碼邊注釋,修改代碼同時修改相應(yīng)的注釋,以保證注釋與代碼的一致性。不再有用的注釋要刪除。注釋應(yīng)當準確、易懂,防止注釋有二義性。錯誤的注釋不但無益反而有害。盡量避免在注釋中使用縮寫,特別是不常用縮寫。注釋的位置應(yīng)與被描述的代碼相鄰,可以放在代碼的上方或右方,不可放在下方。當代碼比較長,特別是有多重嵌套時,應(yīng)當在一些段落的結(jié)束處加注釋,便于閱讀。程序代碼中的主要邏輯部分要有適當?shù)淖⑨?。在最終版本上修改文件是要注釋,修改人及修改內(nèi)容和修改時間。復(fù)雜條件判斷要加簡單注釋說明含義對于函數(shù)內(nèi)部的重要變量做簡單注釋。頭文件中的常量等需要注釋其作用。2.6.3程序注釋放在每個程序的開始。包含以下部分:.ProgramID運行程序名
DescriptionInputParametersExitValuesInputFilesOutputFilesSpecialNotesModificationLog邏輯描述輸入?yún)?shù)退出值極其含義文件輸入文件輸出指明程序的使用限制及程序中不易理解的邏輯。修改日志程序說明示例:/*******************************************************************************************ProgramID:userManager.cDescription:ThisprogramwillmanagenetworkuseracnCountInputparms:-nUserName-pUserPasswordExitvalue:SUCCESS-normalterminationFAILURE-abnormalterminationInputFiles:NoneOutputFiles:error.log-theerrorlogfileSpecialnotes:NoneModificationLog:DATEAUTHORDESCRIPTION07/30/2002TangWangInitialRelease**************************************************************************************2.6.4模塊注釋每個模塊應(yīng)包含以下模塊注釋:ProgramID運行程序名Description模塊邏輯Functions本模塊函數(shù)聲明示例:模塊說明****************************************************************************************ProgramID:addUser.cDescription:ThismodulewilladdausertothesystemFunctions:boolbSortName()boolbCheckDuplicate()boolbAddUser()ModificationLog:DATEAUTHORDESCRIPTION*07302002TiangWangInitialRelease****************************************************************************************2.6.5函數(shù)注釋在每個函數(shù)開始之前,應(yīng)該加以下注釋:Functionname函數(shù)名.Description功能簡介Parameters參數(shù)說明?類型?參數(shù)名?輸入、輸出參數(shù)功能使用簡要描述.ReturnValue返回值描述:類型名字簡要描述,例如可能的值ModificationLog修改日志示例:函數(shù)說明/*****************************************************************************************ProgramID:sortnameDescription:ThisfunctionwillsorttheusernameParameters:char**psInputNameList(I)-inputnamelistchar**psSortNameList(O)-sortednamelistReturnValue:boolSUCCESS-sortsuccessfulFAILURE-sortfailedModificationLog:DATEAUTHORDESCRIPTION*073096TangWangInitialRelease3?程序設(shè)計3.1基本語句3.1.1if語句if語句是C++/C語言中最簡單、最常用的語句,然而很多程序員用隱含錯誤的方式寫if語句。本節(jié)以“與零值比較”為例,展開討論。布爾變量與零值比較不可將布爾變量直接與TRUE、FALSE或者1、0進行比較根據(jù)布爾類型的語義,零值為“假”(記為FALSE),任何非零值都是“真”(記為TRUE)。TRUE的值究竟是什么并沒有統(tǒng)一的標準。例如VisualC++將TRUE定義為1,而VisualBasic則將TRUE定義為-1。假設(shè)布爾變量名字為flag,它與零值比較的標準if語句如下:if(flag)//表示flag為真if(!flag)//表示flag為假其它的用法都屬于不良風(fēng)格,例如:if(flag==TRUE)if(flag==1)if(flag==FALSE)if(flag==0)整型變量與零值比較應(yīng)當將整型變量用“==”或“!=”直接與0比較。假設(shè)整型變量的名字為value,它與零值比較的標準if語句如下:if(value==0)if(value!=0)不可模仿布爾變量的風(fēng)格而寫成if(value)//會讓人誤解value是布爾變量if(!value)浮點變量與零值比較不可將浮點變量用“==”或“!=”與任何數(shù)字比較。千萬要留意,無論是float還是double類型的變量,都有精度限制。所以一定要避免將浮點變量用“==”或“!=”與數(shù)字比較,應(yīng)該設(shè)法轉(zhuǎn)化成“>=”或“<=”形式。
假設(shè)浮點變量的名字為X,應(yīng)當將if(x==0.0)//隱含錯誤的比較轉(zhuǎn)化為if((X>=-EPSINON)&&(X<=EPSINON))其中EPSINON是允許的誤差(即精度)。指針變量與零值比較應(yīng)當將指針變量用“==”或“!=”與NULL比較。指針變量的零值是“空”(記為NULL)。盡管NULL的值與0相同,但是兩者意義不同。假設(shè)指針變量的名字為P,它與零值比較的標準if語句如下:if(p==0)if(p!=0)或者if(p)if(!p)if(p==NULL)if(p!=NULL)不要寫成//pif(p==NULL)if(p!=NULL)不要寫成//容易讓人誤解p是整型變量//容易讓人誤解p是布爾變量補充說明程序中有時會遇到if/else/return的組合,應(yīng)該將如下不良風(fēng)格的程序if(condition)returnX;returny;改寫為if(condition){returnX;}else{returny;}或者改寫成更加簡練的return(condition?X:y);3.1.2循環(huán)語句的效率C++/C循環(huán)語句中,for語句使用頻率最高,while語句其次,do語句很少用。本節(jié)重點論述循環(huán)體的效率。提高循環(huán)體效率的基本辦法是降低循環(huán)體的復(fù)雜性。?如果循環(huán)體內(nèi)存在邏輯判斷,并且循環(huán)次數(shù)很大,宜將邏輯判斷移到循環(huán)體的外面。示例3-l(a)的程序比示例3-l(b)多執(zhí)行了N-1次邏輯判斷。并且由于前者老要進行邏輯判斷,打斷了循環(huán)“流水線”作業(yè),使得編譯器不能對循環(huán)進行優(yōu)化處理降低了效率。如果N非常大,最好采用示例3-1(b)的寫法,可以提高效率。如果N非常小,兩者效率差別并不明顯,采用示例3-1(a)的寫法比較好,因為程序更加簡潔。for(i=0;i〈n;i++){if(condition){if(condition)for(i=0;i<n;i++)DoSomething();DoSomething();else}DoOtherthing();else}{for(i=0;i<n;i++)}DoOtherthing();表3T(a)效率低但程序簡潔表3T(b)效率高但程序不簡潔3.1.3for語句的循環(huán)控制變量不可在for循環(huán)體內(nèi)修改循環(huán)變量,防止for循環(huán)失去控制。建議for語句的循環(huán)控制變量的取值采用“半開半閉區(qū)間”寫法。示例3-2(a)中的x值屬于半開半閉區(qū)間“0=<x<N”,起點到終點的間隔為N,循環(huán)次數(shù)為N。示例3-2(b)中的x值屬于閉區(qū)間“0=<x<=N-1”,起點到終點的間隔為N-1,循環(huán)次數(shù)為N。相比之下,示例3-2(a)的寫法更加直觀,盡管兩者的功能是相同的。for(inti=0;i〈n;i++){???}for(inti=0;i〈二nT;i++){???}示例3-2(a)循環(huán)變量屬于半開半閉區(qū)間示例3-2(b)循環(huán)變量屬于閉區(qū)間3.1.4switch語句有了if語句為什么還要switch語句?switch是多分支選擇語句,而if語句只有兩個分支可供選擇。雖然可以用嵌套的if語句來實現(xiàn)多分支選擇,但那樣的程序冗長難讀。這是switch語句存在的理由。switch語句的基本格式是:switch(variable){TOC\o"1-5"\h\zcasevalue1:…break;casevalue2:…break;???default:…break;}每個case語句占用一行,case語句按字母順序排列。每個case語句的結(jié)尾不要忘了加break,否則將導(dǎo)致多個分支重疊(除非有意使多個分支重疊,這種情況要加注釋說明)。不要忘記最后那個default分支。即使程序真的不需要default處理,也應(yīng)該保留語句default:break;這樣做并非多此一舉,而是為了防止別人誤以為你忘了default處理。3?1?5goto語句由于got。語句可以靈活跳轉(zhuǎn),如果不加限制,它的確會破壞結(jié)構(gòu)化設(shè)計風(fēng)格。其次,goto語句經(jīng)常帶來錯誤或隱患。它可能跳過了某些對象的構(gòu)造、變量的初始化、重要的計算等語句,例如:gotostate;Strings1,s2;//被goto跳過intsum=0;//被goto跳過???state:???如果編譯器不能發(fā)覺此類錯誤,每用一次goto語句都可能留下隱患。但是goto語句的效率很高,若運用得當,用goto語句的實現(xiàn)可讀性強于用非goto語句的實現(xiàn)。因此合理的原則是盡是少用goto語句,要使用時遵循以下原則:在程序為單一入口、單一出口時可使用goto語句。?使用goto語句時盡量不要往回跳,可以往后跳。盡量不要用多于一的goto語句標記,這會導(dǎo)致程序跳來跳去,可讀性差。?要確保在使用goto語句時不要產(chǎn)生程序不可能執(zhí)行的代碼。以下是兩個使用goto語句的例子{…{…{…gotoerror;}}}error:???上例中g(shù)oto語句能從多重循環(huán)體中一下子跳到外面,用不著寫很多次的break語句。HRESULTInit(){char*pszMyName=(char*)malloc(…);if(pszMyName==NULL){gotoExit;}char*pszHeName=(char*)malloc(…);if(pszHeName==NULL){gotoExit;}char*pszItName=(char*)malloc(…);if(pszItName==NULL){gotoExit;}???Exit:if(pszMyName!=NULL){free(pszMyName);}if(pszheName!=NULL){free(pszHeName);}if(pszItName!=NULL){free(pszItName);}returnhr;}3.2常量常量是一種標識符,它允許你告訴編譯器和程序員其值在運行期間恒定不變。C語言用#define來定義常量(稱為宏常量)。C++語言除了#define外還可以用const來定義常量(稱為const常量),在C++中你應(yīng)盡可能使用const常量。3.2.1為什么需要常量如果不使用常量,直接在程序中填寫數(shù)字或字符串,將會有以下問題:1)程序的可讀性(可理解性)變差。程序員自己會忘記那些數(shù)字或字符串是什么意思,用戶則更加不知它們從何處來、表示什么。2)在程序的很多地方輸入同樣的數(shù)字或字符串,難保不發(fā)生書寫錯誤。3)如果要修改數(shù)字或字符串,則會在很多地方改動,既麻煩又容易出錯。盡量使用含義直觀的常量來表示那些將在程序中多次出現(xiàn)的數(shù)字或字符串。例如:#defineMAX_PATH260/*C語言的宏常量*/constintMAX_LENGTH=100;//C++語言的const常量constfloatPI=3.1415926;//C++語言的const常量3.2.2盡量用const而不用#defineC++語言可以用const來定義常量,也可以用#define來定義常量。在C++程序中應(yīng)盡量使用const常量而不使用宏常量,即const常量完全取代宏常量。因為前者比后者有更多的優(yōu)點:(1)const常量有數(shù)據(jù)類型,而宏常量沒有數(shù)據(jù)類型。編譯器可以對前者進行類型安全檢查。而對后者只進行字符替換,沒有類型安全檢查,并且在字符替換可能會產(chǎn)生意料不到的錯誤(邊際效應(yīng))。(2)許多集成化的調(diào)試工具都可以對const常量進行調(diào)試,但是不能對宏常量進行調(diào)試。#defineMAX_LINES500編譯器會永遠也看不到MAX_LINES這個符號名,因為在源碼進入編譯器之前,它會被預(yù)處理程序去掉,于是MAX_LINES不會加入到符號列表中。如果涉及到這個常量的代碼在編譯時報錯,就會很令人費解,因為報錯信息指的是500,而不是MAX_LINES。如果MAX_LINES不是在你自己寫的頭文件中定義的,你就會奇怪500是從哪里來的,甚至?xí)〞r間跟蹤下去。這個問題也會出現(xiàn)在符號調(diào)試器中,因為同樣地,你所寫的符號名不會出現(xiàn)在符號列表中。解決這個問題的方案很簡單:不用預(yù)處理宏,定義一個常量:constintMAX_LINES=500;常量定義規(guī)則需要對外公開的常量放在頭文件中,不需要對外公開的常量放在定義文件的頭部。為便于管理,可以把不同模塊的常量集中存放在一個公共的頭文件中。如果某一常量與其它常量密切相關(guān),應(yīng)在定義中包含這種關(guān)系,而不應(yīng)給出一些孤立的值。例如:constfloatRADIUS=100;constfloatDIAMETER=RADIUS*2;指針中const的用法對指針來說,可以指定指針本身為const,也可以指定指針所指的數(shù)據(jù)為const,或二者同時指定為const,還有,兩者都不指定為const:char*p="hello";//非const指針,非const數(shù)據(jù)constchar*p="hello";//非const指針,const數(shù)據(jù)char*constp="hello";//const指針,非const數(shù)據(jù)constchar*constp="hello";//const指針,const數(shù)據(jù)一般來說,你可以在頭腦里畫一條垂直線穿過指針聲明中的星號(*)位置,如果const出現(xiàn)在線的左邊,指針指向的數(shù)據(jù)為常量;如果const出現(xiàn)在線的右邊,指針本身為常量;如果const在線的兩邊都出現(xiàn),二者都是常量。在指針所指為常量的情況下,有些程序員喜歡把const放在類型名之前,有些程序員則喜歡把const放在類型名之后、星號之前。所以,下面的函數(shù)取的是同種參數(shù)類型:classwidget{...};voidfl(constwidget*pw);//fl取的是指向widget常量對象的指針voidf2(widgetconst*pw);//同f2因為兩種表示形式在實際代碼中都存在,所以要使自己對這兩種形式都習(xí)慣,但在實際寫代碼時我們使用第一種寫法。3.2.5函數(shù)中const的用法const的一些強大的功能基于它在函數(shù)聲明中的應(yīng)用。在一個函數(shù)聲明中,const可以指的是函數(shù)的返回值,或某個參數(shù)。讓函數(shù)返回一個常量值經(jīng)??梢栽诓唤档桶踩院托实那闆r下減少用戶出錯的幾率。用const修飾函數(shù)的參數(shù)const只能修飾輸入?yún)?shù),如果參數(shù)作輸出參數(shù)用,不論它是什么數(shù)據(jù)類型,也不論它采用“指針傳遞”還是“引用傳遞”,都不能加const修飾,否則該參數(shù)將失去輸出功能。例如:char*strcpy(char*strDestination,constchar*strSource);strSource指針所指數(shù)據(jù)在strcpy函數(shù)中不允許被修改,只能作輸入?yún)?shù)使用,否則會引起編譯錯誤,strDestination作為輸出參數(shù),其所指數(shù)據(jù)在strcpy函數(shù)中可以被修改。因此不能加const修飾。對于內(nèi)部數(shù)據(jù)類型的輸入?yún)?shù),不要將“值傳遞”的方式改為'Const引用傳遞”。否則既達不到提高效率的目的,又降低了函數(shù)的可理解性。例如voidSetYear(intnYear)不應(yīng)該改為SetYear(constint&nYear)。對于非內(nèi)部數(shù)據(jù)類型的輸入?yún)?shù),應(yīng)該將“值傳遞”的方式改為'const引用傳遞”,目的是提高效率。例如將BOOLFindUser(CStringstrUserName)改為BOOLFindUser(constCString&strUserName)。用const修飾函數(shù)的返回值以const修飾函數(shù)的返回值表示函數(shù)的返回值不能被修改例如函數(shù)constchar*GetUserName(void);如下語句將出現(xiàn)編譯錯誤:char*strUserName=GetUserName();正確的用法是constchar*strUserName=GetUserName()不要把函數(shù)intGetUserNum(void)改成constintGetUserNum(void)3.2.6類中const的用法類的const數(shù)據(jù)成員的初始化只能在類構(gòu)造函數(shù)的初始化表中進行。例如:classA{…A(intnSize);//構(gòu)造函數(shù)constintSIZE;};A::A(intnSize):SIZE(nSize)//構(gòu)造函數(shù)的初始化表{???}Aa(100);//對象a的SIZE值為100Ab(200);//對象b的SIZE值為200在類中const不僅可用來修飾函數(shù)的返回值和函數(shù)的輸入?yún)?shù)。還可以用于修飾整個函數(shù),表示這個函數(shù)不能對類的任何成員(包括基類成員,但不包括靜態(tài)成員)進行修改,const修飾符應(yīng)置于函數(shù)尾部。例如:classstring{public://用于非const對象的operator[]char&operator[](intnPosition){returndata[nPosition];}//用于const對象的operator[]constchar&operator[](intnPosition)const{returndata[nPosition];}private:char*data;};如上例所示,第二個operator[]函數(shù)不能對類的任何成員(如data指針)進行修改,并且函數(shù)返回值也是常量,不能被修改。類成員函數(shù)僅在const方面有不同的也可以重載,如上面string類中operator[]函數(shù)。指針與引用的區(qū)別指針與引用看上去完全不同(指針用操作符'*'和'->',引用使用操作符'.'),但是它們似乎有相同的功能。指針與引用都是讓你間接引用其他對象。它們有什么區(qū)別呢?引用是被引用物的別名,對引用的任何操作就是對被引用物的操作?!耙脗鬟f”的性質(zhì)象“指針傳遞”,而書寫方式象“值傳遞”。引用被創(chuàng)建的同時必須被初始化(指針則可以在任何時候被初始化)。要認識到在任何情況下都不能用指向空值的引用。一個引用必須總是指向某些對象。因此如果你使用一個變量并讓它指向一個對象,但是該變量在某些時候也可能不指向任何對象,這時你應(yīng)該把變量聲明為指針,因為這樣你可以賦空值給該變量。相反,如果變量肯定指向一個對象,例如你的設(shè)計不允許變量為空,這時你就可以把變量聲明為引用。string&rstr;//錯誤,引用必須被初始化stringstrTmp("Test");string&rstr=strTmp;//正確,rstr指向strTmp指針沒有這樣的限制。string*pstrTmp;//未初始化的指針,合法但危險我們要避免讓引用指向空值。例如:char*pNull=0;//設(shè)置指針為空值char&rRefer=*pNull;//讓引用指向空值不存在指向空值的引用這個事實意味著使用引用的代碼效率比使用指針的要高。因為在使用引用之前不需要測試它的合法性。voidPrintDouble(constdouble&dfNumber){cout<<dfNumber;//不需要測試dfNumber,它肯定指向一個double值}相反,指針則應(yīng)該總是被測試,防止其為空:voidprintDouble(constdouble*pdfNumber){if(pdfNumber)//檢查是否為NULL{cout<<*dfNumber;}}引用一旦被初始化,就不能改變引用的關(guān)系,指針則可以隨時改變所指的對象。strings1("C++");strings2("C#");string&rs=s1;//rs引用s1string*ps=&s1;//ps指向s1rs=s2;//rs仍舊引用s1,但是si的值現(xiàn)在是"C#"ps=&s2;//ps現(xiàn)在指向s2;s1沒有改變總的來說,在以下情況下你應(yīng)該使用指針,一是你考慮到存在不指向任何對象的可能(在這種情況下,你能夠設(shè)置指針為空),二是你需要能夠在不同的時刻指向不同的對象(在這種情況下,你能改變指針的指向)。如果總是指向一個對象并且一旦指向一個對象后就不會改變指向,那么你應(yīng)該使用引用。還有一種情況,就是當你重載某個操作符時,你應(yīng)該使用引用。最普通的例子是操作符[]。這個操作符典型的用法是返回一個目標對象,其能被賦值。例如:strings1="hello";s1[0]=‘M';當你知道你必須指向一個對象并且不想改變其指向時,或者在重載操作符并為防止不必要的語義誤解時,你不應(yīng)該使用指針。而在除此之外的其他情況下,則應(yīng)使用指針。C++函數(shù)的高級特性相對于C語言的函數(shù),C++增加了重載(overloaded)、內(nèi)聯(lián)(inline)、友元(friend)、const和virtual五種新機制。其中重載和內(nèi)聯(lián)機制既可用于全局函數(shù)也可用于類的成員函數(shù),friend、const與virtual機制僅用于類的成員函數(shù)。3.4.1函數(shù)重載函數(shù)重載的概念如果兩個函數(shù)的名字相同,并且在相同的域中被聲明,但是參數(shù)表不同,那它們就是重載函數(shù)(overloadedfunction)。函數(shù)重載允許多個函數(shù)共享同一函數(shù)名。但針對不同參數(shù)類型提供共同的操作。這樣便于記憶,提高了函數(shù)的易用性。例如://constructsemptyCStringstring();//copyconstructorstring(conststring&stringSrc);//fromasinglecharacterstring(TCHARch,intnRepeat=1);//fromanANSIstring(convertstoTCHAR)string(LPCSTRlpsz);//fromaUNICODEstring(convertstoTCHAR)string(LPCWSTRlpsz);//subsetofcharactersfromanANSIstring(convertstoTCHAR)string(LPCSTRlpch,intnLength);//subsetofcharactersfromaUNICODEstring(convertstoTCHAR)string(LPCWSTRlpch,intnLength);//fromunsignedcharactersstring(constunsignedchar*psz);函數(shù)重載的實現(xiàn)在C++中兩個同名函數(shù)只要它們的參數(shù)表是唯一的,或者是參數(shù)的個數(shù)不同,或者是參數(shù)的類型不同,它們就是重載函數(shù)(注意:只有函數(shù)返回值不同是不行的)。如果同名函數(shù)的參數(shù)不同(包括個數(shù)、類型、順序不同),那么它們是不同的重載函數(shù)。例如:參數(shù)個數(shù)不同string();string(conststring&stringSrc);參數(shù)類型不同string(LPCSTRlpsz);string(LPCWSTRlpsz);參數(shù)類型不同string(LPCSTRlpch,intnLength);string(intnCount,LPCSTRlpch);如果兩個同名函數(shù)的參數(shù)表和返回值相同只是參數(shù)名不同,編譯器視它們?yōu)橄嗤暮瘮?shù)。例如:string(conststring&strSrc);string(conststring&strSource);如果兩個同名函數(shù)的參數(shù)表相同,而返回值不同,那第二個函數(shù)將視為第一個的重復(fù)聲明,編譯時會出錯。例如:intmax(intm,intn);unsignedintmax(intm,intn);如果兩個同名函數(shù)的參數(shù)表中只有缺省實參不同,那第二個函數(shù)將視為第一個的重復(fù)聲明例如:intmax(intm,intn);intmax(intm,intn=100);函數(shù)重載時確保函數(shù)的所有版本都有共同的目的和相似的行為。如果不同的函數(shù)名所提供的信息使程序更好理解或它們并不共享同樣的操作,這些函數(shù)就沒有必要使用重載。避免函數(shù)重載在指針和整形類型上。例如:voidprint(intn);voidprint(char函數(shù)重載與函數(shù)覆蓋成員函數(shù)被重載的特征:函數(shù)重載與函數(shù)覆蓋成員函數(shù)被重載的特征:相同的范圍(在同一個類中);函數(shù)名字相同;參數(shù)不同;virtual關(guān)鍵字可有可無。盡量避免重載在模板上。全局函數(shù)和類的成員函數(shù)同名不算重載,因為函數(shù)的作用域不同。例如:voidDisplay(…);//全局函數(shù)classWindow{…voudDisplay(…);//成員函數(shù)}不論兩個Display函數(shù)的參數(shù)是否不同,如果類的某個成員函數(shù)要調(diào)用全局函數(shù)Display,為了與成員函數(shù)Display區(qū)別,全局函數(shù)被調(diào)用時應(yīng)加'::'標志。例如::Display(…);//表示Display是全局函數(shù)而非成員函數(shù)。成員函數(shù)的重載、覆蓋與隱藏覆蓋是指派生類函數(shù)覆蓋基類函數(shù),特征是:(1)不同的范圍(分別位于派生類與基類);(2)函數(shù)名字相同;(3)參數(shù)相同;(4)基類函數(shù)必須有virtual關(guān)鍵字。函數(shù)隱藏在這里“隱藏”是指派生類的函數(shù)屏蔽了與其同名的基類函數(shù),規(guī)則如下:(1)如果派生類的函數(shù)與基類的函數(shù)同名,但是參數(shù)不同。此時,不論有無virtual關(guān)鍵字,基類的函數(shù)將被隱藏(注意別與重載混淆)。(2)如果派生類的函數(shù)與基類的函數(shù)同名,并且參數(shù)也相同,但是基類函數(shù)沒有virtual關(guān)鍵字。此時,基類的函數(shù)被隱藏(注意別與覆蓋混淆)。運算符重載在C++語言中,可以用關(guān)鍵字operator加上運算符來表示函數(shù),叫做運算符重載。例如兩個字符串相加函數(shù):stringAdd(conststring&a,conststring&b);可以用運算符重載來表示:stringoperator+(conststring&a,conststring&b);運算符與普通函數(shù)在調(diào)用時的不同之處是:對于普通函數(shù),參數(shù)出現(xiàn)在圓括號內(nèi);而對于運算符,參數(shù)出現(xiàn)在其左、右側(cè)。例如stringa,b,c;???c=Add(a,b);//用普通函數(shù)c=a+b;//用運算符+如果運算符被重載為全局函數(shù),那么只有一個參數(shù)的運算符叫做一元運算符,有兩個參數(shù)的運算符叫做二元運算符。如果運算符被重載為類的成員函數(shù),那么一元運算符沒有參數(shù),二元運算符只有一個右側(cè)參數(shù),因為對象自己成了左側(cè)參數(shù)。從語法上講,運算符既可以定義為全局函數(shù),也可以定義為成員函數(shù)。文獻[Murray,p44-p47]對此問題作了較多的闡述,并總結(jié)了表3-2的規(guī)則。運算符規(guī)則所有的一兀運算符建議重載為成員函數(shù)=()[]->只能重載為成員函數(shù)+=-=/=*=&二1二~=%=>>=<<=建議重載為成員函數(shù)所有其它運算符建議重載為全局函數(shù)表3-2運算符的重載規(guī)則由于C++語言支持函數(shù)重載,才能將運算符當成函數(shù)來用,C語言就不行。對于運算符重載:1)不要過分擔(dān)心自己不會用,它的本質(zhì)仍然是程序員們熟悉的函數(shù)。2)不要過分熱心地使用,如果它不能使代碼變得更加易讀易寫,那就別用,否則會自找麻煩。?不能被重載的運算符在C++運算符集合中,有一些運算符是不允許被重載的。這種限制是出于安全方面的考慮,可防止錯誤和混亂。不能改變C++內(nèi)部數(shù)據(jù)類型(如int,float等)的運算符。不能重載‘.',因為‘.'在類中對任何成員都有意義,已經(jīng)成為標準用法。不能重載目前C++運算符集合中沒有的符號,如#,@,$等。原因有兩點,一是難以理解,二是難以確定優(yōu)先級。對已經(jīng)存在的運算符進行重載時,不能改變優(yōu)先級規(guī)則,否則將引起混亂。3.4.2函數(shù)參數(shù)的缺省值有一些參數(shù)的值在每次函數(shù)調(diào)用時都相同,書寫這樣的語句會使人厭煩。C++語言采用參數(shù)的缺省值使書寫變得簡潔(在編譯時,缺省值由編譯器自動插入)。參數(shù)缺省值的使用規(guī)則:參數(shù)缺省值只能出現(xiàn)在函數(shù)的聲明中,而不能出現(xiàn)在定義體中。例如:voidAdd(intm=0,intn=0);//正確,缺省值出現(xiàn)在函數(shù)的聲明中voidAdd(intm=0,intn=0)//錯誤,缺省值出現(xiàn)在函數(shù)的定義體中{???}如果函數(shù)有多個參數(shù),參數(shù)只能從后向前挨個兒缺省,否則將導(dǎo)致函數(shù)調(diào)用語句怪模怪樣。正確的示例如下:voidFunction(intm,intn=0,intk=0);錯誤的示例如下:voidFunction(intm=0,intn,intk=0);要注意,使用參數(shù)的缺省值并沒有賦予函數(shù)新的功能,僅僅是使書寫變得簡潔一些。它可能會提高函數(shù)的易用性,但是也可能會降低函數(shù)的可理解性。所以我們只能適當?shù)厥褂脜?shù)的缺省值,要防止使用不當產(chǎn)生負面效果。示例3-3中,不合理地使用參數(shù)的缺省值將導(dǎo)致重載函數(shù)output產(chǎn)生二義性。#include<iostream.h〉voidPrint(intm);voidPrint(intm,floata=0.0);voidPrint(intm){cout〈〈"outputint"〈〈m〈〈endlvoidPrint(intm,floata){cout<<"outputint"<<m<<"andfloat"<<a<<endl}voidmain(void)jintm=1;floata=0.5;//Print(m);//error!ambiguouscall}Print(m,a);//outputint1andfloat0.5示例3-3參數(shù)的缺省值將導(dǎo)致重載函數(shù)產(chǎn)生二義性3.4.3內(nèi)聯(lián)函數(shù)用內(nèi)聯(lián)取代宏代碼C++語言支持函數(shù)內(nèi)聯(lián),其目的是為了提高函數(shù)的執(zhí)行效率(速度)。在C程序中,可以用宏代碼提高執(zhí)行效率。宏代碼本身不是函數(shù),但使用起來象函數(shù)。預(yù)處理器用復(fù)制宏代碼的方式代替函數(shù)調(diào)用,省去了參數(shù)壓棧、生成匯編語言的CALL調(diào)用、返回參數(shù)、執(zhí)行return等過程,從而提高了速度。使用宏代碼最大的缺點是容易出錯,預(yù)處理器在復(fù)制宏代碼時常常產(chǎn)生意想不到的邊際效應(yīng)。例如#definemax(a,b)(a)>(b)?(a):(b)語句result=max(i,j)+2;將被預(yù)處理器解釋為result=(i)>(j)?(i):(j)+2;由于運算符‘+'比運算符‘:'的優(yōu)先級高,所以上述語句并不等價于期望的result=((i)>(j)?(i):(j))+2;如果把宏代碼改寫為#definemax(a,b)((a)>(b)?(a):(b))則可以解決由優(yōu)先級引起的錯誤。但是即使使用修改后的宏代碼也不是萬無一失的,例如語句result=max(i++,j);將被預(yù)處理器解釋為result=(i++)>(j)?(i++):(j);對于C++而言,使用宏代碼還有另一種缺點:無法操作類的私有數(shù)據(jù)成員。讓我們看看C++的“函數(shù)內(nèi)聯(lián)”是如何工作的。對于任何內(nèi)聯(lián)函數(shù),編譯器在符號表里放入函數(shù)的聲明(包括名字、參數(shù)類型、返回值類型)。如果編譯器沒有發(fā)現(xiàn)內(nèi)聯(lián)函數(shù)存在錯誤,那么該函數(shù)的代碼也被放入符號表里。在調(diào)用一個內(nèi)聯(lián)函數(shù)時,編譯器首先檢查調(diào)用是否正確(進行類型安全檢查,或者進行自動類型轉(zhuǎn)換,當然對所有的函數(shù)都一樣)。如果正確,內(nèi)聯(lián)函數(shù)的代碼就會直接替換函數(shù)調(diào)用,于是省去了函數(shù)調(diào)用的開銷。這個過程與預(yù)處理有顯著的不同,因為預(yù)處理器不能進行類型安全檢查,或者進行自動類型轉(zhuǎn)換。假如內(nèi)聯(lián)函數(shù)是成員函數(shù),對象的地址(this)會被放在合適的地方,這也是預(yù)處理器辦不到的。C++語言的函數(shù)內(nèi)聯(lián)機制既具備宏代碼的效率,又增加了安全性,而且可以自由操作類的數(shù)據(jù)成員。所以在C++程序中,應(yīng)該用內(nèi)聯(lián)函數(shù)取代所有宏代碼,“斷言assert”恐怕是唯一的例外。assert是僅在Debug版本起作用的宏,它用于檢查“不應(yīng)該”發(fā)生的情況。為了不在程序的Debug版本和Release版本引起差別‘a(chǎn)ssert不應(yīng)該產(chǎn)生任何副作用。如果assert是函數(shù),由于函數(shù)調(diào)用會引起內(nèi)存、代碼的變動,那么將導(dǎo)致Debug版本與Release版本存在差異。所以assert不是函數(shù),而是宏。(參見6.5節(jié)“使用斷言”)內(nèi)聯(lián)函數(shù)的編程風(fēng)格關(guān)鍵字inline必須與函數(shù)定義體放在一起才能使函數(shù)成為內(nèi)聯(lián),僅將inline放在函數(shù)聲明前面不起任何作用。如下風(fēng)格的函數(shù)Add不能成為內(nèi)聯(lián)函數(shù):inlineintAdd(intm,intn);//inline僅與函數(shù)聲明放在一起intAdd(intm,intn){???}而如下風(fēng)格的函數(shù)Add則成為內(nèi)聯(lián)函數(shù):intAdd(intm,intn);intAdd(intm,intn)//inline與函數(shù)定義體放在一起{???}所以說,inline是一種“用于實現(xiàn)的關(guān)鍵字”,而不是一種“用于聲明的關(guān)鍵字”一般地,用戶可以閱讀函數(shù)的聲明,但是看不到函數(shù)的定義。盡管在大多數(shù)教科書中內(nèi)聯(lián)函數(shù)的聲明、定義體前面都加了inline關(guān)鍵字,但我認為inline不應(yīng)該出現(xiàn)在函數(shù)的聲明中。這個細節(jié)雖然不會影響函數(shù)的功能,但是體現(xiàn)了高質(zhì)量C++/C程序設(shè)計風(fēng)格的一個基本原則:聲明與定義不可混為一談,用戶沒有必要、也不應(yīng)該知道函數(shù)是否需要內(nèi)聯(lián)。定義在類聲明之中的成員函數(shù)將自動地成為內(nèi)聯(lián)函數(shù),例如classA{public:intAdd(intm,intm){…}//自動地成為內(nèi)聯(lián)函數(shù)}將成員函數(shù)的定義體放在類聲明之中雖然能帶來書寫上的方便,但不是一種良好的編程風(fēng)格,上例應(yīng)該改成://頭文件classA{public:intAdd(intm,intm);}//定義文件inlineintA::Add(intm,intm){???}慎用內(nèi)聯(lián)內(nèi)聯(lián)能提高函數(shù)的執(zhí)行效率
溫馨提示
- 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)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 工業(yè)品買賣合同書
- 康雙的離婚協(xié)議書
- 三農(nóng)村生態(tài)建設(shè)實施指南
- 工程監(jiān)理承包合同
- 云計算在企業(yè)IT架構(gòu)中應(yīng)用教程
- 運動訓(xùn)練方法與技巧指南
- 軟件測試流程與質(zhì)量保障作業(yè)指導(dǎo)書
- 臨設(shè)工程勞務(wù)分包合同
- 網(wǎng)絡(luò)安全威脅防范與應(yīng)對作業(yè)指導(dǎo)書
- 鋼渣購銷合同
- Starter Unit 1 Hello!說課稿2024-2025學(xué)年人教版英語七年級上冊
- 2025年初中語文:春晚觀后感三篇
- Unit 7 第3課時 Section A (Grammar Focus -4c)(導(dǎo)學(xué)案)-【上好課】2022-2023學(xué)年八年級英語下冊同步備課系列(人教新目標Go For It!)
- 《教育強國建設(shè)規(guī)劃綱要(2024-2035年)》解讀講座
- 《基于新課程標準的初中數(shù)學(xué)課堂教學(xué)評價研究》
- 省級產(chǎn)業(yè)園區(qū)基礎(chǔ)設(shè)施項目可行性研究報告
- 預(yù)算績效評價管理機構(gòu)入圍投標文件(技術(shù)方案)
- 2019北師大版高中英語選擇性必修四單詞表
- 園藝產(chǎn)品的品質(zhì)講義
- 鋼筋混凝土框架結(jié)構(gòu)工程監(jiān)理的質(zhì)量控制
- 桃花節(jié)活動方案
評論
0/150
提交評論