版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
if第5const#define有了 為什么還要new/delete new/delete附錄A:C++/C代碼表附錄B:C++/C試題賞。就象在武俠小說中,那些獨來獨往、不受約束且?guī)c邪氣的高手最令人。我曾經(jīng)也這樣信奉,這些軟件頻頻獲獎,有一個軟件獲得首屆學(xué)生電腦大賽軟件展示一等獎。在1995年開發(fā)的一套圖位真正的軟件高手請教。他雖然從未涉足過3D圖形領(lǐng)域,卻在幾十分鐘內(nèi)該軟件多處重大設(shè)計錯誤。讓人感覺那套軟件是用紙糊的華麗衣服,扯一下掉一塊,戳一下破個洞。我目瞪口呆地這套設(shè)計的基礎(chǔ)知識。補修“內(nèi)功”之后,又覺得腰板硬了起來。博士畢業(yè)前半年,我曾到微軟中國找在大學(xué)里從來沒有人如此嚴格地考查過我的程序。我化了半個小時,修改了數(shù)次,他還不盡滿意,讓我回家好好琢磨。我精神抖擻地進“考場”,大汗淋漓地出“考場”。這“高手”當(dāng)?shù)靡蔡C囊了。我又好好地反省了一次。型IT企業(yè)如、貝爾、中興等公司的們廣泛交流。大家認為提高質(zhì)量與生產(chǎn)率是軟件工程要現(xiàn)在國內(nèi)IT企業(yè)擁有學(xué)士、、博士文憑的軟件開發(fā)人員比比皆是,但他們在接受大學(xué)教育時就“且能在實踐中運用自如??!案哔|(zhì)量”可不是干活點就能實現(xiàn)的!事實證明如此。我到貝爾工作一年來,陸續(xù)面試或測試過近百名“新”“老”程序員的編程技能,質(zhì)量大約是10%。很少有人能夠?qū)懗鐾耆腺|(zhì)量要求的if語句,很多程序員對指針、內(nèi)存管理們不敢相信這是真的。我做過現(xiàn)場試驗:有一次部門新進14名生,在開歡迎會之前對他們進行數(shù)人得。競爭對手公司的朋友們也做過試驗,同樣一敗涂地。要知道、貝爾、中興等公司的員工素質(zhì)在國內(nèi)IT企業(yè)中是比較前列的,倘若他們的編程質(zhì)量都后中趕超發(fā)達國家嗎?只要你能下決心改掉不良的編程習(xí)慣,第二次考試就能及格了。適的工作單位,不妨到貝爾試一試。果制定了大家認可的編程風(fēng)格,那么所有組員都要遵守。如果讀者覺得本書的編程風(fēng)格比較合你的工作,那么就采用它,不要只看不做。人在小時候說話發(fā)音,寫字潦草,如果不改正,總有后悔的時候。第七章至第十一章是專題論述,技術(shù)難度比較高,看書時要積極思考。特別是第七章“內(nèi)存管理”,讀了并不表示懂了,懂了并不表示就能正確使用。有一位同事看了第七章后覺得“野指針”寫得不錯,與我切磋了一把??墒沁^了兩周,他告訴我,他忙了兩天追查出一個Bug,想不到又是“野指針”出問題,只按照CMMI規(guī)范做事,讓自己的綜合水平上升一個臺階。貝爾的員工可以向網(wǎng)絡(luò)應(yīng)用事業(yè)部軟件工 作者,不得或大量本書預(yù)計到2002年7月,建立切合中國國情的CMMI3級解決方案。屆時,包括本書在內(nèi)的約 Copyright(c)2001,貝爾網(wǎng)絡(luò)應(yīng)用事業(yè)Allrights 年 原作者:輸入原作者(或修改者) 年
和版本(參見示例1-1)假設(shè)頭文件名稱為【規(guī)則1-2-2】用#include<filename.h>格式來標(biāo)準(zhǔn)庫的頭文件(編譯器將從標(biāo)準(zhǔn)庫開 【規(guī)則1-2-3】用#include“filename.h”格式來非標(biāo)準(zhǔn)庫的頭文件(編譯器將從用戶的工作【建議1-2-1】頭文件中只存放“”而不存放“定義在C++語法中,類的成員函數(shù)可以在的同時被定義,并且自動成為內(nèi)聯(lián)函數(shù)。這雖然會帶來書寫上的方便,但卻造成了風(fēng)格不一致,弊大于利。建議將成員函數(shù)的定義與分開,不論該函數(shù)體有多么【建議1-2-2】不提倡使用全局變量,盡量不要在頭文件中出現(xiàn)象externintvalue這類 #ifndefGRAPHICS_H//防止graphics.h被重復(fù)#define#include //標(biāo)準(zhǔn)庫的頭文…#include“myheader.h”//非標(biāo)準(zhǔn)庫的頭文…voidFunction1(…);//…class 類結(jié)構(gòu){… 和版本(參見示例1-1)對一些頭文件的假設(shè)定義文件的名稱為 #include //頭文…void{…}void{…}
通過頭文件來調(diào)用庫功能。在很多場合,源代碼不便(或)向用戶公布,只要向用戶提供頭錄,以便于。 隔。參見示例2-1(b)void{…}void{…}void{…}while{if{}{}}示例2-1(a)函數(shù)之間的空 示例2-1(b)函數(shù)內(nèi)部的空多少都要加{}。這樣可以防止書寫。intwidth;//寬度intheight;//intdepth;//intwidth,height,depth;//x=a+y=c+z=e+X=a+b;y=c+d;z=e+if(width<{}if(width<height)for(initialization;condition;{}空行for(initialization;condition;update)示例2-2(a)風(fēng)格良好的代碼 示例2-2(b)風(fēng)格不良的代碼intwidth=10; intheight=10;//定義并初紿化heightintdepth 定義并初紿化【規(guī)則2-3-1】關(guān)鍵字之后要留空格。象const、virtual、inline、case等關(guān)鍵字之后至少要留一【規(guī)則2-3-4】‘,’之后要留空格,如Function(x,y,z)。如果‘;’不是一行的結(jié)束符號,其后要留空格,如for(initialization;condition;update)。如“=”、、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后應(yīng)當(dāng)加空格,如for(i=0;i<10i++)和if((a<=b)&&voidFunc1(intx,inty,intvoidFunc1(intx,inty,intif(year>=if((a>=b)&&for(i=0;i<10;i++)for(i=0;I<10;ix=a<b?a:int*x=int*x=&array[5]=0;array5ab【規(guī)則2-4-1】程序的分界符‘{’和‘}’應(yīng)獨占一行并且位于同一列,同時與它們的語句左對{}voidFunction(int{…//program}voidFunction(int…//program}ifif{…//program…//program}}else…//program{}…//program}for(initialization;condition;{…//program}for(initialization;condition;…//program}While{…//program}while…//program}{…{…}…}示例2-4(a)風(fēng)格良好的對 示例2-4(b)風(fēng)格不良的對if((very_longer_variable1>=&&(very_longer_variable3<=very_longer_variable14)&&(very_longer_variable5<={}virtualCMatrixCMultiplyMatrix(CMatrixCMatrixfor{}
示例2-5*靠近數(shù)據(jù)類型,例如:int*x;從語義上講此寫法比較直觀,即x是int上述寫法的弊端是容易引起誤解,例如:int*x,y;此處y容易被誤解為指針變量。雖然將x和y分行定義【規(guī)則2-6-1*charint*xy;//此處y i1程程返回值:voidFunction(floatx,floaty,float{…}.8if{…while{…}//endof…}//endof釋釋protected和private,分別用于哪些數(shù)據(jù)和函數(shù)是公有的、受保護的或者是私有的。這樣可以達到信封裝功能,它當(dāng)成火鍋,什么東西都往里扔。classclass{void… i,j;floatx,y;…}class{ i,j;floatx,y;void…}示例8.3(a)以數(shù)據(jù)為中心版 示例8.3(b)以行為為中心的版比較著名名規(guī)則當(dāng)推公司的“匈牙利”法,該命名規(guī)則的主要思想是“在變量和函數(shù)名中加入 i,j,k;floatx,y, iI,iJ,ik;//前綴i表示int類型floatfX,fY,fZ;//f表示float類型據(jù),沒有一種命名規(guī)則可以讓所有的程序員贊同,程序設(shè)計教科書一般都不指定命名規(guī)則。命名規(guī)則對軟件產(chǎn)品而言并不是“成敗悠關(guān)”的事,我們不要化太多精力試圖發(fā)明世界上最好名規(guī)則,而應(yīng)當(dāng)制定一種令大多數(shù)項目成員滿意名規(guī)則,并在項目中實施。 【規(guī)則3-1-2】標(biāo)識符的長度應(yīng)當(dāng)符合“min-length&&max-information”幾十年前老ANSIC規(guī)定名字超過6個字符,現(xiàn)今的C++/C不再有此限制。一般來說,長名字能更好地表達含義,所以函數(shù)名、變量名、類名長達十幾個字符不足為怪。那么名字是否越長約好?不見得!例如變量名maxval就比maxValueUntilOverflow好用。單字符的名字也是有用的,常見的如i,j,k,m,n,x,y,zintx, 變量xXvoidfoo(int 函數(shù)foo與FOOvoidFOO(floatfloatfloatoldValue;floatnewValue; classNode; //類名classLeafNode; //類名void //函數(shù)voidSetValue(intvalue);//BOOLintconstintMAX=constintMAX_LENGTH=void{staticint …}int intg_howMu //全局變voidObject::SetValue(intwidth,int{m_width=width;m_height=height;}C++/C語言的運算符有數(shù)十個,運算符的優(yōu)先級與結(jié)合律如表4-1+-*的優(yōu)([->!~++--(類型)+-**/+<<<>==&^|=+=-=*=/=%=&==<<=word=(high<<8)|if((a|b)&&(a&abc0這樣的表達式稱為復(fù)合表達式。允許復(fù)合表達式存在的理由是:(1)iab&&cd&&cfgh;//d=(a=b+c)+ra=b+d=a+if(a<b<c) abc是數(shù)學(xué)表達式而不是程序表達式if((a<b)&&if((a<b)<cif竟是什么并沒有統(tǒng)一的標(biāo)準(zhǔn)。例如VisualC++將TRUE定義為1,而VisualBasic則將TRUE定義為-1。if 表示flagif 表示flagif(flag==if(flag==1if(flag==FALSE)if(flag==0)if(value==if(value!=if(value) value是布爾變量if(!value)if(x==0.0) if((x>=-EPSINON)&&if(p==NULL) p與NULL顯式比較,強調(diào)p是指針變量if(p!=NULL)if(p0)//容易讓人誤解pif(p!=if //ifif(NULLp)ifNULL)if(pNULL),而有意把p和NULLifpNULL)if(NULL=p)是錯誤的,因為NULL不能被賦值。ifreturny;if{return}{return}return(condition?x:forfor(col=0;col<5;col++{for(row=0;row<100;{sum=sum+}}for(row=0;row<100;{for(col=0;col<5;col++{sum=sum+}}示例4-4(a)低效率:長循環(huán)在最外 示例4-4(b)高效率:長循環(huán)在最內(nèi)【建議2】如果循環(huán)體內(nèi)存在邏輯判斷,并且循環(huán)次數(shù)很大,宜將邏輯判斷移到循環(huán)體的外面。示例)的程序比示例)多執(zhí)行了1次邏輯判斷。并且由于前者老要進行邏輯判斷,打斷了循環(huán)“流水線”作業(yè),使得編譯器不能對循環(huán)進行優(yōu)化處理,降低了效率。如果N非常大,最好采用示例)的寫法,可以提高效率。如果N非常小,兩者效率差別并不明顯,采用示例)for(i=0;i<N;{if(condition)}if{for(i=0;i<N;i++)}{for(i=0;i<N;i++)}表4-4(c)效率低但程序簡 表4-4(d)效率高但程序不簡示例4-5(a)中的x值屬于半開半閉區(qū)間“0xN”,起點到終點的間隔為N,循環(huán)次數(shù)為N。示例4-5(b)中的x值屬于閉區(qū)間“0=<x<=N-1”,起點到終點的間隔為N-1,循環(huán)次數(shù)為N。for(intx=0;x<N;for(intx=0;x<=N-1;{{……}}示例4-5(a)循環(huán)變量屬于半開半閉區(qū) 示例4-5(b)循環(huán)變量屬于閉區(qū)switch(variable){casevalue1:…casevalue2:……default: }【規(guī)則4-6-2】記最后那個default分支。即使程序真的不需要default處理,也應(yīng)該保留語 default:break;這樣做并非多此一舉,而是為了防止別人誤以為你忘了default處理。自從提倡結(jié)構(gòu)化設(shè)計以來,goto就成了有爭議的語句。首先,由于goto語句可以靈活跳轉(zhuǎn),如果不加限制,它的確會破壞結(jié)構(gòu)化設(shè)計風(fēng)格。其次,goto語句經(jīng)常帶來錯誤或隱患。它可能跳過了某些對象的gotoStrings1,s2;被gotointsum=0;//被goto……很多人建議C++/C的goto語句,以絕后患。但實事求是地說,錯誤是程序員自己造成的,不是goto的過錯。goto語句至少有一處可顯神通,它能從多重循環(huán)體中咻地一下子跳到外面,用不著寫很多次的break語句;例如{{{goto}}}…語言用語言除了【規(guī)則5-1-1 MAX /*C語言的宏常量const MAX //C++語言的constconst PI //C++語言的constconst#defineconstfloatRADIUS=constfloatDIAMETER=RADIUS*classconstintSIZE= //錯誤,企圖在類中初始化const數(shù)據(jù)成int 錯誤,未知的classAA(int constintSIZEA::A(intsize {…}Aa(100);對象a的SIZE值為100Ab(200);b的SIZE值為classenumSIZE1100,SIZE2200};intint(passbyvalue)和指針傳遞(passbypointer)。C++語言中多了傳遞(passbyreference)。由于voidSetValue(intwidth,intheight);//voidSetValue(int, float float voidStringCopy(char*str1,char voidStringCopy(char*strSource,charcharStringCopy(str,“oWorld”);//參數(shù)順序顛voidStringCopy(char*strDestination,constcharintprintf(constchat*format[,argument]…);亂,規(guī)定任何C++/C函數(shù)都必須有類型。如果函數(shù)沒有返回值,那么應(yīng)為void類型。charc=getchar();if(c==…int失敗,這種“”人們一般哪里料得到!導(dǎo)致本例錯誤的責(zé)任并不在用戶,是函數(shù)getchar誤導(dǎo)了使用函數(shù)getcharBOOLGetChar(char靈活,例如());char*strcpy(char*strDest,constcharcharintlength=strlen(strcpy(str,“oWorld”)效率。而有些場合只能用“值傳遞”而不能用“傳遞”,否則會出錯。classString&operate=(constString//相加函數(shù),如果沒有friend修飾則只許有一個右側(cè)參數(shù) Stringoperate+(constString&s1,constString&s2);char}String的賦值函數(shù)operateString&String::operate=(constString{if(this==&other)return*this;deletem_data=newchar[strlen(other.data)+1];strcpy(m_data,other.data);return //返回的是*this的,無需拷貝過}String…a 如果用“值傳遞”*thisabc;//如果用“值傳遞”*thisString的相加函數(shù)operateStringoperate+(constString&s1,constString{Stringdeletetemp.data; //temp.data是僅含‘\0’的字符串temp.datanewchar[strlen(s1.datastrlen(s2.data)+1];strcpy(temp.data,s1.data);returntemp;}指向局部對象temp的“”。由于temp在函數(shù)結(jié)束時被自動銷毀,將導(dǎo)致返回的“”無效。例如:c=a+ab并不返回期望值,c們可以在函數(shù)體的“處”和“出口處”從嚴把關(guān),從而提高函數(shù)的質(zhì)量。char*{charstr[]=“o …return }要搞清楚返回的究竟是“值”、“指針”還是“”returnString(s1+Stringtemp(s1+returnreturnint(xy);inttemp=x+return帶有“”功能的函數(shù),其行為可能是不可預(yù)測的,因為它的行為可能取決于某種“狀態(tài)”。這樣的函數(shù)既不易理解又不利于測試和。在C/C++語言中,函數(shù)的static局部變量是函數(shù)的“”器。斷言assert是僅在Debug版本起作用的宏,它用于檢查“不應(yīng)該”發(fā)生的情況。示例6-5是一個內(nèi)存函數(shù)。在運行過程中,如果assert的參數(shù)為假,那么程序就會中止(一般地還會出現(xiàn)提示,說明在什么地方了assert)。void*memcpy(void*pvTo,constvoid*pvFrom,size_t{assert((pvToNULL&&pvFrom byte*pbTo(byte*) 防止改變pvTobyte*pbFrombyte*)pvFrom;防止改變pvFromwhile(size-->0*pbTo++=*pbFrom++;returnpvTo;}示例6-5不的內(nèi)存DbuRelae版本引起差別,ser不應(yīng)該產(chǎn)生ser害測試。如程序assr處終了,并是說含該assr出t序員忽略,甚至被刪除。[Maguire,p8-p30]【規(guī)則6-5-2】在函數(shù)的處,使用斷言檢查參數(shù)的有效性()是C++中的概念,初學(xué)者容易把和指針一起。一下程序中,n是m的一(reference),m是被物(referent)intint&n=n相當(dāng)于m的別名(),對n的任何操作就是對m的操作。例人名叫王小毛,他的是“三(1)被創(chuàng)建的同時必須被初始化(指針則可以在任何時候被初始化)不能有NULL,必須與合法的單元關(guān)聯(lián)(指針則可以是NULL)一旦被初始化,就不能改 。語句k=j并不能將k修改成為j的,只是把k的值改變成inti=intj=6;int&k=i;k k和i的值都變成了回值。C++語言中,函數(shù)的參數(shù)和返回值的傳遞方式有三種:值傳遞、指針傳遞和傳遞。voidFunc1(int{x=x+}…intn=cout<<“n=”<<n<< //n=voidFunc2(int{(*x)=(*x)+}…intn=0;cout<<“n=”<<n<< //n=以下是“傳遞”的示例程序。由于Func3函數(shù)體內(nèi)的x是外部變量n的,x和n是同一個東西,改voidFunc3(int{x=x+}…intn=0;cout<<“n=”<<n<< //n=對比上述三個示例程序,會發(fā)現(xiàn)“傳遞”的性質(zhì)象“指針傳遞”,而書寫方式象“值傳遞”。實際上“”可以做的任何事情“指針”也都能夠做,為什么還要“”這東西?如果的確只需要借用一下某個對象的“別名”,那么就用“”,而不要用“指針”,以免發(fā)生意外。比如說,需要一份證明,本來在文件上蓋上公章的印子就行了,如果把取公章的交給他,那么他就歡迎進入內(nèi)存這片雷區(qū)。偉大的BillGates640Koughttobeenoughfor—BillGates從靜態(tài)區(qū)域分配。內(nèi)存在程序編譯的時候就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間在棧上創(chuàng)建。在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些單元自動被釋放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量 或ew申請任意多少的內(nèi)存,程序員自己負責(zé)在何時用或delete釋放內(nèi)存。動態(tài)內(nèi)存的生存期由我們決定,使用非常靈活,但問題也最到。而這些錯誤大多沒有明顯的癥狀,時隱時現(xiàn),增加了改錯的難度。有時用戶怒氣沖沖地找來,編程新手常犯這種錯誤,因為他們沒有內(nèi)存分配會不成功。常用解決辦法是,在使用內(nèi)存之前檢查指針是否為NULL。如果指針p是函數(shù)的參數(shù),那么在函數(shù)的處用assert(p!=NULL)進行檢查。如果 或ew來申請內(nèi)存,應(yīng)該用if(p==NULL)或if(p!=NULL)進行防錯處理。函數(shù)的return語句寫錯了,注意不要返回指向“棧內(nèi)存”的“指針”或者“”,因為該內(nèi)存在函數(shù)體 或ew申請內(nèi)存之后,應(yīng)該立即檢查指針值是否為NULL。防止使用指 示例7-3-1中,字符數(shù)組a的容量是6個字符,其內(nèi)容為o\0。a的內(nèi)容可以改變,如a[0]=‘X’。指針p指向常量字符串“world”(位于靜態(tài)區(qū),內(nèi)容為world\0),常量字符串的內(nèi)容是不可以被修改的。從語法上看,編譯器并不覺得語句p[0]=‘X’有什么不妥,但是該語句企圖修改常量字符串的內(nèi)容而導(dǎo)致chara[]= a[0]=cout<<a<<char*p p[0] cout<<p<<內(nèi)容與比不能對數(shù)組名進行直接與比較。示例7-3-2中,若想把數(shù)組a的內(nèi)容給數(shù)組b,不能用語句b=a,否則將產(chǎn)生編譯錯誤。應(yīng)該用標(biāo)準(zhǔn)庫函數(shù)strcpy進行。同理,比較b和a的內(nèi)容是否相同,不能用if(b==a)來判斷,應(yīng)該用標(biāo)準(zhǔn)庫函數(shù)strcmp進行比較。語句p=a并不能把a的內(nèi)容指針p,而是把a的地址賦給了p。要想a的內(nèi)容,可以先用庫函數(shù)malloc為p申請一塊容量為strlen(a)+1個字符的內(nèi)存,再用strcpy進行字符串。同理,語句if(p==a)比數(shù)組chara[]="charstrcpy(b,a); //不能用b=a;if(strcmp(b,a0)//不能用if(ba)…指針intlen=char*pchar*)malloc(sizeof(char)*(len+1)); //不要用p=a;if(strcmp(p,a0)//if(p…示例7-3-2數(shù)組和指針的內(nèi)容與比組a的容量是多少,sizeof(a)始終等于sizeof(char*)。chara[]= ochar*p=coutsizeof(aendl;//12字節(jié)cout<<sizeof(p)<<endl;//4字節(jié)voidFunc(char{coutsizeof(aendl;//4字節(jié)而不是100}示例7-3-3(b)數(shù)組為指voidGetMemory(char*p,int{p=(char*)malloc(sizeof(char)*}void{char*str=GetMemory(str,100); strNULLstrcpy(str,"o");//運行錯誤}毛病出在函數(shù)Getemoy中。編譯器總是要為函數(shù)的每個參數(shù)制作臨時副本,指針參數(shù)p的副本是編譯器使_p=p。如果函數(shù)體內(nèi)的程序修改了p的內(nèi)容,就導(dǎo)致參數(shù)p的內(nèi)容作相應(yīng)的修改。這就是指針可以用作輸出參數(shù)的原因。在本例中,p申請了新的內(nèi)存,只是把p所指的內(nèi)存地址改變了,但是p絲毫未變。所以函數(shù)GetMeory并不能輸出任何東西。事實上,每執(zhí)行一次GetMeory就會一塊內(nèi)存,因沒有用釋放內(nèi)。voidGetMemory2(char**p,int{*p=(char*)malloc(sizeof(char)*}char*GetMemory3(int{char*p=(char*)malloc(sizeof(char)*num);returnp;}void{char*str=str=GetMemory3(100);strcpy(str,"o");cout<<str<<endl;}char{charp[]="oreturnp;//}void{char*str=strGetString();//strcout<<str<<}用調(diào)試器逐步Test4,發(fā)現(xiàn)執(zhí)行str=GetString語句后str不再是NULL指針,但是str的內(nèi)容不是“char{char*p="oworld";returnp;}void{char*str=NULL;str=GetString2();cout<<str<<} (pNULL)進行防錯處理。很遺憾,此時if語句起不到防錯作用,因為即便p不是NULL指針,它也不指char*p=(char*)strcpy(p,“ p所指的內(nèi)存被釋放,但是p…if(pNULL)//{strcpy(p,“world”);//}
示例7-5p針變量,它消亡的時候會讓它所指的動態(tài)內(nèi)存一起。這是錯覺!void{char*pchar*)malloc(100} char*p=char*str=(char*) class{voidFunc(void){cout<<“FuncofclassA”<<endl;void{A{Ap a} p是“野指針} 為什么還要new/delete對于非內(nèi)部數(shù)據(jù)類型的對象而言,光用maloc/動態(tài)對象的要求。對象在創(chuàng)建的同時要自執(zhí)行構(gòu)造函數(shù),對象在消亡之前要自動執(zhí)行析構(gòu)函數(shù)。由于malloc/ class{publicObj(void){cout<<“Initialization”<<endl;~Obj(void){cout<<“Destroy”<<endl; Initialize(void){cout<<“Initialization”<<endl;} Destroy(void){cout<<“Destroy”<<endl;}void {Obj*a(obj*)malloc(sizeof(obj));// a->Destroy();// }void{Obj*anewObj;//delete }類Obj的函數(shù)Initialize模擬了構(gòu)造函數(shù)的功能,函數(shù)Destroy模擬了析構(gòu)函數(shù)的功能。函數(shù) 和ew將返回NULL指針,內(nèi)存申請失void{A*a=newA;if(a=={}…}void{A*a=newA;if(a=={cout<<“MemoryExhausted”<<endl;}…}為new和malloc設(shè)置異常處理函數(shù)。例如VisualC++可以用_set_new_hander函數(shù)為new設(shè)置用戶自 與ew相同的異常處理函數(shù)。詳細內(nèi)容請參考C++使用手有一個很重要的現(xiàn)象要告訴大家。對于32位以上的應(yīng)用程序而言,無論怎樣使用與ew,幾乎不可能導(dǎo)致“內(nèi)存耗盡”。我在Windows98下用VisualC++編寫了測試程序,見示例7-9。這個程序會無休只聽到硬盤嘎吱嘎吱地響,Window98已經(jīng)累得對鍵盤、鼠標(biāo)毫無反應(yīng)。void{float*p=NULL;{p=new cout<<“eatmemory”<<endl;}}
void*malloc(size_tint*p=(int*)malloc(sizeof(int)*malloc返回值的類型是void*,所以在調(diào)用malloc時要顯式地進行類型轉(zhuǎn)換,將void*轉(zhuǎn)換成所住int,float等數(shù)據(jù)類型的變量的確切字節(jié)數(shù)。例如int變量在16位系統(tǒng)下是2個字節(jié),在32位下是4個cout<<sizeof(char)<<endl;cout<<sizeof(int)<<cout<<sizeof(unsignedint)<<endl;cout<<sizeof(long)<<endl;cout<<sizeof(unsignedlong)<<endl;cout<<sizeof(float)<<endl;cout<<sizeof(double)<<endl;cout<<sizeof(void*)<<endl;在malloc的“()”中使用sizeofpmalloc(sizeofvoid(void*memblock (p)能正確地釋放內(nèi)存。如果p是NULL指針,那么對p無論操作多少次都不會出問題。如果p不是NULL指針,那么對p連續(xù)操作兩次就會導(dǎo)致程序運行錯誤。int*p1=(int*)malloc(sizeof(int)*int*p2=new建動態(tài)對象的同時完成了初始化工作。如果對象有多個構(gòu)造函數(shù),那么new的語句也可以有多種形式。class{public Obj(int …}void{Obj*a=newObj*bnew 初值為…deletea;delete}Obj*objects=newObj[100]; Obj*objectsnewObj[100](1);//創(chuàng)建100個動態(tài)對象的同時賦初值1delete delete 我認識不少技術(shù)不錯的C++/C程序員,很少有人能拍拍胸脯說通曉指針與內(nèi)存管理(包括)。我最初學(xué)習(xí)C語言時特別怕指針,導(dǎo)致我開發(fā)第一個應(yīng)用軟件(約1萬行C代碼)時沒有使用一個指針,全自然語言中,一個詞可以有許多不同的含義,即該詞被重載了。人們可以通過上下文來判斷該詞到底是哪種含義?!霸~的重載”可以使語言更加簡練。例如“吃飯”的含義十分廣泛,人們沒有必要每次非得說在C++程序中,可以將語義、功能相似的幾個函數(shù)用同一個名字表示,即函數(shù)重載。這樣便于,提高了函數(shù)的易用性,這是C++語言采用重載機制的一個理由。例如示例8-1-1中的函數(shù)voidvoidvoidEat(Beef//可以改 voidEat(Fish//可以改 voidEat(Chicken示例8-1-1重載函數(shù)C++語言采用重載機制的另一個理由是:類的構(gòu)造函數(shù)需要重載機制。因為C++規(guī)定構(gòu)造函數(shù)與類同voidintFunctionintx=Functionvoidfoo(intx,intextern{voidfoo(intx,int}extern{#include其它C}開發(fā)商已經(jīng)對C標(biāo)準(zhǔn)庫的頭文件作了extern“C”處理,所以我們可以用#include直接這些頭文件void classvoid } //示例813中,第一個ouput函數(shù)的參數(shù)是int類型,第二個outut函數(shù)的參數(shù)是float類型。由于數(shù)字本身沒有類型,將數(shù)字當(dāng)作參數(shù)時將自動進行類型轉(zhuǎn)換(稱為隱式類型轉(zhuǎn)換)。語句outut(05)譯錯誤,因為編譯器不知道該將05轉(zhuǎn)換成int還是float類型的參數(shù)。隱式類型轉(zhuǎn)換在很多地方可以簡化#includevoidoutputint voidoutput(floatx);//voidoutput(int{cout<<"outputint"<<x<<endl}voidoutput(float{cout<<"outputfloat"<<x<<endl}void{intx=1;floaty=1.0; //outputint //outputfloat //outputint// error!ambiguouscalloutput(int(0.5));//outputintoutput(float(0.5));//outputfloat}#includeclass{voidf(intx){cout<<"Base::f(int)"<<x<<endl;}voidf(floatx){cout<<"Base::f(float)"<<x<<endl;}virtualvoidg(void){cout<<"Base::g(void)"<<classDerived:public{virtualvoidg(void){cout<<"Derived::g(void)"<<void{DerivedBase*pb= //Base::f(int) //Base::f(float) //}#includeclass{virtualvoidf(floatx){cout<<"Base::f(float)"<<x<<endl;}voidg(floatx){cout<<"Base::g(float)"<<x<<endl;}voidh(floatx){cout<<"Base::h(float)"<<x<<endl;classDerived:public{virtualvoidf(floatx){cout<<"Derived::f(float)"<<x<<endl;}voidg(intx){cout<<"Derived::g(int)"<<x<<endl;}voidh(floatx){cout<<"Derived::h(float)"<<x<<endl;voidvoid{DerivedBase*pb=Derived*pd=//Good:behaviordependssolelyontypeoftheobjectpb->f(3.14f);//Derived::f(float)3.14pd->f(3.14f);//Derived::f(float)//Bad:behaviordependsontypeofthepointerpb->g(3.14f);//Base::g(float)3.14pd->g(3.14f);//Derived::g(int) //Bad:behaviordependsontypeofthepointerpb->h(3.14f);//Base::h(float)3.14 pd->h(3.14f);//Derived::h(float)3.14}
隱藏規(guī)則引起了不少麻煩。示例8-2-3程序中,語句pd->f(10)的本意是想調(diào)用函數(shù)Base::f(int),但是Base::f(int)不幸被Derived::f(char*)隱藏了。由于數(shù)字10不能被隱式地轉(zhuǎn)化為字符串,所以在編譯時出class{voidf(intclassDerived:public{voidf(charvoid{Derived*pd=newDerived; //error}寫語句pd->f(10)的人可能真的想調(diào)用Derived::f(char*)函數(shù),只是他誤將參數(shù)寫錯了。有了隱藏規(guī)則,編譯器就可以明確錯誤,這未必不是好事。否則,編譯器會靜悄悄地將錯就錯,程序員classDerived:public{voidf(charvoidf(intx){Base::f(x);voidFoo(intx=0,int //正確,缺省值出現(xiàn)在函數(shù)的voidFoo(intx=0,int {…}為什么會這樣?是有兩個原因:一是函數(shù)的實現(xiàn)(定義)本來就與參數(shù)是否有缺省值無關(guān),所以沒有必要讓缺省值出現(xiàn)在函數(shù)的定義體中。二是參數(shù)的缺省值可能會改動,顯然修改函數(shù)的比修改函voidFoo(intx,inty=0,intvoidFoo(intx=0,inty,int當(dāng)產(chǎn)生效果。示例8-3-2中,不合理地使用參數(shù)的缺省值將導(dǎo)致重載函數(shù)output產(chǎn)生二義性。#includevoidoutput(intvoidoutput(intx,floatvoidoutput(int{cout<<"outputint"<<x<<endl}voidoutput(intx,float{cout<<"outputint"<<x<<"andfloat"<<y<<endl}void{intx=1;//output(x); //error!ambiguouscall //outputint1andfloat0.5} ComplexAdd(constComplex&a,constComplexComplexoperator+(constComplex&a,constComplexComplexa,b,…cAdd(a,b);ca 從語法上講,運算符既可以定義為全局函數(shù),也可以定義為成員函數(shù)。文獻[Murrayp44-p47]對此問=()[]-+=-=/=*=&=|=~=%=>>=C++語言支持函數(shù)內(nèi)聯(lián),其目的是為了提高函數(shù)的執(zhí)行效率(速度)在C程序中,可以用宏代碼提高執(zhí)行效率。宏代碼本身不是函數(shù),但使用起來象函數(shù)。預(yù)處理器用復(fù)制宏代碼的方式代替函數(shù)調(diào)用,省去了參數(shù)壓棧、生成匯編語言的ALL調(diào)用、返回參數(shù)、執(zhí)行return等過程,從而提高了速度。使用宏代碼最大的缺點是容易出錯,預(yù)處理器在宏代碼時常常產(chǎn)生意想不#defineMAX(a, (a)>(b)?(a):result=MAX(i,j)+2result=(i)>(j)?(i):(j)+2result=((i)>(j)?(i):(j))+2#defineMAX(a, ((a)>(b)?(a):(b)result=MAX(i++,result=(i++)>(j)?(i++):C++語言的函數(shù)內(nèi)聯(lián)機制既具備宏代碼的效率,又增加了安全性,而且可以自由操作類的數(shù)據(jù)成員。所以在C++程序中,應(yīng)該用內(nèi)聯(lián)函數(shù)取代所有宏代碼,“斷言assert”恐怕是唯一的例外。assertDebuginlinevoidFoo(intx,int //inline僅與函數(shù)放在一voidFoo(intx,int{…}voidFoo(intx,intinlinevoidFoo(intx,inty)//inline{…}函數(shù)的,但是看不到函數(shù)的定義。盡管在大多數(shù)教科書中內(nèi)聯(lián)函數(shù)的、定義體前面都加了 class{voidFoo(intx,inty) }class{voidFoo(intx,int}inlinevoidA::Foo(intx,int{…}內(nèi)聯(lián)是以代碼膨脹()為代價,僅僅省去了函數(shù)調(diào)用的開銷,從而提高函數(shù)的執(zhí)行效率。如果執(zhí)函數(shù)的調(diào)用都要代碼,將使程序的總代碼量增大,消耗的內(nèi)存空間。以下情況不宜使用內(nèi)聯(lián):C++語言中的重載、內(nèi)聯(lián)、缺省參數(shù)、隱式轉(zhuǎn)換等機制展現(xiàn)了很多優(yōu)點,但是這些優(yōu)點的背后都隱藏 A(constA A&operateconstA class{String(constchar*str String(constString ~ String&operateconstString 作為比C更先進的語言,C++提供了更好的機制來增強程序的安全性。C++編譯器具有嚴格的類型安全檢查功能,它幾乎能找出程序中所有的語法問題,這的確幫了程序員的大忙。但是程序通過了編譯檢查并不表示錯誤已經(jīng)不存在了,在“錯誤”的大家庭里,“語法錯誤”的地位只能算是小弟弟。級別高的錯工作很容易遺忘。Stroustrup在設(shè)計C++語言時充分考慮了這個問題并很好地予以解決:把對象的初構(gòu)造函數(shù)與析構(gòu)函數(shù)的名字不能隨便起,必須讓編譯器認得出才可以被自動執(zhí)行。Stroustrup名除了名字外,構(gòu)造函數(shù)與析構(gòu)函數(shù)的另一個特別之處是沒有返回值類型,這與返回值類型為void的函數(shù)不同。構(gòu)造函數(shù)與析構(gòu)函數(shù)的使命非常明確,就象出生與,光溜溜地來光溜溜地去。如果它們有返回值類型,那么編譯器將不知所措。為了防止節(jié)外生枝,干脆規(guī)定沒有返回值類型。(kel,p55p6])之后,卻在函數(shù)體{}之前。這說明該表里的初始化工作發(fā)生在函數(shù)體內(nèi)的任何代碼被執(zhí)行之前。classA(int AclassB:publicB(intx,inty);//BB::B(intx,int //{…}class A(constA A&operateconstA class{B(constA&a); A B::B(constB::B(constA{m_a=…}B::B(constA:{…}示例9-2(a)成員對象在初始化表中被初始 示例9-2(b)成員對象在函數(shù)體內(nèi)被初始class{F(intx,int intm_x,m_y;intm_i,m_j;}F::F(intF::F(intx,int{m_x=x;m_y=y;m_i=0;m_j=}F::F(intx,int:m_x(x),{m_i=m_j=}示例9-2(c)數(shù)據(jù)成員在初始化表中被初始 示例9-2(d)數(shù)據(jù)成員在函數(shù)體內(nèi)被初始[Eckel,p260-261]//String的普通構(gòu)造函數(shù)String::String(constchar*str){{m_data=new*m_data=}{intlength=strlen(str);m_data=newchar[length+1];strcpy(m_data,str);}}//String的析構(gòu)函數(shù){delete[]由于m_datadelete}現(xiàn)將a賦給b,缺省賦值函數(shù)的“位拷貝”意味著執(zhí)行b.m_data=a.m_data。這將造成三個錯誤:一是b.m_data原有的內(nèi)存沒被釋放,造成內(nèi)存;二是b.m_data和a.m_data指向同一塊內(nèi)存,a或b任何一Stringa(“StringStringc=a; c(a);c=b;//調(diào)用了賦值函數(shù))String::String(constString{允許操作other的私有成員m_dataintlength=strlen(other.m_data);m_data=newchar[length+1];strcpy(m_data,other.m_data);}String&String::operate=(constString{//(1)檢查自賦值if(this&other)return*this;(2)delete[]//(3)分配新的內(nèi)存資源,并內(nèi)容intlength=strlen(other.m_data);m_data=newchar[length+1];strcpy(m_data,return}類String拷貝構(gòu)造函數(shù)與普通構(gòu)造函數(shù)(參見9.4節(jié))的區(qū)別是:在函數(shù)處無需與NULL進行比較,這是因為“”不可能是NULL,而“指針”可以為NULL。aa這樣的自賦值語句!b=b=……c=a=…a=if(this==if(*this==束符‘\0’。函數(shù)strcpy則連‘\0’一起。第四步,返回本對象的,目的是為了實現(xiàn)象a=b=c這樣的鏈?zhǔn)奖磉_。注意不要將*thisreturnthis。那么能否寫成returnother那么returnother返回的將是。class{…A(constA A&operateconstA A b #includeclass{virtual~Base(){cout<<"~Base"<<endl;classDerived:public{virtual~Derived(){cout<<"~Derived"<<endl;void{Base*pB=newDerived;//upcastdeletepB;}class{…Base&operate(constBase 類Baseintm_i,m_j,classDerived:public{…Derived&operateconstDerived //intm_x,m_y,Derived&Derived::operate=(constDerived{if(this==&other)returnBase::operate m_x=other.m_x;m_y=other.m_y;m_z=return*this;}Three”,請好好閱讀參考文獻[Cline][Meyers][Murry]。class{
voidFunc1(void);voidclassB:public{
voidFunc3(void);void{B //B從A繼承了函數(shù)Func1 //B從A繼承了函數(shù)Func2}屬性。不要覺得“白吃白不吃”,讓一個好端端的健壯青年無緣無故地參補身體?!疽?guī)則10-1-2】若在邏輯上B是A的“一種”(akindof),則允許B繼承Aclass{…classMan:public{…classBoy:public{…class{virtualvoid…classOstrich:public{…【規(guī)則10-2-1】若在邏輯上A是B的“一部分”(apartof),則不允許B從A派生,用A和class{voidclass{voidclass{voidclass{voidclass{ {m_eye.Look();} {m_nose.Smell();} Eat(void){m_mouth.Eat();} {m_ear.Listen(); Mouthm_mouth; m_ear;如果允許Head從Eye、Nose、Mouth、Ear派生而成,那么Head將自動具有LookSmell、Eat、Listen這classHead:publicEye,publicNose,publicMouth,public{ const定義常量,那么相當(dāng)于把僅用于制作鞭。const更大的是它可以修飾函數(shù)的參數(shù)、返回動,能提高程序的健壯性。所以很多C++程序設(shè)計書籍建議:“Useconstwheneveryouneed”。voidStringCopy(char*strDestination,constchar例如不要將函數(shù)voidFunc1(intx)寫成voidFunc1(constintx)。同理不要將函數(shù)voidFunc2(Aa)寫成voidFunc2(constAa)。其中A為用戶自定義的數(shù)據(jù)類型。對于非內(nèi)部數(shù)據(jù)類型的參數(shù)而言,象voidFunc(Aa)這樣的函數(shù)注定效率比較底。因為函數(shù)體內(nèi)將產(chǎn)生A類型的臨時對象用于參數(shù)a,而臨時對象的構(gòu)造、、析構(gòu)過程都將消耗時為了提高效率,可以將函數(shù)改為voidFunc(A&a),因為“傳遞”僅借用一下參數(shù)的別名而已,不需要產(chǎn)生臨時對象。但是函數(shù)voidFunc(A&a)存在一個缺點:“傳遞”有可能改變參數(shù)a,這是我們不期望的。解決這個問題很容易,加const修飾即可,因此函數(shù)最終成為voidFunc(constA&a)。以此類推,是否應(yīng)將voidFunc(intx)改寫為voidFunc(constint&x),以便提高效率?完全沒有必要,因率。例如將voidFunc(Aa改為voidFunc(constA&a)效率的目的,又降低了函數(shù)的可理解性。例如voidFunc(intx)不應(yīng)該改為voidFunc(constint&x)表11-1-1“const&”constchar*char*str=constchar*str=例如函數(shù)intGetInt(void)寫成constintGetInt(void)同理函數(shù)AGetA(void)寫成constAGetA(void),其中A為用戶自定義的數(shù)據(jù)類型如果返回值不是內(nèi)部數(shù)據(jù)類型,將函數(shù)AGetA(void)改寫為constA&GetA(void)的確能提高效率。但此時千萬千萬要,一定要搞清楚函數(shù)究竟是想返回一個對象的“拷貝”還是僅返回“別名”就可以了,classA&operate(constA Aa,b, a,b,c為A…ab (ab) 如果將賦值函數(shù)的返回值加constabc仍然正確,但是語句(a=b)=c則是的。classStack{ GetCount(void)const;//const成員函數(shù) intStack::GetCount(void){++m_num;//編譯錯誤,企圖修改數(shù)據(jù)成員m_num //編譯錯誤,企圖調(diào)用非const函數(shù)return}[Cline]MarshallP.ClineandGregA.Lomow,C++FAQs,Addison-Wesley,[Eckel]BruceEckel,ThinkinginC++(C++編程思想,等譯),機械工業(yè),2000[Maguire]SteveMaguire,WritingCleanCode(編程精粹,等譯),電子工業(yè),1993[Meyers]ScottMeyers,EffectiveC++,Addison-Wesley,1992[Murry]RobertB.Murry,C++StrategiesandTactics,Addison-Wesley,[Summit]SteveSummit,CProgrammingFAQs,Addison-Wesley,項 頭文件是否使用了ifndef/define/endif預(yù)處理塊項“{”和“}”在定義變量(或參數(shù))時,是否將修飾符*類結(jié)構(gòu)的public,protected,private順序是否在所有的程序中保持項標(biāo)識符的長度應(yīng)當(dāng)符合“min-length&&max-information”項是否用隱含錯誤的方式寫if語句?使用goto語句時是否留下隱患?例如跳過了某些對象的構(gòu)造、變項+項使用了assert?例如情況與錯誤情況,后者是必然數(shù)、返回值,甚至函數(shù)的定義體。“Useconstwheneveryou項 或ew申請內(nèi)存之后,是否立即檢查指針值是否為 和new/delete使用C++項項是否違背編程規(guī)范而讓C+編譯器自動為類產(chǎn)生四個缺省的函內(nèi)存資源;(3)分配新的內(nèi)存資源,并內(nèi)容;(4)返回項若在邏輯上A是B的“一部分”(apartof),則不允許B從項一、請?zhí)顚態(tài)OOLfloat,“零值”if語句。(10分提示:這里“零值”可以是0,0.0FALSE或者“空指針”intn與“零值”ifif(n==0if(n!=0BOOLflag與“零值”iffloatx與“零值”ifchar*p與“零值”ifcharstr[]=o”voidFunc(char{sizeof(str)}char*p=str n=sizeof(str)sizeof(p)void*p=malloc(100sizeof(n)
溫馨提示
- 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)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 腳手架搭設(shè)專項施工方案
- 個人小額無抵押借款合同協(xié)議書
- 結(jié)束協(xié)議房地產(chǎn)代理合同
- 蔬菜營銷策略購買合同
- 瓷磚訂購合同模板
- 電子元件采購合同范本
- 購銷紡織品的合同樣本
- 校園多媒體設(shè)備招標(biāo)文件
- 網(wǎng)絡(luò)購銷合同規(guī)范化管理的方法與策略
- 農(nóng)資采購合同的效力問題
- 高校人力資源管理系統(tǒng)
- 03船舶證書一覽表
- 國外發(fā)達國家中水回用現(xiàn)狀
- 墻體構(gòu)造設(shè)計
- 成人哮喘生命質(zhì)量評分表
- 相親相愛一家人簡譜
- 數(shù)字油畫-社團活動記錄課件
- 燃氣紅外線輻射采暖技術(shù)交底
- 液壓系統(tǒng)課件(完整)課件
- 驗收合格證明(共9頁)
- 蘇強格命名規(guī)則
評論
0/150
提交評論