版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
高質(zhì)量C++/C編程指南文件狀態(tài)[]草稿文件[]更改正式文件文件標識:林銳博士完成日期:2001年7月24日起止日期備注2001-7-1至正式文件朱洪海審查V0.9, 6 1.1版權(quán)和版本的聲明 1.2頭文件的結(jié)構(gòu) 1.3定義文件的結(jié)構(gòu) 1.4頭文件的作用 2.2代碼行 2.4對齊 2.5長行拆分 2.6修飾符的位置 202.8類的版式 21 223.1共性規(guī)則 223.2簡單的WINDOws應(yīng)用程序命 25 264.1運算符的優(yōu)先級 26 27 274.4循環(huán)語句的效率 29 335.1為什么需要常量 5.3常量定義規(guī)則 5.4類中的常量 366.1參數(shù)的規(guī)則 6.2返回值的規(guī)則 6.3函數(shù)內(nèi)部實現(xiàn)的規(guī)則 406.5使用斷言 416.6引用與指針的比較 42 47.1內(nèi)存分配方式 447.2常見的內(nèi)存錯誤及其對策 447.3指針與數(shù)組的對比 457.4指針參數(shù)是如何傳遞內(nèi)存的? 7.6動態(tài)內(nèi)存會被自動釋放嗎? 7.7杜絕“野指針” 7.9內(nèi)存耗盡怎么辦? 7.12一些心得體會 56 578.1函數(shù)重載的概念 8.3參數(shù)的缺省值 8.4運算符重載 8.5函數(shù)內(nèi)聯(lián) 8.6一些心得體會 第9章類的構(gòu)造函數(shù)、析構(gòu)函數(shù)與賦值函數(shù) 9.1構(gòu)造函數(shù)與析構(gòu)函數(shù)的起源 9.3構(gòu)造和析構(gòu)的次序 9.4示例:類STRING的構(gòu)造函數(shù)與析構(gòu)函數(shù) 9.5不要輕視拷貝構(gòu)造函數(shù)與賦值函數(shù) 9.6示例:類STRING的拷貝構(gòu)造函數(shù)與賦值函數(shù) 739.7偷懶的辦法處理拷貝構(gòu)造函數(shù)與賦值函數(shù) 9.8如何在派生類中實現(xiàn)類的基本函數(shù) 9.9一些心得體會 第10章類的繼承與組合 78 第11章其它編程經(jīng)驗 82 84 88附錄B:C++/C試題 93 前捏造的C++/C程序怎么會有那么多的毛病?”(10)真正的程序員不會在上午9:00到下午5:00之間工作,如果你看到我開發(fā)的軟件都與科研相關(guān)(集成電路CAD和3D圖形學(xué)領(lǐng)域),(1)編程風(fēng)格;(2)出錯處理;(3)算法復(fù)雜度分析(用于提高性能)。(1)編程老手可能會長期用隱含錯誤的方式編程(習(xí)慣成自然),發(fā)現(xiàn)毛病后都不愿相序員的編程技能,質(zhì)量合格率大約是10%。很少有人能夠?qū)懗鐾耆腺|(zhì)量要求的if(1)知錯就改;(2)經(jīng)常溫故而知新;(3)堅持學(xué)習(xí),天天向上。(3)如果你考出85分以上的好成績,你有義務(wù)和資格為你所在的團隊作“C++/C編程”預(yù)計到2002年7月,我們將建立切合中國國情的CMMI3級解決方案。屆時,包括林銳,2001年7月第1章文件結(jié)構(gòu)稱為頭文件。另一個文件用于保存程序的實現(xiàn)(implementation),稱為定義(definition)的定義文件通常以“.cpp”為后綴(也有一些系統(tǒng)以“.cc”或“.cxx”為后綴)。1.1版權(quán)和版本的聲明版權(quán)和版本的聲明位于頭文件和定義文件的開頭(參見示例1-1),主要內(nèi)容有:(1)版權(quán)信息。(2)文件名稱,標識符,摘要。(3)當(dāng)前版本號,作者/修改者,完成日期。(4)版本歷史信息。*Copyright(c)2001,上海貝爾有限公司網(wǎng)絡(luò)應(yīng)用事業(yè)部**文件標識:見配置管理計劃書*摘要:簡要描述本文件的內(nèi)容**當(dāng)前版本:1.1*作者:輸入作者(或修改者)名字*完成日期:2001年7月20日**取代版本:1.0*原作者:輸入原作者(或修改者)名字*完成日期:2001年5月10日1.2頭文件的結(jié)構(gòu)(1)頭文件開頭處的版權(quán)和版本聲明(參見示例1-1)。(2)預(yù)處理塊。(3)函數(shù)和類結(jié)構(gòu)聲明等。
【建議1-2-1】頭文件中只存放“聲明//版權(quán)和版本聲明見示例1-1,此處省略。#ifndefGRAPHICS_H//防止graphics.h被#include“myheader.h”voidFunction1(…);//{//類結(jié)構(gòu)聲明//版權(quán)和版本聲明見示例1-1,此處省略。//全局函數(shù)的實現(xiàn)體{//類成員函數(shù)的實現(xiàn)體1.4頭文件的作用(1)通過頭文件來調(diào)用庫功能。在很多場合,源代碼不便(或不準)向用戶1.5目錄結(jié)構(gòu)第2章程序的版式空行起著分隔程序段落的作用??招械皿w(不過多也不過少)將使程序的布局更加{}}{}{{}{}}2.2代碼行示例2-2(a)為風(fēng)格良好的代碼行,示例2-2(b)intheight;//高度intwidth,height,depth;//寬度高度深度}for(initialization;con{}for(initialization;con
【建議2-2-1】盡可能在定義變量的同時初始化該變量(就近原則)intheight=10;//定義并初紿化height2.3代碼行內(nèi)的空格掉一些空格,如for(i=0;i<10;i++)//不要寫成array[5]=0;//不要寫成a.Function();//不要寫成b->Function();示例2-3代碼行內(nèi)的空格2.4對齊●【規(guī)則2-4-1】程序的分界符‘{’和‘}'應(yīng)獨占一行并且位于同一列,同時與引用●【規(guī)則2-4-2】{}之內(nèi)的代碼塊在‘{’右邊數(shù)格處左對齊。示例2-4(a)為風(fēng)格良好的對齊,示例2-4(b)為風(fēng)格不良的對齊。{}}{}{}}for(initialization;cond{}for(initialization;condi}{}}2.5長行拆分&&(very_longer_variable3<=very_longer_variabl&&(very_longer_variable5<=very_longer_variable{}{}2.6修飾符的位置修飾符*和&應(yīng)該靠近數(shù)據(jù)類型還是該靠近變量名,是個有爭議的活題。●【規(guī)則2-6-1】應(yīng)當(dāng)將修飾符*和&緊靠變量名2.7注釋(1)版本、版權(quán)聲明;(2)函數(shù)接口說明;(3)重要的代碼行或段落提示。i++;//i加1,多余的注釋{}{2.8類的版式的接口(或服務(wù))。{}floatfX,fY,fz;//前綴f表示float類型程序中的英文單詞一般不會太復(fù)雜,用詞應(yīng)當(dāng)準確。例如不要把CurrentValue寫成幾十年前老ANSIC規(guī)定名字不準超過6個字符,現(xiàn)今的C++/C不再有此限制。一怪。那么名字是否越長約好?不見得!例如變量名maxval就比maxValueUntil0verflow●【規(guī)則3-1-4】程序中不要出現(xiàn)僅靠大小寫區(qū)分的相似的標識符。intx,X;//變量x與X容易混淆●【規(guī)則3-1-5】程序中不要出現(xiàn)標識符完全相同的局部變量和全局變量,盡管兩者的作用域不同而不會發(fā)生語法錯誤,但會使人誤解。●【規(guī)則3-1-7】全局函數(shù)的名字應(yīng)當(dāng)使用“動詞”或者“動詞十名詞”(動賓詞組)。類的成員函數(shù)應(yīng)當(dāng)只使用“動詞”,被省略掉的名詞就是對象本身。//類的成員函數(shù)●【規(guī)則3-1-8】用正確的反義詞組命名具有互斥意義的變量或相反動作的函數(shù)等。
【建議3-1-1】盡量避免名字中出現(xiàn)數(shù)字編號,如Valuel,Value2等,除非邏輯上的確需要編號。這是為了防止程序員偷懶,不肯為命名動腦筋而導(dǎo)致產(chǎn)生無意義的名字(因為用數(shù)字編號最省事)。3.2簡單的Windows應(yīng)用程序命名規(guī)則作者對“匈牙利”命名規(guī)則做了合理的簡化,下述的命名規(guī)則簡單易用,比較適合于Windows應(yīng)用軟件的開發(fā)?!瘛疽?guī)則3-2-1】類名和函數(shù)名用大寫字母開頭的單詞組合而成。classNode;//類名classLeafNode;//類名voidDraw(void);//函數(shù)名●【規(guī)則3-2-2】變量和參數(shù)用小寫字母開頭的單詞組合而成。constintMAX_LENGTH{staticints_initValue;//靜態(tài)變量}●【規(guī)則3-2-5】如果不得已需要全局變量,則使全局變量加前綴g_(表示global)。intg_howManyPeople;intg_howMuchMoney;//全局變量●【規(guī)則3-2-6】類的數(shù)據(jù)成員加前綴m_(表示member),這樣可以避免數(shù)據(jù)成員與void0bject::SetValue(intwidth,intheight){均以gl開頭,所有常量(或宏定義)均以GL開頭。3.3簡單的Unix應(yīng)用程序命名規(guī)則第4章表達式和基本語句讀者可能懷疑:連if、for、while、goto、switch這樣簡單的東西也要探討編程風(fēng)格,是不是小題大做?我真的發(fā)覺很多程序員用隱含錯誤的方式寫表達式和基本語句,我自己也犯過類似表達式和語句都屬于C++/C的短語結(jié)構(gòu)語法。它們看似簡單,但使用時隱患比較多。本章歸納了正確使用表達式和語句的一些規(guī)則與建議。C++/C語言的運算符有數(shù)十個,運算符的優(yōu)先級與結(jié)合律如表4-1所示。注意一元運算符+-*的優(yōu)先級高于對應(yīng)的二元運算符。優(yōu)先級結(jié)合律從左至右從右至左從左至右十從左至右從左至右從左至右從左至右&從左至右A從左至右從左至右從左至右Ⅱ從右至左從右至左從左至右●【規(guī)則4-1-1】如果代碼行中的運算符比較多,用括號確定表達式的操作順序,避免由于將表4-1熟記是比較困難的,為了防止產(chǎn)生歧義并提高可讀性,應(yīng)當(dāng)用括號確4.2復(fù)合表達式4.3if語句4.3.1布爾變量與零值比較●【規(guī)則4-3-1】不可將布爾變量直接與TRUE、FALSE或者1、0進行比較。if(!flag)//表示flag為假其它的用法都屬于不良風(fēng)格,例如:if(flagif(flag4.3.2整型變量與零值比較●【規(guī)則4-3-2】應(yīng)當(dāng)將整型變量用“==”或“!=”直接與0比較。假設(shè)整型變量的名字為value,它與零值比較的標準if語句如下:不可模仿布爾變量的風(fēng)格而寫成if(value)//會讓人誤解value是布爾變量4.3.3浮點變量與零值比較●【規(guī)則4-3-3】不可將浮點變量用“==”或“!=”與任何數(shù)字比較。千萬要留意,無論是float還是double類型的變量,都有精度限制。所以一定要避免將浮點變量用“==”或“!=”與數(shù)字比較,應(yīng)該設(shè)法轉(zhuǎn)化成“>=”或“<=”形式。假設(shè)浮點變量的名字為x,應(yīng)當(dāng)將if(x==0.0)//隱含錯誤的比較轉(zhuǎn)化為if((x>=-EPSINON)&&(x<其中EPSINON是允許的誤差(即精度)。4.3.4指針變量與零值比較●【規(guī)則4-3-4】應(yīng)當(dāng)將指針變量用“==”或“!=”與NULL比較。指針變量的零值是“空”(記為NULL)。盡管NULL的值與0相同,但是兩者意義不同。假設(shè)指針變量的名字為p,它與零值比較的標準if語句如下:if(p==NULL)//p與NULL顯式比較,強調(diào)p是指針變量不要寫成if(p==0)//容易讓人誤解p是整型變量或者if(p)//容易讓人誤解p是布爾變量4.3.5對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)該將如下不良風(fēng)格的程序{}{}或者改寫成更加簡練的4.4循環(huán)語句的效率C++/C循環(huán)語句中,for語句使用頻率最高,重點論述循環(huán)體的效率。提高循環(huán)體效率的基本辦法是降低循環(huán)體的復(fù)雜性?!瘛窘ㄗh4-4-1】在多重循環(huán)中,如果有可能,應(yīng)當(dāng)將最長的循環(huán)放在最內(nèi)層,最短的循環(huán)放在最外層,以減少CPU跨切循環(huán)層的次數(shù)。例如示例4-4(b)的效率比示例4-4(a)的高。{}{}示例4-4(a)低效率:長循環(huán)在最外層示例4-4(b)高效率:長循環(huán)在最內(nèi)層●【建議4-4-2】如果循環(huán)體內(nèi)存在邏輯判斷,并且循環(huán)次數(shù)很大,宜將邏輯判斷移到}{}{}表4-4(c)效率低但程序簡潔表4-4(d)效率高但程序不簡潔示例4-5(a)中的x值屬于半開半閉區(qū)間“0=<x<N”,起點到終點的間隔為N,循示例4-5(b)中的x值屬于閉區(qū)間“0=<x<=N-1”,起點到終點的間隔為N-1,循{{}switch是多分支選擇語句,而if語句只有兩個分支可供選擇。雖然可以用嵌套的if語句來實現(xiàn)多分支選擇,但那樣的程序冗長難讀。這是switch語句存在的理由。switch語句的基本格式是:{}●【規(guī)則4-6-1】每個case語句的結(jié)尾不要忘了加break,否則將導(dǎo)致多個分支重疊(除非有意使多個分支重疊)。也應(yīng)該保留語句default:break;這樣做并非多此一舉,而是為了防止別人誤以為你忘了default處理。自從提倡結(jié)構(gòu)化設(shè)計以來,goto就成了有爭議的語句。首先,由于goto語句可以靈活跳轉(zhuǎn),如果不加限制,它的確會破壞結(jié)構(gòu)化設(shè)計風(fēng)格。其次,goto語句經(jīng)常帶來錯誤或隱患。它可能跳過了某些對象的構(gòu)造、變量的初始化、重要的計算等語句,例如:intsum=0;//被goto跳過如果編譯器不能發(fā)覺此類錯誤,每用一次goto語句都可能留下隱患。很多人建議廢除C++/C的goto語句,以絕后患。但實事求是地說,錯誤是程序員自己造成的,不是goto的過錯。goto語句至少有一處可顯神通,它能從多重循環(huán)體中咻地一下子跳到外面,用不著寫很多次的break語句;例如為宏常量)。C++語言除了#define外還可以用const來定義常量(稱為const常量)。constfloatPI5.3常量定義規(guī)則5.4類中的常量有時我們希望某些常量只在類中有效。由于#define定義的宏常量是全局的,不能達到目的,于是想當(dāng)然地覺得應(yīng)該用const修飾數(shù)據(jù)成員來實現(xiàn)。const數(shù)據(jù)成員的確是存在的,但其含義卻不是我們所期望的。const數(shù)據(jù)成員只在某個對象生存期內(nèi)是常量,而對于整個類而言卻是可變的,因為類可以創(chuàng)建多個對象,不同的對象其const數(shù)不能在類聲明中初始化const數(shù)據(jù)成員。以下用法是錯誤的,因為類的對象未被創(chuàng)建時,編譯器不知道SIZE的值是什么。constintSIZE=100;//錯誤,企圖在類聲明中初始化const數(shù)據(jù)成員intarray[SIZE];//錯誤,未知的SIZEconst數(shù)據(jù)成員的初始化只能在類構(gòu)造函數(shù)的初始化表中進行,例如A(intsize);//構(gòu)造函數(shù)A::A(intsize):SIZE(size)//構(gòu)造函數(shù)的初始化表{Aa(100);//對象a的SIZE值為100Ab(200);//對象b的SIZE值為200怎樣才能建立在整個類中都恒定的常量呢?別指望const數(shù)據(jù)成員了,應(yīng)該用類中的枚舉常量來實現(xiàn)。例如第6章函數(shù)設(shè)計voidSetValue(int,int);//不良的風(fēng)格floatGetValue(void);//良好的風(fēng)格floatGetValue();//不良的風(fēng)格6.2返回值的規(guī)則由于c是char類型,取值范圍是[-128,127],如果宏EOF的值在char的取值范圍(通常為負1)。因此函數(shù)getchar就成了int類型。函數(shù)getchar可以改寫成B00LGeintlength=strlen(strcpy(str,“Hello//賦值函數(shù)//相加函數(shù),如果沒有friend修飾則只許有一個右側(cè)參數(shù)}return*this;//返回的是*this的引用,無需拷貝過程a=b;;//如果用“值傳遞”,將產(chǎn)生一次*this拷貝a=b=c;//如果用“值傳遞”,將產(chǎn)生兩次*this拷貝{deletetemtemp.data=newchar[strlen(s1}{}(3)如果函數(shù)返回值是一個對象,要考慮return語句6.4其它建議static局部變量是函數(shù)的“記憶”存儲器。建議盡量少用static局部變量,除非必需。
【建議6-4-4】不僅要檢查輸入?yún)?shù)的有效性,還要檢查通過其它途徑進入函數(shù)體內(nèi)的變量的有效性,例如全局變量、文件句柄等。
【建議6-4-5】用于出錯處理的返回值一定要清楚,讓使用者不容易忽視或誤解錯誤6.5使用斷言程序一般分為Debug版本和Release版本,De斷言assert是僅在Debug版本起作用的宏,它用于檢查“不應(yīng)該”發(fā)生的情況。示例6-5是一個內(nèi)存復(fù)制函數(shù)。在運行過程中,如果assert的參數(shù)為假,那么程序就會中止(一般地還會出現(xiàn)提示對話,說明在什么地方引發(fā)了assert)。void*memcpy(void*pvTo,const{byte*pbTo=(byte*)pvTo;//防止改變pbyte*pbFrom=(byte*)pvFrom;//防止改變pvFrom的地址assert不是一個倉促拼湊起來的宏。為了不在程序的Debug版本和Release版本引起差別,assert不應(yīng)該產(chǎn)生任何副作用。所以a看成一個在任何系統(tǒng)狀態(tài)下都可以安全使用的無害測試手段。如果程序在assert處終止很少有比跟蹤到程序的斷言,卻不知道該斷言的作用更讓人沮喪的事了。你化了很多時間,不是為了排除錯誤,而只是為了弄清楚這個錯誤到底是什么。有的時候,程序員偶爾還會設(shè)計出有錯誤的斷言。所以如果搞不清楚斷言檢查的是什么,就很難判斷錯誤是出現(xiàn)在程序中,還是出現(xiàn)在斷言中。幸運的是這個問題很好解決,只要加上清晰的看到樹上釘著一塊“危險”的大牌子。但危險到底是什么?樹要倒?有廢井?有野獸?除非告訴人們“危險”是什么,否則這個警告牌難以起到積極有效的作用。難以理解的斷言常常被程序員忽略,甚至被刪除。[Maguire,p8-p30]6.6引用與指針的比較(2)不能有NULL引用,引用必須與合法的存儲單元關(guān)聯(lián)(指針則可以是NULL)。用,只是把k的值改變成為6。由于k是intj=6;k=j;//k和i的值都變成了6;改變x的值不會影響n,所以n的值仍然是0。{以下是“指針傳遞”的示例程序。由于Func2函數(shù)體內(nèi)的x是指向外部變量n的指針,改變該指針的內(nèi)容將導(dǎo)致n的值改變,所以n的值成為10。{}cout<<“n=”<<n<<endl;//n=10以下是“引用傳遞”的示例程序。由于Func3函數(shù)體內(nèi)的x是外部變量n的引用,x和n是同一個東西,改變x等于改變n,所以n的值成為10。對比上述三個示例程序,會發(fā)現(xiàn)“引用傳遞”的性質(zhì)象“指針傳遞”,而書寫方式象這東西?指針能夠毫無約束地操作內(nèi)存中的如何東西,盡管指針功能強大,但是非常危險。就象一把刀,它可以用來砍樹、裁紙、修指甲、理發(fā)等等,誰敢這樣用?以免發(fā)生意外。比如說,某人需要一份證明,本來在文件上蓋上公章的印子就行了,如果把取公章的鑰匙交給他,那么他就獲得了不該有的權(quán)利。第7章內(nèi)存管理◆忘記了釋放內(nèi)存,造成內(nèi)存泄露?!翎尫帕藘?nèi)存卻繼續(xù)使用它?!瘛疽?guī)則7-2-3】避免數(shù)組或指針的下標越界,特別要當(dāng)心發(fā)生“多1”或者“少1”示例7-3-1中,字符數(shù)組a的容量是6個字符,其內(nèi)容為hello\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)致運行錯誤。char*p=“world”//注意p指向常量字符串不能對數(shù)組名進行直接復(fù)制與比較。示例7-3-2中,若想把數(shù)組a的內(nèi)容復(fù)制給數(shù)組b,不能用語句b=a,否則將產(chǎn)生編譯錯誤。應(yīng)該用標準庫函數(shù)strcpy進行復(fù)制。語句p=a并不能把a的內(nèi)容復(fù)制指針p,而是把a的地址賦給了p。要想復(fù)制a的內(nèi)容,可以先用庫函數(shù)malloc為p申請一塊容量為strlen(a)+1個字符的內(nèi)存strcpy進行字符串復(fù)制。同理,語句if(p==a)比較的不是內(nèi)容而是地址,應(yīng)該用庫函//數(shù)組..strcpy(b,a);//不能用b=a;if(strcmp(b,a)==0)//不能用if(b==a)//指針..char*p=(char*)malloc(sizeof(charstrcpy(p,a);//不要用p=a;if(strcmp(p,a)==0)//不要用if(p==a)cout<<sizeof(a)<<endl;//12字節(jié)cout<<sizeof(p)<<endl;//4字節(jié){cout<<sizeof(a)<<endl;//4字節(jié)而不是100字節(jié)}voidGetMemory(char*p,in{}{GetMemory(str,100);//ststrcpy(str,"hello");//運行錯誤}參數(shù)p的副本是_p,編譯器使_p=p。如果函數(shù)體內(nèi)的程序修改了_p的內(nèi)容,就導(dǎo)致voidGetMemory2(char**p,{}{GetMemory2(&str,100);//注意參數(shù)是&st}{}{}{returnp;//編譯器將提出警告}{str=GetString();//str的內(nèi)容是垃圾}如果把示例7-4-4改寫成示例7-4-5,會怎么樣?{}{}別看free和delete的名字惡狠狠的(尤其是delete),它們只是把指針所指的內(nèi)存給用調(diào)試器跟蹤示例7-5,發(fā)現(xiàn)指針p被free以后其地址仍然不變(非NULL),只是free(p);//p所指的內(nèi)存被釋放,但是p{strcpy(p,“world”);//出錯}{char*p=(char*)malloc(100);//動態(tài)內(nèi)存會自動釋放嗎?}示例7-6試圖讓動態(tài)內(nèi)存自動釋放(1)指針消亡了,并不表示它所指的內(nèi)存會被自動釋放。(2)內(nèi)存被釋放了,并不表示指針會消亡或者成了NULL指針。7.7杜絕“野指針”(2)指針p被free或者delete之后,沒有置為NULL,讓人誤以為p是個合法的指針。{voidFunc(void){cout<<“FuncofclassA”<<{}p->Func();//p是“野指針”在創(chuàng)建的同時要自動執(zhí)行構(gòu)造函數(shù),對象在消亡之前要自動執(zhí)行析構(gòu)函數(shù)。由于{voidInitialize(void)voidDestroy(void){cout<<“De{Obj*a=(obj*)malloc(sizeof(obj)//...a->Destroy();//清除工作}{0bj*a=newObj;//申請動態(tài)內(nèi)存并且初始化//...}示例7-8用malloc/free和new/delete如何實現(xiàn)對象的動態(tài)內(nèi)存管理既然new/delete的功能完全覆蓋了malloc/free,為什么C++不把malloc/free淘汰出局呢?這是因為C++程序經(jīng)常要調(diào)用C函數(shù),而C程序只能用malloc/free管理動如果用free釋放“new創(chuàng)建的動態(tài)對象”,那么該對象因無法執(zhí)行析構(gòu)函數(shù)而可能但是該程序的可讀性很差。所以new/delete必須配對使用,malloc/free也一樣。7.9內(nèi)存耗盡怎么辦?如果在申請動態(tài)內(nèi)存時找不到足夠大的內(nèi)存塊,malloc和new將返回NULL指針,宣告內(nèi)存申請失敗。通常有三種方式處理“內(nèi)存耗盡”問題。(1)判斷指針是否為NULL,如果是則馬上用return語句終止本函數(shù)。例如:}(2)判斷指針是否為NULL,如果是則馬上用exit(1)終止整個程序的運行。例如:(3)為new和malloc設(shè)置異常處理函數(shù)。例如VisualC++可以用_set_new_hander函數(shù)為new設(shè)置用戶自己定義的異常處理函數(shù),也可以讓malloc享用與new相同的異常處理函數(shù)。詳細內(nèi)容請參考C++使用手冊。上述(1)(2)方式使用最普遍。如果一個函數(shù)內(nèi)有多處需要申請動態(tài)內(nèi)存,那么方式(1)就顯得力不從心(釋放內(nèi)存很麻煩),應(yīng)該用方式(2)來處理。很多人不忍心用exit(1),問:“不編寫出錯處理程序,讓操作系統(tǒng)自己解決行不行?”不行。如果發(fā)生“內(nèi)存耗盡”這樣的事情,一般說來應(yīng)用程序已經(jīng)無藥可救。如果不用exit(1)把壞程序殺死,它可能會害死操作系統(tǒng)。道理如同:如果不把歹徒擊斃,歹有一個很重要的現(xiàn)象要告訴大家。對于32位以上的應(yīng)用程序而言,無論怎樣使用malloc與new,幾乎不可能導(dǎo)致“內(nèi)存耗盡”。我在Windows98下用VisualC++測試程序,見示例7-9。這個程序會無休止地運行下去,根本不會終止。因為32位操作系統(tǒng)支持“虛存”,內(nèi)存用完了,自動用硬盤空間頂替。我只聽到硬盤嘎吱嘎吱地響,Window98已經(jīng)累得對鍵盤、鼠標毫無反應(yīng)。我可以得出這么一個結(jié)論:對于32位以上的應(yīng)用程序,“內(nèi)存耗盡”錯誤處理程序毫無用處。這下可把Unix和Windows程序員們樂壞了:反正錯誤處理程序不起作用,我就不寫了,省了很多麻煩。我不想誤導(dǎo)讀者,必須強調(diào):不加錯誤處理將導(dǎo)致程序的質(zhì)量很差,千萬不可因小{p=newfloat[100007.10malloc/free的使用要點用malloc申請一塊長度為length的整數(shù)類型的內(nèi)存,程序如下:int*pcout<<sizeof(int)<<endl;cout<<sizeof(unsignedlong)<<endl;cout<<sizeof(float)<cout<<sizeof(double)<<voidfree(void*7.11new/delete的使用要點{0bj*b=newObj(1);//初值為10bj*objects=newObj[100];//創(chuàng)建100個動態(tài)對象Obj*objects=newObj[100](1);//創(chuàng)建100個動態(tài)對象的同時賦初值1后者相當(dāng)于deleteobjects[0],漏掉了另外99個對象。7.12一些心得體會(包括我自己)。我最初學(xué)習(xí)C語言時特別怕指針,導(dǎo)致我開發(fā)第一個應(yīng)用軟件(約1第8章C++函數(shù)的高級特性8.1.1重載的起源//可以改為//可以改為voidEat(Fish…);//可以改為構(gòu)造函數(shù)與類同名(請參見第9章),構(gòu)造函數(shù)只能有一個8.1.2重載是如何實現(xiàn)的?intFunction(void 如果C++程序要調(diào)用已經(jīng)被編譯后的C函數(shù),該怎么辦?extern“C”…//其它函數(shù)extern“C”#include“myheader.h”…//其它C頭文件} voidPrint(…);//成員函數(shù)}不論兩個Print函數(shù)的參數(shù)是否不同,如果類的某個成員函數(shù)要調(diào)用全局函數(shù)示例8-1-3中,第一個output函數(shù)的參數(shù)是int類型,第二個output函數(shù)的參數(shù)voidoutput(intx);//函數(shù)聲明voidoutput(floatx);//函數(shù)聲明{}{}{//output(0.5);//error!ambigu}(1)相同的范圍(在同一個類中);(2)函數(shù)名字相同;(3)參數(shù)不同;(1)不同的范圍(分別位于派生類與基類);(2)函數(shù)名字相同;(3)參數(shù)相同;(4)基類函數(shù)必須有virtual關(guān)鍵字。示例8-2-1中,函數(shù)Base::f(int)與Base::f(float)相互重載,而Base::g(void){voidf(intx){cout<<"Base:virtualvoidg(void){cout<<"BasclassDerived:publi{virtualvoidg(void){{pb->g()://Derived}8.2.2令人迷惑的隱藏規(guī)則(1)如果派生類的函數(shù)與基類的函數(shù)同名,但是參數(shù)不同。此時,不論有無vir示例程序8-2-2(a)中:(1)函數(shù)Derived::f(float)覆蓋了Base::f(float)。(2)函數(shù)Derived::g(int)隱藏了Base::g(float),而不是重載。(3)函數(shù)Derived::h(float)隱藏了Base::h(float),而不是覆蓋。{virtualvoidf(floatx){cout<<"Base::f(float)"<<x<<envoidg(floatx){cout<<"Base::gvoidh(floatx){cout<<"Base::h(floatclassDerived:publi{virtualvoidf(floatx){cout<<"Derived::f(float)"<<x<<envoidg(intx){cout<<"Derived:voidh(floatx){cout<<"Deri示例8-2-2(a)成員函數(shù)的重載、覆蓋和隱藏{//Good:behaviordeoftheobjectpb->f(3.14f);//Derived::f(float)pd->f(3.14f);//Derived::f(float)//Bad:behaviordependsontypeofthepointerpb->g(3.14f);pd->g(3.14f);//Bad:behaviordependsontypeofthepointerpb->h(3.14f);//Bpd->h(3.14f);//Derived::h(float)}{{}◆寫語句pd->f(10)的人可能真的想調(diào)用Derive示例8-2-3中,如果語句pd->f(10)一定要調(diào)用函數(shù)Base::f(int),那么將類{{為什么會這樣?我想是有兩個原因:一是函數(shù)的實現(xiàn)(定義)本來就與參數(shù)是否有voidoutput(intx,floa}}}8.4運算符重載8.4.1概念ComplexAdd(constComplex&a,constCo運算符與普通函數(shù)在調(diào)用時的不同之處是:對于普通函數(shù),參數(shù)出現(xiàn)在圓括號內(nèi);而對于運算符,參數(shù)出現(xiàn)在其左、右側(cè)。例如c=Add(a,b);//用普通函數(shù)c=a+b;//用運算符+如果運算符被重載為全局函數(shù),那么只有一個參數(shù)的運算符叫做一元運算符,有兩如果運算符被重載為類的成員函數(shù),那么一元運算符沒有參數(shù),二元運算符只有一個右側(cè)參數(shù),因為對象自己成了左側(cè)參數(shù)。p44-p47]對此問題作了較多的闡述,并總結(jié)了表8-4-1的規(guī)則。規(guī)則建議重載為成員函數(shù)只能重載為成員函數(shù)建議重載為成員函數(shù)建議重載為全局函數(shù)由于C++語言支持函數(shù)重載,才能將運算符當(dāng)(1)不要過分擔(dān)心自己不會用,它的本質(zhì)仍然是程序員們熟悉的函數(shù)。(2)不要過分熱心地使用,如果它不能使代碼變得更加易讀易寫,那就別用,否則會自8.4.2不能被重載的運算符在C++運算符集合中,有一些運算符是不允許被重載的。這種限制是出于安全方面(1)不能改變C++內(nèi)部數(shù)據(jù)類型(如int,float等)的運算符。(2)不能重載‘.’,因為‘.’在類中對任何成員都有意義,已經(jīng)成為標準用法。(3)不能重載目前C++運算符集合中沒有的符號,如#,@,$等。原因有兩點,一是難以理解,二是難以確定優(yōu)先級。(4)對已經(jīng)存在的運算符進行重載時,不能改變優(yōu)先級規(guī)則,否則將引起混亂。8.5函數(shù)內(nèi)聯(lián)8.5.1用內(nèi)聯(lián)取代宏代碼#defineMAX(a,b)(a)>(b#defineMAX(a,b)((a)>(b)?(a):(b)){}voidFoo(intx,inty);inlinevoidFoo(intx,inty)//inline與函數(shù)定義體放在一起{}一般地,用戶可以閱讀函數(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ù)是否需定義在類聲明之中的成員函數(shù)將自動地成為內(nèi)聯(lián)函數(shù),例如{voidFoo(intx,inty){…}//自動地成為內(nèi)聯(lián)函數(shù)}將成員函數(shù)的定義體放在類聲明之中雖然能帶來書寫上的方便,但不是一種良好的編程//頭文件{voidFoo(intx,inty);}//定義文件inlinevoidA::Foo(intx,i{}8.5.3慎用內(nèi)聯(lián)內(nèi)聯(lián)能提高函數(shù)的執(zhí)行效率,為什么不把所有的函數(shù)都定義成內(nèi)聯(lián)函數(shù)?如果所有的函數(shù)都是內(nèi)聯(lián)函數(shù),還用得著“內(nèi)聯(lián)”這個關(guān)鍵字嗎?內(nèi)聯(lián)是以代碼膨脹(復(fù)制)為代價,僅僅省去了函數(shù)調(diào)用的開銷,從而提高函數(shù)的8.6一些心得體會第9章類的構(gòu)造函數(shù)、析構(gòu)函數(shù)與賦值函數(shù)A(void);//缺省的無參數(shù)構(gòu)造函數(shù)~A(void);//缺省的析構(gòu)函數(shù)~String(void);//析構(gòu)函數(shù)char*m_data;//用于保存字符串9.1構(gòu)造函數(shù)與析構(gòu)函數(shù)的起源員的大忙。但是程序通過了編譯檢查并不表示錯誤已經(jīng)不存在了,在“錯誤”的大家庭根據(jù)經(jīng)驗,不少難以察覺的程序錯誤是由于變量沒有被正確初始化或清除造成的,而初始化和清除工作很容易被人遺忘。Stroustrup在設(shè)計C++語言時充分考慮了這個問題并很好地予以解決:把對象的初始化工作放在構(gòu)造函數(shù)中,把清除工作放在析構(gòu)函數(shù)中。當(dāng)對象被創(chuàng)建時,構(gòu)造函數(shù)被自動執(zhí)行。當(dāng)對象消亡時,析構(gòu)函數(shù)被自動執(zhí)行。這下就不用擔(dān)心忘了對象的初始化和清除工作。構(gòu)造函數(shù)與析構(gòu)函數(shù)的名字不能隨便起,必須讓編譯器認得出才可以被自動執(zhí)行。Stroustrup的命名方法既簡單又合理:讓構(gòu)造函數(shù)、目的與構(gòu)造函數(shù)的相反,就加前綴~,以示區(qū)別。除了名字外,構(gòu)造函數(shù)與析構(gòu)函數(shù)的另一個特別之處是沒有返回值類型,這與返回值類型為void的函數(shù)不同。構(gòu)造函數(shù)與析構(gòu)函數(shù)的使命非常明確,就象出生與死亡,光溜溜地來光溜溜地去。如果它們有返回值類型,那么編譯器將不知所措。為了防止節(jié)外生枝,干脆規(guī)定沒有返回值類型。(以上典故參考了文獻[Eekel,p55-p56])9.2構(gòu)造函數(shù)的初始化表構(gòu)造函數(shù)有個特殊的初始化方式叫“初始化表達式表”(簡稱初始化表)。初始化表位于函數(shù)參數(shù)表之后,卻在函數(shù)體{}之前。這說明該表里的初始化工作發(fā)生在函數(shù)體◆如果類存在繼承關(guān)系,派生類必須在其初始化表里調(diào)用基類的構(gòu)造函數(shù)。A(intx);//A的構(gòu)造函數(shù)B(intx,inty);//B的構(gòu)造函數(shù):A(x)//在初始化表里調(diào)用A的構(gòu)造函數(shù){◆類的const常量只能在初始化表里被初始化,因為它不能在函數(shù)體內(nèi)用賦值的方式來初始化(參見5.4節(jié))?!纛惖臄?shù)據(jù)成員的初始化可以采用初始化表或函數(shù)體內(nèi)賦值兩種方式,這兩種方式的效率不完全相同。非內(nèi)部數(shù)據(jù)類型的成員對象應(yīng)當(dāng)采用第一種方式初始化,以獲取更高的效率。例如A(void);//無參數(shù)構(gòu)造函數(shù)A(constA&other);//拷貝構(gòu)造函數(shù)A&operate=(constA&other);//賦值函數(shù){A&a);//B的構(gòu)造函數(shù)//成員對象示例9-2(a)中,類B的構(gòu)造函數(shù)在其初始化表里調(diào)用了類A的拷貝構(gòu)造函數(shù),從而將成員對象m_a初始化。示例9-2(b)中,類B的構(gòu)造函數(shù)在函數(shù)體內(nèi)用賦值的方式將成員對象m_a初始化。我們看到的只是一條賦值語句,但實際上B的構(gòu)造函數(shù)干了兩件事:先暗地里創(chuàng)建m_a對象(調(diào)用了A的無參數(shù)構(gòu)造函數(shù)),再調(diào)用類A的賦值函數(shù),將參數(shù)a賦給m_a。{對于內(nèi)部數(shù)據(jù)類型的數(shù)據(jù)成員而言,兩種初始化方式的效率幾乎沒有區(qū)別,但后者的程序版式似乎更清晰些。若類F的聲明如下:F(intx,inty);//構(gòu)造函數(shù)}示例9-2(c)中F的構(gòu)造函數(shù)采用了第一種初始化方式,示例9-2(d)中F的構(gòu)造函數(shù)采用了第二種初始化方式。{}{9.3構(gòu)造和析構(gòu)的次序構(gòu)造從類層次的最根處開始,在每一層中,首先調(diào)用基類的構(gòu)造函數(shù),然后調(diào)用成員對象的構(gòu)造函數(shù)。析構(gòu)則嚴格按照與構(gòu)造相反的次序執(zhí)行,該次序是唯一的,否則編一個有趣的現(xiàn)象是,成員對象初始化的次序完全不受它們只由成員對象在類中聲明的次序決定。這是因為類的聲明是唯一的,而類的構(gòu)造函數(shù)可以有多個,因此會有多個不同次序的初始化表。如果成員對象按照初始化表的次序進行構(gòu)造,這將導(dǎo)致析構(gòu)函數(shù)無法得到唯一的逆序。[Eckel,p29.4示例:類String的構(gòu)造函數(shù)與析構(gòu)函數(shù)//String的普通構(gòu)造函數(shù){{}{//String的析構(gòu)函數(shù){//由于m_data是內(nèi)部數(shù)據(jù)類型,也可以寫成deletem_data;}由于并非所有的對象都會使用拷貝構(gòu)造函數(shù)和賦值函數(shù),程序員可能對這兩個函數(shù)有些輕視。請先記住以下的警告,在閱讀正文時就會多心:◆本章開頭講過,如果不主動編寫拷貝構(gòu)造函數(shù)和賦值函數(shù),編譯器將以“位拷貝”的方式自動生成缺省的函數(shù)。倘若類中含有指針變量,那么這兩個缺省的函數(shù)就隱含了錯誤。以類String的兩個對象a,b為例,假設(shè)a.m_data的內(nèi)容為“hello”,b.m_data的內(nèi)容為“world”?,F(xiàn)將a賦給b,缺省賦值函數(shù)的“位拷貝”意味著執(zhí)行b.m_data=a.m_data。這將造成三個錯誤:一是b.m_data原有的內(nèi)存沒被釋放,造成內(nèi)存泄露;二是◆拷貝構(gòu)造函數(shù)和賦值函數(shù)非常容易混淆,常導(dǎo)致錯寫、錯用。拷貝構(gòu)造函數(shù)是在對象被創(chuàng)建時調(diào)用的,而賦值函數(shù)只能被已經(jīng)存在了的對象調(diào)用。以下程序中,第三個語句和第四個語句很相似,你分得清楚哪個調(diào)用了拷貝構(gòu)造函數(shù),哪個調(diào)用了賦Stringc=a;//調(diào)用了拷貝構(gòu)造函數(shù),最好寫成c(a);c=b;//調(diào)用了賦值函數(shù)本例中第三個語句的風(fēng)格較差,宜改寫成Stringc(a)以區(qū)別于第四個語句。//拷貝構(gòu)造函數(shù){//允許操作other的私有成員m_data//賦值函數(shù){//(1)檢查自賦值//(2)釋放原有的內(nèi)存資源//(3)分配新的內(nèi)存資源,并復(fù)制內(nèi)容//(4)返回本對象的引用}類String拷貝構(gòu)造函數(shù)與普通構(gòu)造函數(shù)(參見9.4節(jié))的區(qū)別是:在函數(shù)入口處無需與NULL進行比較,這是因為“引用”不可能是NULL,而“指針”可以為NULL。類String的賦值函數(shù)比構(gòu)造函數(shù)復(fù)雜得多,分四(1)第一步,檢查自賦值。你可能會認為多此一舉,難道有人會愚蠢到寫出a=a這樣的自賦值語句!的確不會。但是間接的自賦值仍有可能出現(xiàn),例如己而已,反正不會出錯!”意不要將return*this錯寫成returnthis。那么能否寫成returnother呢?效果9.7偷懶的辦法處理拷貝構(gòu)造函數(shù)與賦值函數(shù)A(constA&a);//私有的拷貝構(gòu)造函數(shù)A&operate=(constA9.8如何在派生類中實現(xiàn)類的基本函數(shù)◆基類與派生類的析構(gòu)函數(shù)應(yīng)該為虛(即加virtual關(guān)鍵字)。例如{virtual~Base(){cout<<"~Base"<<endl;}{virtual~Derived(){cout<<"~Derived"<<endl輸出結(jié)果為:如果析構(gòu)函數(shù)不為虛,那么輸出結(jié)果為◆在編寫派生類的賦值函數(shù)時,注意不要忘記對基類的數(shù)據(jù)成員重新賦值。例如:{Base&operate=(constBase&other);//類Base的賦值函數(shù)classDerived:public{Derived&operate=(constDerived&other);//類Derived的賦值函數(shù)Derived&Derived::operate=(constDerived&other){//(1)檢查自賦值//(2)對基類的數(shù)據(jù)成員重新賦值Base::operate=(other);//因為不能直接操作私有數(shù)據(jù)成員//(3)對派生類的數(shù)據(jù)成員賦值//(4)返回本對象的引用9.9一些心得體會有些C++程序設(shè)計書籍稱構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值函數(shù)是類的“Big-Three”,它們的確是任何類最重要的函數(shù),不容輕視。也許你認為本章的內(nèi)容已經(jīng)夠多了,學(xué)會了就能平安無事,我不能作這個保證。如果你希望吃透“Big-Three”,請好好閱讀參考文獻[Cline][Meyers][Murry]。第10章類的繼承與組合對象(Object)是類(Class)的一個實例(Instance)。如果將對象比作房子,那么類就是房子的設(shè)計圖紙。所以面向?qū)ο笤O(shè)計的重點是類的設(shè)計,而不是對象的設(shè)計。對于C++程序而言,設(shè)計孤立的類是比較容易的,難的是正確設(shè)計基類及其派生類。本章僅僅論述“繼承”(Inheritance)和“組合”(Composition)注意,當(dāng)前面向?qū)ο蠹夹g(shù)的應(yīng)用熱點是COM和CORBA,這些內(nèi)容超出了C++教材的范疇,請閱讀COM和CORBA相關(guān)論著。{}這個簡單的示例程序說明了一個事實:C++的“繼承”特性可以提高程序的可復(fù)用●【規(guī)則10-1-1】如果類A和類B毫不相關(guān),不可以為了使B的功能更多些而讓B繼承A的功能和屬性。不要覺得“白吃白不吃”,讓一個好端端的健壯青年無緣無●【規(guī)則10-1-2】若在邏輯上B是A的“一種”(akindof),則允許B繼承A的功能和屬性。例如男人(Man)是人(Human)的一種,男孩(Boy)是男人的一種。那么類Man可以從類Human派生,類Boy可以從類Man派生?!糇⒁馐马棥疽?guī)則10-1-2】看起來很簡單,但是實際應(yīng)用時可能會有意外,繼承的概念在程序世界與現(xiàn)實世界并不完全相同。例如從生物學(xué)角度講,鴕鳥(Ostrich)是鳥(Bird)的一種,按理說類Ostrich應(yīng)該可以從類Bird派生。但是鴕鳥不能飛,那么Ostrich::Fly是什么東西?例如從數(shù)學(xué)角度講,圓(Circle)是一種特殊的橢圓(Ellipse),按理說類Circle應(yīng)該可以從類Ellipse派生。但是橢圓有長軸和短軸,如果圓繼承了橢圓的長軸和短軸,豈10.2組合{{{{Listen(void){m_ear.LiSmell、Eat、Listen這些功能。示例10-2-2十分簡短并且運行正確,但是這種設(shè)計方法卻是不對的。classHead:publicEye,publicNose,publicMouth,publicE一只公雞使勁地追打一只剛下了蛋的母雞,你知道為什么嗎?因為母雞下了鴨蛋。很多程序員經(jīng)不起“繼承”的誘惑而犯下設(shè)計錯誤。“運行正確”的程序不見得是高質(zhì)量的程序,此處就是一個例證。第11章其它編程經(jīng)驗11.1使用const提高函數(shù)的健壯性11.1.1用const修飾函數(shù)的參數(shù)對于非內(nèi)部數(shù)據(jù)類型的輸入?yún)?shù),應(yīng)該將“值傳遞”的方式改為遞”,目的是提高效率。例如將voidFunc(Aa)改為voidFunc(constA&a)。表11-1-1“const&”修飾輸入?yún)?shù)的規(guī)則11.1.2用const修飾函數(shù)的返回值◆如果給以“指針傳遞”方式的函數(shù)返回值加const修飾,那么函數(shù)返回值(即指針)的內(nèi)容不能被修改,該返回值只能被賦給加const修飾的同類型指針?!羧绻瘮?shù)返回值采用“值傳遞方式”,由于函數(shù)會把返回值復(fù)制到外部臨時的存儲單元中,加const修飾沒有任何價值。例如不要把函數(shù)intGetInt(void)寫成constintGetInt(void)。同理不要把函數(shù)AGetA(void)寫成constAGetA(void),其中A為用戶自定義的如果返回值不是內(nèi)部數(shù)據(jù)類型,將函數(shù)AGetA(void)改寫為constA&GetA(void)的確能提高效率。但此時千萬千萬要小心,一定要搞清楚函數(shù)究竟是想返回一個對象的“拷貝”還是僅返回“別名”就可以了,否則程序會出錯。見6.2節(jié)“返回值的規(guī)則”。◆函數(shù)返回值采用“引用傳遞”的場合并不多,這種方式一般只出現(xiàn)在類的賦值函數(shù)Aa,b,c;//a,b,c為A的對象a=b=c;//正常的鏈式賦值(a=b)=c//不正常的鏈式賦值,但合法如果將賦值函數(shù)的返回值加const修飾,那么該返回值的內(nèi)容不允許被改動。上例中,語句a=b=c仍然正確,但是語句(a=b)=c則是非法的。任何不會修改數(shù)據(jù)成員的函數(shù)都應(yīng)該聲明為const類型。如果在編寫const成員函數(shù)時,不慎修改了數(shù)據(jù)成員,或者調(diào)用了其它非const成員函數(shù),編譯器將指出錯誤,以下程序中,類stack的成員函數(shù)GetCount僅用于計數(shù),從邏輯上講GetCount應(yīng)當(dāng){intGetCount(void)const;//const {++m_num;//編譯錯誤,企圖修改數(shù)據(jù)成員m_numPop();//編譯錯誤,企圖調(diào)用非const函數(shù)}const成員函數(shù)的聲明看起來怪怪的:const關(guān)鍵字只能放在函數(shù)聲明的尾部,大概11.2提高程序的效率程序的時間效率是指運行速度,空間效率是指程序占用內(nèi)存或者外存的狀況。全局效率是指站在整個系統(tǒng)的角度上考慮的效率,局部效率是指站在模塊或函數(shù)角●【規(guī)則11-2-1】不要一味地追求程序的效率,應(yīng)當(dāng)在滿足正確性、可靠性、健壯性、可讀性等質(zhì)量因素的前提下,設(shè)法提高程序的效率。11.3一些有益的建議我們經(jīng)常會把“==”誤寫成“=”,象“IⅡ”、“&&”、“<=”、“>=”這類符號也很
【建議11-3-2】變量(指針、數(shù)組)被創(chuàng)建之后應(yīng)當(dāng)及時把它們初始化,以防止把[Eckel]BruceEckel,ThinkinginC++[Maguire]SteveMaguire,WritingCleanCode(編程精粹,姜靜波等譯),電子工業(yè)出版[Summit]SteveSummit,CProgrammingFAQs,Addison-Wesl審查項結(jié)論頭文件是否使用了ifndef/define/endif預(yù)處理塊?頭文件中是否只存放“聲明”而不存放“定義”審查項結(jié)論“{”和“}”是否各占一行并且對齊于同一列?If、for、while、do等語句自占一行,不論執(zhí)行語句多類結(jié)構(gòu)的public,protected,private順序是否在所有的審查項結(jié)論.審查項結(jié)論(1)將布爾變量直接與TRUE、FALSE或者1、0進(2)將浮點變量用“==”或“!=”與任何數(shù)字比較。(3)將指針變量用“==”或“!=”與NULL比較。如果循環(huán)體內(nèi)存在邏輯判斷,并且循環(huán)次數(shù)很大,是是否忘記寫switch的default分支?審查項結(jié)論是否使用含義直觀的常量來表示那些將在程序中多審查項結(jié)論函數(shù)名字與返回值類型在語義上是否沖突?當(dāng)用輸出參數(shù)獲得,而錯誤標志用retur在函數(shù)體的“入口處”,是否用asser使用濫用了assert?例如混淆非法情況與錯誤情return語句是否返回指向“棧內(nèi)存”“引用”?wheneveryouneed”審查項結(jié)論值是否為NULL?(防止使用指針值為NULL的內(nèi)存)是否忘記為數(shù)組和動態(tài)內(nèi)存賦初值?(防止將未被初始化的內(nèi)存作為右值使用)動態(tài)內(nèi)存的申請與釋放是否配對?(防止內(nèi)存泄漏)(2)用free或delete釋放了內(nèi)存之后,忘記將指針是否將malloc/free和new/delete混淆使用?malloc語句是否正確無誤?例如字節(jié)數(shù)是在創(chuàng)建與釋放動態(tài)對象數(shù)組時,new/de審查項結(jié)論是否濫用內(nèi)聯(lián)函數(shù)?例如函數(shù)體內(nèi)的代碼比較長,函是否用內(nèi)聯(lián)函數(shù)取代了宏代碼?審查項結(jié)論是否違背編程規(guī)范而讓C++編譯器自動為類產(chǎn)生四個缺省的函數(shù):(1)缺省的無參數(shù)構(gòu)造函數(shù);(2)缺省的拷貝構(gòu)造函數(shù);(3)缺省的析構(gòu)函數(shù);(4)缺省的賦值函數(shù)。構(gòu)造函數(shù)中是否遺漏了某些初始化工作?是否正確地使用構(gòu)造函數(shù)的初始化表?是否錯寫、錯用了拷貝構(gòu)造函數(shù)和賦值函數(shù)?放原有內(nèi)存資源;(3
溫馨提示
- 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. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 關(guān)于夫妻雙方離婚協(xié)議書
- 土地租賃合同雙方協(xié)議書七篇
- 2025無財產(chǎn)離婚協(xié)議書
- 面神經(jīng)炎病因介紹
- 錯構(gòu)瘤病因介紹
- 蕁麻疹病因介紹
- 11化學(xué)中考真題匯編《氧氣的性質(zhì)》及答案
- (2024)乳制品加工項目可行性研究報告寫作范本(一)
- 2024-2025學(xué)年人教版八年級英語上學(xué)期期末真題 專題01 單項選擇(安徽專用)
- 2023年耐磨劑項目融資計劃書
- 立式儲罐課課程設(shè)計
- 吉林省長春市南關(guān)區(qū)2024-2025學(xué)年八年級上學(xué)期10月期中考試語文試題(含答案)
- DB11T 1194-2015 高處懸吊作業(yè)企業(yè)安全生產(chǎn)管理規(guī)范
- 11.5 歌曲《賣報歌》課件(14張)
- 學(xué)校微課制作校本培訓(xùn)方案
- Lesson 6 I can see with my eyes. 單元整體(教學(xué)設(shè)計)-2024-2025學(xué)年接力版英語四年級上冊
- 20S515 鋼筋混凝土及磚砌排水檢查井
- 醫(yī)院重點監(jiān)控藥品管理制度
- 崗前安全培訓(xùn)試題及參考答案【奪分金卷】
- 【課件】擁有積極的人生態(tài)度 2024-2025學(xué)年統(tǒng)編版道德與法治七年級上冊
- 九年級下冊孔乙己課文原文及解讀
評論
0/150
提交評論