高質(zhì)量C++編程指南_第1頁(yè)
高質(zhì)量C++編程指南_第2頁(yè)
高質(zhì)量C++編程指南_第3頁(yè)
高質(zhì)量C++編程指南_第4頁(yè)
高質(zhì)量C++編程指南_第5頁(yè)
已閱讀5頁(yè),還剩175頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

高質(zhì)量C++/C編程指南文件狀態(tài)[]草稿文件[]更改正式文件文件標(biāo)識(shí):林銳博士完成日期: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對(duì)齊 2.5長(zhǎng)行拆分 2.6修飾符的位置 202.8類的版式 21 223.1共性規(guī)則 223.2簡(jiǎn)單的WINDOws應(yīng)用程序命 25 264.1運(yùn)算符的優(yōu)先級(jí) 26 27 274.4循環(huán)語(yǔ)句的效率 29 335.1為什么需要常量 5.3常量定義規(guī)則 5.4類中的常量 366.1參數(shù)的規(guī)則 6.2返回值的規(guī)則 6.3函數(shù)內(nèi)部實(shí)現(xiàn)的規(guī)則 406.5使用斷言 416.6引用與指針的比較 42 47.1內(nèi)存分配方式 447.2常見(jiàn)的內(nèi)存錯(cuò)誤及其對(duì)策 447.3指針與數(shù)組的對(duì)比 457.4指針參數(shù)是如何傳遞內(nèi)存的? 7.6動(dòng)態(tài)內(nèi)存會(huì)被自動(dòng)釋放嗎? 7.7杜絕“野指針” 7.9內(nèi)存耗盡怎么辦? 7.12一些心得體會(huì) 56 578.1函數(shù)重載的概念 8.3參數(shù)的缺省值 8.4運(yùn)算符重載 8.5函數(shù)內(nèi)聯(lián) 8.6一些心得體會(huì) 第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如何在派生類中實(shí)現(xiàn)類的基本函數(shù) 9.9一些心得體會(huì) 第10章類的繼承與組合 78 第11章其它編程經(jīng)驗(yàn) 82 84 88附錄B:C++/C試題 93 前捏造的C++/C程序怎么會(huì)有那么多的毛病?”(10)真正的程序員不會(huì)在上午9:00到下午5:00之間工作,如果你看到我開(kāi)發(fā)的軟件都與科研相關(guān)(集成電路CAD和3D圖形學(xué)領(lǐng)域),(1)編程風(fēng)格;(2)出錯(cuò)處理;(3)算法復(fù)雜度分析(用于提高性能)。(1)編程老手可能會(huì)長(zhǎng)期用隱含錯(cuò)誤的方式編程(習(xí)慣成自然),發(fā)現(xiàn)毛病后都不愿相序員的編程技能,質(zhì)量合格率大約是10%。很少有人能夠?qū)懗鐾耆腺|(zhì)量要求的if(1)知錯(cuò)就改;(2)經(jīng)常溫故而知新;(3)堅(jiān)持學(xué)習(xí),天天向上。(3)如果你考出85分以上的好成績(jī),你有義務(wù)和資格為你所在的團(tuán)隊(duì)作“C++/C編程”預(yù)計(jì)到2002年7月,我們將建立切合中國(guó)國(guó)情的CMMI3級(jí)解決方案。屆時(shí),包括林銳,2001年7月第1章文件結(jié)構(gòu)稱為頭文件。另一個(gè)文件用于保存程序的實(shí)現(xiàn)(implementation),稱為定義(definition)的定義文件通常以“.cpp”為后綴(也有一些系統(tǒng)以“.cc”或“.cxx”為后綴)。1.1版權(quán)和版本的聲明版權(quán)和版本的聲明位于頭文件和定義文件的開(kāi)頭(參見(jiàn)示例1-1),主要內(nèi)容有:(1)版權(quán)信息。(2)文件名稱,標(biāo)識(shí)符,摘要。(3)當(dāng)前版本號(hào),作者/修改者,完成日期。(4)版本歷史信息。*Copyright(c)2001,上海貝爾有限公司網(wǎng)絡(luò)應(yīng)用事業(yè)部**文件標(biāo)識(shí):見(jiàn)配置管理計(jì)劃書(shū)*摘要:簡(jiǎn)要描述本文件的內(nèi)容**當(dāng)前版本:1.1*作者:輸入作者(或修改者)名字*完成日期:2001年7月20日**取代版本:1.0*原作者:輸入原作者(或修改者)名字*完成日期:2001年5月10日1.2頭文件的結(jié)構(gòu)(1)頭文件開(kāi)頭處的版權(quán)和版本聲明(參見(jiàn)示例1-1)。(2)預(yù)處理塊。(3)函數(shù)和類結(jié)構(gòu)聲明等。

【建議1-2-1】頭文件中只存放“聲明//版權(quán)和版本聲明見(jiàn)示例1-1,此處省略。#ifndefGRAPHICS_H//防止graphics.h被#include“myheader.h”voidFunction1(…);//{//類結(jié)構(gòu)聲明//版權(quán)和版本聲明見(jiàn)示例1-1,此處省略。//全局函數(shù)的實(shí)現(xiàn)體{//類成員函數(shù)的實(shí)現(xiàn)體1.4頭文件的作用(1)通過(guò)頭文件來(lái)調(diào)用庫(kù)功能。在很多場(chǎng)合,源代碼不便(或不準(zhǔn))向用戶1.5目錄結(jié)構(gòu)第2章程序的版式空行起著分隔程序段落的作用??招械皿w(不過(guò)多也不過(guò)少)將使程序的布局更加{}}{}{{}{}}2.2代碼行示例2-2(a)為風(fēng)格良好的代碼行,示例2-2(b)intheight;//高度intwidth,height,depth;//寬度高度深度}for(initialization;con{}for(initialization;con

【建議2-2-1】盡可能在定義變量的同時(shí)初始化該變量(就近原則)intheight=10;//定義并初紿化height2.3代碼行內(nèi)的空格掉一些空格,如for(i=0;i<10;i++)//不要寫(xiě)成array[5]=0;//不要寫(xiě)成a.Function();//不要寫(xiě)成b->Function();示例2-3代碼行內(nèi)的空格2.4對(duì)齊●【規(guī)則2-4-1】程序的分界符‘{’和‘}'應(yīng)獨(dú)占一行并且位于同一列,同時(shí)與引用●【規(guī)則2-4-2】{}之內(nèi)的代碼塊在‘{’右邊數(shù)格處左對(duì)齊。示例2-4(a)為風(fēng)格良好的對(duì)齊,示例2-4(b)為風(fēng)格不良的對(duì)齊。{}}{}{}}for(initialization;cond{}for(initialization;condi}{}}2.5長(zhǎng)行拆分&&(very_longer_variable3<=very_longer_variabl&&(very_longer_variable5<=very_longer_variable{}{}2.6修飾符的位置修飾符*和&應(yīng)該靠近數(shù)據(jù)類型還是該靠近變量名,是個(gè)有爭(zhēng)議的活題?!瘛疽?guī)則2-6-1】應(yīng)當(dāng)將修飾符*和&緊靠變量名2.7注釋(1)版本、版權(quán)聲明;(2)函數(shù)接口說(shuō)明;(3)重要的代碼行或段落提示。i++;//i加1,多余的注釋{}{2.8類的版式的接口(或服務(wù))。{}floatfX,fY,fz;//前綴f表示float類型程序中的英文單詞一般不會(huì)太復(fù)雜,用詞應(yīng)當(dāng)準(zhǔn)確。例如不要把CurrentValue寫(xiě)成幾十年前老ANSIC規(guī)定名字不準(zhǔn)超過(guò)6個(gè)字符,現(xiàn)今的C++/C不再有此限制。一怪。那么名字是否越長(zhǎng)約好?不見(jiàn)得!例如變量名maxval就比maxValueUntil0verflow●【規(guī)則3-1-4】程序中不要出現(xiàn)僅靠大小寫(xiě)區(qū)分的相似的標(biāo)識(shí)符。intx,X;//變量x與X容易混淆●【規(guī)則3-1-5】程序中不要出現(xiàn)標(biāo)識(shí)符完全相同的局部變量和全局變量,盡管兩者的作用域不同而不會(huì)發(fā)生語(yǔ)法錯(cuò)誤,但會(huì)使人誤解?!瘛疽?guī)則3-1-7】全局函數(shù)的名字應(yīng)當(dāng)使用“動(dòng)詞”或者“動(dòng)詞十名詞”(動(dòng)賓詞組)。類的成員函數(shù)應(yīng)當(dāng)只使用“動(dòng)詞”,被省略掉的名詞就是對(duì)象本身。//類的成員函數(shù)●【規(guī)則3-1-8】用正確的反義詞組命名具有互斥意義的變量或相反動(dòng)作的函數(shù)等。

【建議3-1-1】盡量避免名字中出現(xiàn)數(shù)字編號(hào),如Valuel,Value2等,除非邏輯上的確需要編號(hào)。這是為了防止程序員偷懶,不肯為命名動(dòng)腦筋而導(dǎo)致產(chǎn)生無(wú)意義的名字(因?yàn)橛脭?shù)字編號(hào)最省事)。3.2簡(jiǎn)單的Windows應(yīng)用程序命名規(guī)則作者對(duì)“匈牙利”命名規(guī)則做了合理的簡(jiǎn)化,下述的命名規(guī)則簡(jiǎn)單易用,比較適合于Windows應(yīng)用軟件的開(kāi)發(fā)?!瘛疽?guī)則3-2-1】類名和函數(shù)名用大寫(xiě)字母開(kāi)頭的單詞組合而成。classNode;//類名classLeafNode;//類名voidDraw(void);//函數(shù)名●【規(guī)則3-2-2】變量和參數(shù)用小寫(xiě)字母開(kāi)頭的單詞組合而成。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開(kāi)頭,所有常量(或宏定義)均以GL開(kāi)頭。3.3簡(jiǎn)單的Unix應(yīng)用程序命名規(guī)則第4章表達(dá)式和基本語(yǔ)句讀者可能懷疑:連if、for、while、goto、switch這樣簡(jiǎn)單的東西也要探討編程風(fēng)格,是不是小題大做?我真的發(fā)覺(jué)很多程序員用隱含錯(cuò)誤的方式寫(xiě)表達(dá)式和基本語(yǔ)句,我自己也犯過(guò)類似表達(dá)式和語(yǔ)句都屬于C++/C的短語(yǔ)結(jié)構(gòu)語(yǔ)法。它們看似簡(jiǎn)單,但使用時(shí)隱患比較多。本章歸納了正確使用表達(dá)式和語(yǔ)句的一些規(guī)則與建議。C++/C語(yǔ)言的運(yùn)算符有數(shù)十個(gè),運(yùn)算符的優(yōu)先級(jí)與結(jié)合律如表4-1所示。注意一元運(yùn)算符+-*的優(yōu)先級(jí)高于對(duì)應(yīng)的二元運(yùn)算符。優(yōu)先級(jí)結(jié)合律從左至右從右至左從左至右十從左至右從左至右從左至右從左至右&從左至右A從左至右從左至右從左至右Ⅱ從右至左從右至左從左至右●【規(guī)則4-1-1】如果代碼行中的運(yùn)算符比較多,用括號(hào)確定表達(dá)式的操作順序,避免由于將表4-1熟記是比較困難的,為了防止產(chǎn)生歧義并提高可讀性,應(yīng)當(dāng)用括號(hào)確4.2復(fù)合表達(dá)式4.3if語(yǔ)句4.3.1布爾變量與零值比較●【規(guī)則4-3-1】不可將布爾變量直接與TRUE、FALSE或者1、0進(jìn)行比較。if(!flag)//表示flag為假其它的用法都屬于不良風(fēng)格,例如:if(flagif(flag4.3.2整型變量與零值比較●【規(guī)則4-3-2】應(yīng)當(dāng)將整型變量用“==”或“!=”直接與0比較。假設(shè)整型變量的名字為value,它與零值比較的標(biāo)準(zhǔn)if語(yǔ)句如下:不可模仿布爾變量的風(fēng)格而寫(xiě)成if(value)//會(huì)讓人誤解value是布爾變量4.3.3浮點(diǎn)變量與零值比較●【規(guī)則4-3-3】不可將浮點(diǎn)變量用“==”或“!=”與任何數(shù)字比較。千萬(wàn)要留意,無(wú)論是float還是double類型的變量,都有精度限制。所以一定要避免將浮點(diǎn)變量用“==”或“!=”與數(shù)字比較,應(yīng)該設(shè)法轉(zhuǎn)化成“>=”或“<=”形式。假設(shè)浮點(diǎn)變量的名字為x,應(yīng)當(dāng)將if(x==0.0)//隱含錯(cuò)誤的比較轉(zhuǎn)化為if((x>=-EPSINON)&&(x<其中EPSINON是允許的誤差(即精度)。4.3.4指針變量與零值比較●【規(guī)則4-3-4】應(yīng)當(dāng)將指針變量用“==”或“!=”與NULL比較。指針變量的零值是“空”(記為NULL)。盡管NULL的值與0相同,但是兩者意義不同。假設(shè)指針變量的名字為p,它與零值比較的標(biāo)準(zhǔn)if語(yǔ)句如下:if(p==NULL)//p與NULL顯式比較,強(qiáng)調(diào)p是指針變量不要寫(xiě)成if(p==0)//容易讓人誤解p是整型變量或者if(p)//容易讓人誤解p是布爾變量4.3.5對(duì)if語(yǔ)句的補(bǔ)充說(shuō)明有時(shí)候我們可能會(huì)看到if(NULL==p)這樣古怪的格式。不是程序?qū)戝e(cuò)了,是程序員為了防止將if(p==NULL)誤寫(xiě)成if(p=NULL),而有意把p和NULL顛倒。編譯器認(rèn)為if(p=NULL)是合法的,但是會(huì)指出if(NULL=p)是錯(cuò)誤的,因?yàn)镹ULL程序中有時(shí)會(huì)遇到if/else/return的組合,應(yīng)該將如下不良風(fēng)格的程序{}{}或者改寫(xiě)成更加簡(jiǎn)練的4.4循環(huán)語(yǔ)句的效率C++/C循環(huán)語(yǔ)句中,for語(yǔ)句使用頻率最高,重點(diǎn)論述循環(huán)體的效率。提高循環(huán)體效率的基本辦法是降低循環(huán)體的復(fù)雜性?!瘛窘ㄗh4-4-1】在多重循環(huán)中,如果有可能,應(yīng)當(dāng)將最長(zhǎng)的循環(huán)放在最內(nèi)層,最短的循環(huán)放在最外層,以減少CPU跨切循環(huán)層的次數(shù)。例如示例4-4(b)的效率比示例4-4(a)的高。{}{}示例4-4(a)低效率:長(zhǎng)循環(huán)在最外層示例4-4(b)高效率:長(zhǎng)循環(huán)在最內(nèi)層●【建議4-4-2】如果循環(huán)體內(nèi)存在邏輯判斷,并且循環(huán)次數(shù)很大,宜將邏輯判斷移到}{}{}表4-4(c)效率低但程序簡(jiǎn)潔表4-4(d)效率高但程序不簡(jiǎn)潔示例4-5(a)中的x值屬于半開(kāi)半閉區(qū)間“0=<x<N”,起點(diǎn)到終點(diǎn)的間隔為N,循示例4-5(b)中的x值屬于閉區(qū)間“0=<x<=N-1”,起點(diǎn)到終點(diǎn)的間隔為N-1,循{{}switch是多分支選擇語(yǔ)句,而if語(yǔ)句只有兩個(gè)分支可供選擇。雖然可以用嵌套的if語(yǔ)句來(lái)實(shí)現(xiàn)多分支選擇,但那樣的程序冗長(zhǎng)難讀。這是switch語(yǔ)句存在的理由。switch語(yǔ)句的基本格式是:{}●【規(guī)則4-6-1】每個(gè)case語(yǔ)句的結(jié)尾不要忘了加break,否則將導(dǎo)致多個(gè)分支重疊(除非有意使多個(gè)分支重疊)。也應(yīng)該保留語(yǔ)句default:break;這樣做并非多此一舉,而是為了防止別人誤以為你忘了default處理。自從提倡結(jié)構(gòu)化設(shè)計(jì)以來(lái),goto就成了有爭(zhēng)議的語(yǔ)句。首先,由于goto語(yǔ)句可以靈活跳轉(zhuǎn),如果不加限制,它的確會(huì)破壞結(jié)構(gòu)化設(shè)計(jì)風(fēng)格。其次,goto語(yǔ)句經(jīng)常帶來(lái)錯(cuò)誤或隱患。它可能跳過(guò)了某些對(duì)象的構(gòu)造、變量的初始化、重要的計(jì)算等語(yǔ)句,例如:intsum=0;//被goto跳過(guò)如果編譯器不能發(fā)覺(jué)此類錯(cuò)誤,每用一次goto語(yǔ)句都可能留下隱患。很多人建議廢除C++/C的goto語(yǔ)句,以絕后患。但實(shí)事求是地說(shuō),錯(cuò)誤是程序員自己造成的,不是goto的過(guò)錯(cuò)。goto語(yǔ)句至少有一處可顯神通,它能從多重循環(huán)體中咻地一下子跳到外面,用不著寫(xiě)很多次的break語(yǔ)句;例如為宏常量)。C++語(yǔ)言除了#define外還可以用const來(lái)定義常量(稱為const常量)。constfloatPI5.3常量定義規(guī)則5.4類中的常量有時(shí)我們希望某些常量只在類中有效。由于#define定義的宏常量是全局的,不能達(dá)到目的,于是想當(dāng)然地覺(jué)得應(yīng)該用const修飾數(shù)據(jù)成員來(lái)實(shí)現(xiàn)。const數(shù)據(jù)成員的確是存在的,但其含義卻不是我們所期望的。const數(shù)據(jù)成員只在某個(gè)對(duì)象生存期內(nèi)是常量,而對(duì)于整個(gè)類而言卻是可變的,因?yàn)轭惪梢詣?chuàng)建多個(gè)對(duì)象,不同的對(duì)象其const數(shù)不能在類聲明中初始化const數(shù)據(jù)成員。以下用法是錯(cuò)誤的,因?yàn)轭惖膶?duì)象未被創(chuàng)建時(shí),編譯器不知道SIZE的值是什么。constintSIZE=100;//錯(cuò)誤,企圖在類聲明中初始化const數(shù)據(jù)成員intarray[SIZE];//錯(cuò)誤,未知的SIZEconst數(shù)據(jù)成員的初始化只能在類構(gòu)造函數(shù)的初始化表中進(jìn)行,例如A(intsize);//構(gòu)造函數(shù)A::A(intsize):SIZE(size)//構(gòu)造函數(shù)的初始化表{Aa(100);//對(duì)象a的SIZE值為100Ab(200);//對(duì)象b的SIZE值為200怎樣才能建立在整個(gè)類中都恒定的常量呢?別指望const數(shù)據(jù)成員了,應(yīng)該用類中的枚舉常量來(lái)實(shí)現(xiàn)。例如第6章函數(shù)設(shè)計(jì)voidSetValue(int,int);//不良的風(fēng)格floatGetValue(void);//良好的風(fēng)格floatGetValue();//不良的風(fēng)格6.2返回值的規(guī)則由于c是char類型,取值范圍是[-128,127],如果宏EOF的值在char的取值范圍(通常為負(fù)1)。因此函數(shù)getchar就成了int類型。函數(shù)getchar可以改寫(xiě)成B00LGeintlength=strlen(strcpy(str,“Hello//賦值函數(shù)//相加函數(shù),如果沒(méi)有friend修飾則只許有一個(gè)右側(cè)參數(shù)}return*this;//返回的是*this的引用,無(wú)需拷貝過(guò)程a=b;;//如果用“值傳遞”,將產(chǎn)生一次*this拷貝a=b=c;//如果用“值傳遞”,將產(chǎn)生兩次*this拷貝{deletetemtemp.data=newchar[strlen(s1}{}(3)如果函數(shù)返回值是一個(gè)對(duì)象,要考慮return語(yǔ)句6.4其它建議static局部變量是函數(shù)的“記憶”存儲(chǔ)器。建議盡量少用static局部變量,除非必需。

【建議6-4-4】不僅要檢查輸入?yún)?shù)的有效性,還要檢查通過(guò)其它途徑進(jìn)入函數(shù)體內(nèi)的變量的有效性,例如全局變量、文件句柄等。

【建議6-4-5】用于出錯(cuò)處理的返回值一定要清楚,讓使用者不容易忽視或誤解錯(cuò)誤6.5使用斷言程序一般分為Debug版本和Release版本,De斷言assert是僅在Debug版本起作用的宏,它用于檢查“不應(yīng)該”發(fā)生的情況。示例6-5是一個(gè)內(nèi)存復(fù)制函數(shù)。在運(yùn)行過(guò)程中,如果assert的參數(shù)為假,那么程序就會(huì)中止(一般地還會(huì)出現(xiàn)提示對(duì)話,說(shuō)明在什么地方引發(fā)了assert)。void*memcpy(void*pvTo,const{byte*pbTo=(byte*)pvTo;//防止改變pbyte*pbFrom=(byte*)pvFrom;//防止改變pvFrom的地址assert不是一個(gè)倉(cāng)促拼湊起來(lái)的宏。為了不在程序的Debug版本和Release版本引起差別,assert不應(yīng)該產(chǎn)生任何副作用。所以a看成一個(gè)在任何系統(tǒng)狀態(tài)下都可以安全使用的無(wú)害測(cè)試手段。如果程序在assert處終止很少有比跟蹤到程序的斷言,卻不知道該斷言的作用更讓人沮喪的事了。你化了很多時(shí)間,不是為了排除錯(cuò)誤,而只是為了弄清楚這個(gè)錯(cuò)誤到底是什么。有的時(shí)候,程序員偶爾還會(huì)設(shè)計(jì)出有錯(cuò)誤的斷言。所以如果搞不清楚斷言檢查的是什么,就很難判斷錯(cuò)誤是出現(xiàn)在程序中,還是出現(xiàn)在斷言中。幸運(yùn)的是這個(gè)問(wèn)題很好解決,只要加上清晰的看到樹(shù)上釘著一塊“危險(xiǎn)”的大牌子。但危險(xiǎn)到底是什么?樹(shù)要倒?有廢井?有野獸?除非告訴人們“危險(xiǎn)”是什么,否則這個(gè)警告牌難以起到積極有效的作用。難以理解的斷言常常被程序員忽略,甚至被刪除。[Maguire,p8-p30]6.6引用與指針的比較(2)不能有NULL引用,引用必須與合法的存儲(chǔ)單元關(guān)聯(lián)(指針則可以是NULL)。用,只是把k的值改變成為6。由于k是intj=6;k=j;//k和i的值都變成了6;改變x的值不會(huì)影響n,所以n的值仍然是0。{以下是“指針傳遞”的示例程序。由于Func2函數(shù)體內(nèi)的x是指向外部變量n的指針,改變?cè)撝羔樀膬?nèi)容將導(dǎo)致n的值改變,所以n的值成為10。{}cout<<“n=”<<n<<endl;//n=10以下是“引用傳遞”的示例程序。由于Func3函數(shù)體內(nèi)的x是外部變量n的引用,x和n是同一個(gè)東西,改變x等于改變n,所以n的值成為10。對(duì)比上述三個(gè)示例程序,會(huì)發(fā)現(xiàn)“引用傳遞”的性質(zhì)象“指針傳遞”,而書(shū)寫(xiě)方式象這東西?指針能夠毫無(wú)約束地操作內(nèi)存中的如何東西,盡管指針功能強(qiáng)大,但是非常危險(xiǎn)。就象一把刀,它可以用來(lái)砍樹(shù)、裁紙、修指甲、理發(fā)等等,誰(shuí)敢這樣用?以免發(fā)生意外。比如說(shuō),某人需要一份證明,本來(lái)在文件上蓋上公章的印子就行了,如果把取公章的鑰匙交給他,那么他就獲得了不該有的權(quán)利。第7章內(nèi)存管理◆忘記了釋放內(nèi)存,造成內(nèi)存泄露?!翎尫帕藘?nèi)存卻繼續(xù)使用它?!瘛疽?guī)則7-2-3】避免數(shù)組或指針的下標(biāo)越界,特別要當(dāng)心發(fā)生“多1”或者“少1”示例7-3-1中,字符數(shù)組a的容量是6個(gè)字符,其內(nèi)容為hello\0。a的內(nèi)容可以改變,如a[0]=‘X’。指針p指向常量字符串“world”(位于靜態(tài)存儲(chǔ)區(qū),內(nèi)容為world\0),常量字符串的內(nèi)容是不可以被修改的。從語(yǔ)法上看,編譯器并不覺(jué)得語(yǔ)句p[0]=X’有什么不妥,但是該語(yǔ)句企圖修改常量字符串的內(nèi)容而導(dǎo)致運(yùn)行錯(cuò)誤。char*p=“world”//注意p指向常量字符串不能對(duì)數(shù)組名進(jìn)行直接復(fù)制與比較。示例7-3-2中,若想把數(shù)組a的內(nèi)容復(fù)制給數(shù)組b,不能用語(yǔ)句b=a,否則將產(chǎn)生編譯錯(cuò)誤。應(yīng)該用標(biāo)準(zhǔn)庫(kù)函數(shù)strcpy進(jìn)行復(fù)制。語(yǔ)句p=a并不能把a(bǔ)的內(nèi)容復(fù)制指針p,而是把a(bǔ)的地址賦給了p。要想復(fù)制a的內(nèi)容,可以先用庫(kù)函數(shù)malloc為p申請(qǐng)一塊容量為strlen(a)+1個(gè)字符的內(nèi)存strcpy進(jìn)行字符串復(fù)制。同理,語(yǔ)句if(p==a)比較的不是內(nèi)容而是地址,應(yīng)該用庫(kù)函//數(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");//運(yùn)行錯(cuò)誤}參數(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改寫(xiě)成示例7-4-5,會(huì)怎么樣?{}{}別看free和delete的名字惡狠狠的(尤其是delete),它們只是把指針?biāo)傅膬?nèi)存給用調(diào)試器跟蹤示例7-5,發(fā)現(xiàn)指針p被free以后其地址仍然不變(非NULL),只是free(p);//p所指的內(nèi)存被釋放,但是p{strcpy(p,“world”);//出錯(cuò)}{char*p=(char*)malloc(100);//動(dòng)態(tài)內(nèi)存會(huì)自動(dòng)釋放嗎?}示例7-6試圖讓動(dòng)態(tài)內(nèi)存自動(dòng)釋放(1)指針消亡了,并不表示它所指的內(nèi)存會(huì)被自動(dòng)釋放。(2)內(nèi)存被釋放了,并不表示指針會(huì)消亡或者成了NULL指針。7.7杜絕“野指針”(2)指針p被free或者delete之后,沒(méi)有置為NULL,讓人誤以為p是個(gè)合法的指針。{voidFunc(void){cout<<“FuncofclassA”<<{}p->Func();//p是“野指針”在創(chuàng)建的同時(shí)要自動(dòng)執(zhí)行構(gòu)造函數(shù),對(duì)象在消亡之前要自動(dòng)執(zhí)行析構(gòu)函數(shù)。由于{voidInitialize(void)voidDestroy(void){cout<<“De{Obj*a=(obj*)malloc(sizeof(obj)//...a->Destroy();//清除工作}{0bj*a=newObj;//申請(qǐng)動(dòng)態(tài)內(nèi)存并且初始化//...}示例7-8用malloc/free和new/delete如何實(shí)現(xiàn)對(duì)象的動(dòng)態(tài)內(nèi)存管理既然new/delete的功能完全覆蓋了malloc/free,為什么C++不把malloc/free淘汰出局呢?這是因?yàn)镃++程序經(jīng)常要調(diào)用C函數(shù),而C程序只能用malloc/free管理動(dòng)如果用free釋放“new創(chuàng)建的動(dòng)態(tài)對(duì)象”,那么該對(duì)象因無(wú)法執(zhí)行析構(gòu)函數(shù)而可能但是該程序的可讀性很差。所以new/delete必須配對(duì)使用,malloc/free也一樣。7.9內(nèi)存耗盡怎么辦?如果在申請(qǐng)動(dòng)態(tài)內(nèi)存時(shí)找不到足夠大的內(nèi)存塊,malloc和new將返回NULL指針,宣告內(nèi)存申請(qǐng)失敗。通常有三種方式處理“內(nèi)存耗盡”問(wèn)題。(1)判斷指針是否為NULL,如果是則馬上用return語(yǔ)句終止本函數(shù)。例如:}(2)判斷指針是否為NULL,如果是則馬上用exit(1)終止整個(gè)程序的運(yùn)行。例如:(3)為new和malloc設(shè)置異常處理函數(shù)。例如VisualC++可以用_set_new_hander函數(shù)為new設(shè)置用戶自己定義的異常處理函數(shù),也可以讓malloc享用與new相同的異常處理函數(shù)。詳細(xì)內(nèi)容請(qǐng)參考C++使用手冊(cè)。上述(1)(2)方式使用最普遍。如果一個(gè)函數(shù)內(nèi)有多處需要申請(qǐng)動(dòng)態(tài)內(nèi)存,那么方式(1)就顯得力不從心(釋放內(nèi)存很麻煩),應(yīng)該用方式(2)來(lái)處理。很多人不忍心用exit(1),問(wèn):“不編寫(xiě)出錯(cuò)處理程序,讓操作系統(tǒng)自己解決行不行?”不行。如果發(fā)生“內(nèi)存耗盡”這樣的事情,一般說(shuō)來(lái)應(yīng)用程序已經(jīng)無(wú)藥可救。如果不用exit(1)把壞程序殺死,它可能會(huì)害死操作系統(tǒng)。道理如同:如果不把歹徒擊斃,歹有一個(gè)很重要的現(xiàn)象要告訴大家。對(duì)于32位以上的應(yīng)用程序而言,無(wú)論怎樣使用malloc與new,幾乎不可能導(dǎo)致“內(nèi)存耗盡”。我在Windows98下用VisualC++測(cè)試程序,見(jiàn)示例7-9。這個(gè)程序會(huì)無(wú)休止地運(yùn)行下去,根本不會(huì)終止。因?yàn)?2位操作系統(tǒng)支持“虛存”,內(nèi)存用完了,自動(dòng)用硬盤(pán)空間頂替。我只聽(tīng)到硬盤(pán)嘎吱嘎吱地響,Window98已經(jīng)累得對(duì)鍵盤(pán)、鼠標(biāo)毫無(wú)反應(yīng)。我可以得出這么一個(gè)結(jié)論:對(duì)于32位以上的應(yīng)用程序,“內(nèi)存耗盡”錯(cuò)誤處理程序毫無(wú)用處。這下可把Unix和Windows程序員們樂(lè)壞了:反正錯(cuò)誤處理程序不起作用,我就不寫(xiě)了,省了很多麻煩。我不想誤導(dǎo)讀者,必須強(qiáng)調(diào):不加錯(cuò)誤處理將導(dǎo)致程序的質(zhì)量很差,千萬(wàn)不可因小{p=newfloat[100007.10malloc/free的使用要點(diǎn)用malloc申請(qǐng)一塊長(zhǎng)度為length的整數(shù)類型的內(nèi)存,程序如下:int*pcout<<sizeof(int)<<endl;cout<<sizeof(unsignedlong)<<endl;cout<<sizeof(float)<cout<<sizeof(double)<<voidfree(void*7.11new/delete的使用要點(diǎn){0bj*b=newObj(1);//初值為10bj*objects=newObj[100];//創(chuàng)建100個(gè)動(dòng)態(tài)對(duì)象Obj*objects=newObj[100](1);//創(chuàng)建100個(gè)動(dòng)態(tài)對(duì)象的同時(shí)賦初值1后者相當(dāng)于deleteobjects[0],漏掉了另外99個(gè)對(duì)象。7.12一些心得體會(huì)(包括我自己)。我最初學(xué)習(xí)C語(yǔ)言時(shí)特別怕指針,導(dǎo)致我開(kāi)發(fā)第一個(gè)應(yīng)用軟件(約1第8章C++函數(shù)的高級(jí)特性8.1.1重載的起源//可以改為//可以改為voidEat(Fish…);//可以改為構(gòu)造函數(shù)與類同名(請(qǐng)參見(jiàn)第9章),構(gòu)造函數(shù)只能有一個(gè)8.1.2重載是如何實(shí)現(xiàn)的?intFunction(void 如果C++程序要調(diào)用已經(jīng)被編譯后的C函數(shù),該怎么辦?extern“C”…//其它函數(shù)extern“C”#include“myheader.h”…//其它C頭文件} voidPrint(…);//成員函數(shù)}不論兩個(gè)Print函數(shù)的參數(shù)是否不同,如果類的某個(gè)成員函數(shù)要調(diào)用全局函數(shù)示例8-1-3中,第一個(gè)output函數(shù)的參數(shù)是int類型,第二個(gè)output函數(shù)的參數(shù)voidoutput(intx);//函數(shù)聲明voidoutput(floatx);//函數(shù)聲明{}{}{//output(0.5);//error!ambigu}(1)相同的范圍(在同一個(gè)類中);(2)函數(shù)名字相同;(3)參數(shù)不同;(1)不同的范圍(分別位于派生類與基類);(2)函數(shù)名字相同;(3)參數(shù)相同;(4)基類函數(shù)必須有virtual關(guān)鍵字。示例8-2-1中,函數(shù)Base::f(int)與Base::f(float)相互重載,而B(niǎo)ase::g(void){voidf(intx){cout<<"Base:virtualvoidg(void){cout<<"BasclassDerived:publi{virtualvoidg(void){{pb->g()://Derived}8.2.2令人迷惑的隱藏規(guī)則(1)如果派生類的函數(shù)與基類的函數(shù)同名,但是參數(shù)不同。此時(shí),不論有無(wú)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)}{{}◆寫(xiě)語(yǔ)句pd->f(10)的人可能真的想調(diào)用Derive示例8-2-3中,如果語(yǔ)句pd->f(10)一定要調(diào)用函數(shù)Base::f(int),那么將類{{為什么會(huì)這樣?我想是有兩個(gè)原因:一是函數(shù)的實(shí)現(xiàn)(定義)本來(lái)就與參數(shù)是否有voidoutput(intx,floa}}}8.4運(yùn)算符重載8.4.1概念ComplexAdd(constComplex&a,constCo運(yùn)算符與普通函數(shù)在調(diào)用時(shí)的不同之處是:對(duì)于普通函數(shù),參數(shù)出現(xiàn)在圓括號(hào)內(nèi);而對(duì)于運(yùn)算符,參數(shù)出現(xiàn)在其左、右側(cè)。例如c=Add(a,b);//用普通函數(shù)c=a+b;//用運(yùn)算符+如果運(yùn)算符被重載為全局函數(shù),那么只有一個(gè)參數(shù)的運(yùn)算符叫做一元運(yùn)算符,有兩如果運(yùn)算符被重載為類的成員函數(shù),那么一元運(yùn)算符沒(méi)有參數(shù),二元運(yùn)算符只有一個(gè)右側(cè)參數(shù),因?yàn)閷?duì)象自己成了左側(cè)參數(shù)。p44-p47]對(duì)此問(wèn)題作了較多的闡述,并總結(jié)了表8-4-1的規(guī)則。規(guī)則建議重載為成員函數(shù)只能重載為成員函數(shù)建議重載為成員函數(shù)建議重載為全局函數(shù)由于C++語(yǔ)言支持函數(shù)重載,才能將運(yùn)算符當(dāng)(1)不要過(guò)分擔(dān)心自己不會(huì)用,它的本質(zhì)仍然是程序員們熟悉的函數(shù)。(2)不要過(guò)分熱心地使用,如果它不能使代碼變得更加易讀易寫(xiě),那就別用,否則會(huì)自8.4.2不能被重載的運(yùn)算符在C++運(yùn)算符集合中,有一些運(yùn)算符是不允許被重載的。這種限制是出于安全方面(1)不能改變C++內(nèi)部數(shù)據(jù)類型(如int,float等)的運(yùn)算符。(2)不能重載‘.’,因?yàn)椤?’在類中對(duì)任何成員都有意義,已經(jīng)成為標(biāo)準(zhǔn)用法。(3)不能重載目前C++運(yùn)算符集合中沒(méi)有的符號(hào),如#,@,$等。原因有兩點(diǎn),一是難以理解,二是難以確定優(yōu)先級(jí)。(4)對(duì)已經(jīng)存在的運(yùn)算符進(jìn)行重載時(shí),不能改變優(yōu)先級(jí)規(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ù)教科書(shū)中內(nèi)聯(lián)函數(shù)的聲明、定義體前面都加了inline關(guān)鍵字,但我認(rèn)為inline不應(yīng)該出現(xiàn)在函數(shù)的聲明中。這個(gè)細(xì)節(jié)雖然不會(huì)影響函數(shù)的功能,但是體現(xiàn)了高質(zhì)量C++/C程序設(shè)計(jì)風(fēng)格的一個(gè)基本原則:聲明與定義不可混為一談,用戶沒(méi)有必要、也不應(yīng)該知道函數(shù)是否需定義在類聲明之中的成員函數(shù)將自動(dòng)地成為內(nèi)聯(lián)函數(shù),例如{voidFoo(intx,inty){…}//自動(dòng)地成為內(nèi)聯(lián)函數(shù)}將成員函數(shù)的定義體放在類聲明之中雖然能帶來(lái)書(shū)寫(xiě)上的方便,但不是一種良好的編程//頭文件{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)”這個(gè)關(guān)鍵字嗎?內(nèi)聯(lián)是以代碼膨脹(復(fù)制)為代價(jià),僅僅省去了函數(shù)調(diào)用的開(kāi)銷,從而提高函數(shù)的8.6一些心得體會(huì)第9章類的構(gòu)造函數(shù)、析構(gòu)函數(shù)與賦值函數(shù)A(void);//缺省的無(wú)參數(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ù)的起源員的大忙。但是程序通過(guò)了編譯檢查并不表示錯(cuò)誤已經(jīng)不存在了,在“錯(cuò)誤”的大家庭根據(jù)經(jīng)驗(yàn),不少難以察覺(jué)的程序錯(cuò)誤是由于變量沒(méi)有被正確初始化或清除造成的,而初始化和清除工作很容易被人遺忘。Stroustrup在設(shè)計(jì)C++語(yǔ)言時(shí)充分考慮了這個(gè)問(wèn)題并很好地予以解決:把對(duì)象的初始化工作放在構(gòu)造函數(shù)中,把清除工作放在析構(gòu)函數(shù)中。當(dāng)對(duì)象被創(chuàng)建時(shí),構(gòu)造函數(shù)被自動(dòng)執(zhí)行。當(dāng)對(duì)象消亡時(shí),析構(gòu)函數(shù)被自動(dòng)執(zhí)行。這下就不用擔(dān)心忘了對(duì)象的初始化和清除工作。構(gòu)造函數(shù)與析構(gòu)函數(shù)的名字不能隨便起,必須讓編譯器認(rèn)得出才可以被自動(dòng)執(zhí)行。Stroustrup的命名方法既簡(jiǎn)單又合理:讓構(gòu)造函數(shù)、目的與構(gòu)造函數(shù)的相反,就加前綴~,以示區(qū)別。除了名字外,構(gòu)造函數(shù)與析構(gòu)函數(shù)的另一個(gè)特別之處是沒(méi)有返回值類型,這與返回值類型為void的函數(shù)不同。構(gòu)造函數(shù)與析構(gòu)函數(shù)的使命非常明確,就象出生與死亡,光溜溜地來(lái)光溜溜地去。如果它們有返回值類型,那么編譯器將不知所措。為了防止節(jié)外生枝,干脆規(guī)定沒(méi)有返回值類型。(以上典故參考了文獻(xiàn)[Eekel,p55-p56])9.2構(gòu)造函數(shù)的初始化表構(gòu)造函數(shù)有個(gè)特殊的初始化方式叫“初始化表達(dá)式表”(簡(jiǎn)稱初始化表)。初始化表位于函數(shù)參數(shù)表之后,卻在函數(shù)體{}之前。這說(shuō)明該表里的初始化工作發(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常量只能在初始化表里被初始化,因?yàn)樗荒茉诤瘮?shù)體內(nèi)用賦值的方式來(lái)初始化(參見(jiàn)5.4節(jié))?!纛惖臄?shù)據(jù)成員的初始化可以采用初始化表或函數(shù)體內(nèi)賦值兩種方式,這兩種方式的效率不完全相同。非內(nèi)部數(shù)據(jù)類型的成員對(duì)象應(yīng)當(dāng)采用第一種方式初始化,以獲取更高的效率。例如A(void);//無(wú)參數(shù)構(gòu)造函數(shù)A(constA&other);//拷貝構(gòu)造函數(shù)A&operate=(constA&other);//賦值函數(shù){A&a);//B的構(gòu)造函數(shù)//成員對(duì)象示例9-2(a)中,類B的構(gòu)造函數(shù)在其初始化表里調(diào)用了類A的拷貝構(gòu)造函數(shù),從而將成員對(duì)象m_a初始化。示例9-2(b)中,類B的構(gòu)造函數(shù)在函數(shù)體內(nèi)用賦值的方式將成員對(duì)象m_a初始化。我們看到的只是一條賦值語(yǔ)句,但實(shí)際上B的構(gòu)造函數(shù)干了兩件事:先暗地里創(chuàng)建m_a對(duì)象(調(diào)用了A的無(wú)參數(shù)構(gòu)造函數(shù)),再調(diào)用類A的賦值函數(shù),將參數(shù)a賦給m_a。{對(duì)于內(nèi)部數(shù)據(jù)類型的數(shù)據(jù)成員而言,兩種初始化方式的效率幾乎沒(méi)有區(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)造從類層次的最根處開(kāi)始,在每一層中,首先調(diào)用基類的構(gòu)造函數(shù),然后調(diào)用成員對(duì)象的構(gòu)造函數(shù)。析構(gòu)則嚴(yán)格按照與構(gòu)造相反的次序執(zhí)行,該次序是唯一的,否則編一個(gè)有趣的現(xiàn)象是,成員對(duì)象初始化的次序完全不受它們只由成員對(duì)象在類中聲明的次序決定。這是因?yàn)轭惖穆暶魇俏ㄒ坏?,而類的?gòu)造函數(shù)可以有多個(gè),因此會(huì)有多個(gè)不同次序的初始化表。如果成員對(duì)象按照初始化表的次序進(jìn)行構(gòu)造,這將導(dǎo)致析構(gòu)函數(shù)無(wú)法得到唯一的逆序。[Eckel,p29.4示例:類String的構(gòu)造函數(shù)與析構(gòu)函數(shù)//String的普通構(gòu)造函數(shù){{}{//String的析構(gòu)函數(shù){//由于m_data是內(nèi)部數(shù)據(jù)類型,也可以寫(xiě)成deletem_data;}由于并非所有的對(duì)象都會(huì)使用拷貝構(gòu)造函數(shù)和賦值函數(shù),程序員可能對(duì)這兩個(gè)函數(shù)有些輕視。請(qǐng)先記住以下的警告,在閱讀正文時(shí)就會(huì)多心:◆本章開(kāi)頭講過(guò),如果不主動(dòng)編寫(xiě)拷貝構(gòu)造函數(shù)和賦值函數(shù),編譯器將以“位拷貝”的方式自動(dòng)生成缺省的函數(shù)。倘若類中含有指針變量,那么這兩個(gè)缺省的函數(shù)就隱含了錯(cuò)誤。以類String的兩個(gè)對(duì)象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。這將造成三個(gè)錯(cuò)誤:一是b.m_data原有的內(nèi)存沒(méi)被釋放,造成內(nèi)存泄露;二是◆拷貝構(gòu)造函數(shù)和賦值函數(shù)非常容易混淆,常導(dǎo)致錯(cuò)寫(xiě)、錯(cuò)用??截悩?gòu)造函數(shù)是在對(duì)象被創(chuàng)建時(shí)調(diào)用的,而賦值函數(shù)只能被已經(jīng)存在了的對(duì)象調(diào)用。以下程序中,第三個(gè)語(yǔ)句和第四個(gè)語(yǔ)句很相似,你分得清楚哪個(gè)調(diào)用了拷貝構(gòu)造函數(shù),哪個(gè)調(diào)用了賦Stringc=a;//調(diào)用了拷貝構(gòu)造函數(shù),最好寫(xiě)成c(a);c=b;//調(diào)用了賦值函數(shù)本例中第三個(gè)語(yǔ)句的風(fēng)格較差,宜改寫(xiě)成Stringc(a)以區(qū)別于第四個(gè)語(yǔ)句。//拷貝構(gòu)造函數(shù){//允許操作other的私有成員m_data//賦值函數(shù){//(1)檢查自賦值//(2)釋放原有的內(nèi)存資源//(3)分配新的內(nèi)存資源,并復(fù)制內(nèi)容//(4)返回本對(duì)象的引用}類String拷貝構(gòu)造函數(shù)與普通構(gòu)造函數(shù)(參見(jiàn)9.4節(jié))的區(qū)別是:在函數(shù)入口處無(wú)需與NULL進(jìn)行比較,這是因?yàn)椤耙谩辈豢赡苁荖ULL,而“指針”可以為NULL。類String的賦值函數(shù)比構(gòu)造函數(shù)復(fù)雜得多,分四(1)第一步,檢查自賦值。你可能會(huì)認(rèn)為多此一舉,難道有人會(huì)愚蠢到寫(xiě)出a=a這樣的自賦值語(yǔ)句!的確不會(huì)。但是間接的自賦值仍有可能出現(xiàn),例如己而已,反正不會(huì)出錯(cuò)!”意不要將return*this錯(cuò)寫(xiě)成returnthis。那么能否寫(xiě)成returnother呢?效果9.7偷懶的辦法處理拷貝構(gòu)造函數(shù)與賦值函數(shù)A(constA&a);//私有的拷貝構(gòu)造函數(shù)A&operate=(constA9.8如何在派生類中實(shí)現(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é)果為◆在編寫(xiě)派生類的賦值函數(shù)時(shí),注意不要忘記對(duì)基類的數(shù)據(jù)成員重新賦值。例如:{Base&operate=(constBase&other);//類Base的賦值函數(shù)classDerived:public{Derived&operate=(constDerived&other);//類Derived的賦值函數(shù)Derived&Derived::operate=(constDerived&other){//(1)檢查自賦值//(2)對(duì)基類的數(shù)據(jù)成員重新賦值Base::operate=(other);//因?yàn)椴荒苤苯硬僮魉接袛?shù)據(jù)成員//(3)對(duì)派生類的數(shù)據(jù)成員賦值//(4)返回本對(duì)象的引用9.9一些心得體會(huì)有些C++程序設(shè)計(jì)書(shū)籍稱構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值函數(shù)是類的“Big-Three”,它們的確是任何類最重要的函數(shù),不容輕視。也許你認(rèn)為本章的內(nèi)容已經(jīng)夠多了,學(xué)會(huì)了就能平安無(wú)事,我不能作這個(gè)保證。如果你希望吃透“Big-Three”,請(qǐng)好好閱讀參考文獻(xiàn)[Cline][Meyers][Murry]。第10章類的繼承與組合對(duì)象(Object)是類(Class)的一個(gè)實(shí)例(Instance)。如果將對(duì)象比作房子,那么類就是房子的設(shè)計(jì)圖紙。所以面向?qū)ο笤O(shè)計(jì)的重點(diǎn)是類的設(shè)計(jì),而不是對(duì)象的設(shè)計(jì)。對(duì)于C++程序而言,設(shè)計(jì)孤立的類是比較容易的,難的是正確設(shè)計(jì)基類及其派生類。本章僅僅論述“繼承”(Inheritance)和“組合”(Composition)注意,當(dāng)前面向?qū)ο蠹夹g(shù)的應(yīng)用熱點(diǎn)是COM和CORBA,這些內(nèi)容超出了C++教材的范疇,請(qǐng)閱讀COM和CORBA相關(guān)論著。{}這個(gè)簡(jiǎn)單的示例程序說(shuō)明了一個(gè)事實(shí):C++的“繼承”特性可以提高程序的可復(fù)用●【規(guī)則10-1-1】如果類A和類B毫不相關(guān),不可以為了使B的功能更多些而讓B繼承A的功能和屬性。不要覺(jué)得“白吃白不吃”,讓一個(gè)好端端的健壯青年無(wú)緣無(wú)●【規(guī)則10-1-2】若在邏輯上B是A的“一種”(akindof),則允許B繼承A的功能和屬性。例如男人(Man)是人(Human)的一種,男孩(Boy)是男人的一種。那么類Man可以從類Human派生,類Boy可以從類Man派生?!糇⒁馐马?xiàng)【規(guī)則10-1-2】看起來(lái)很簡(jiǎn)單,但是實(shí)際應(yīng)用時(shí)可能會(huì)有意外,繼承的概念在程序世界與現(xiàn)實(shí)世界并不完全相同。例如從生物學(xué)角度講,鴕鳥(niǎo)(Ostrich)是鳥(niǎo)(Bird)的一種,按理說(shuō)類Ostrich應(yīng)該可以從類Bird派生。但是鴕鳥(niǎo)不能飛,那么Ostrich::Fly是什么東西?例如從數(shù)學(xué)角度講,圓(Circle)是一種特殊的橢圓(Ellipse),按理說(shuō)類Circle應(yīng)該可以從類Ellipse派生。但是橢圓有長(zhǎng)軸和短軸,如果圓繼承了橢圓的長(zhǎng)軸和短軸,豈10.2組合{{{{Listen(void){m_ear.LiSmell、Eat、Listen這些功能。示例10-2-2十分簡(jiǎn)短并且運(yùn)行正確,但是這種設(shè)計(jì)方法卻是不對(duì)的。classHead:publicEye,publicNose,publicMouth,publicE一只公雞使勁地追打一只剛下了蛋的母雞,你知道為什么嗎?因?yàn)槟鸽u下了鴨蛋。很多程序員經(jīng)不起“繼承”的誘惑而犯下設(shè)計(jì)錯(cuò)誤。“運(yùn)行正確”的程序不見(jiàn)得是高質(zhì)量的程序,此處就是一個(gè)例證。第11章其它編程經(jīng)驗(yàn)11.1使用const提高函數(shù)的健壯性11.1.1用const修飾函數(shù)的參數(shù)對(duì)于非內(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ù)會(huì)把返回值復(fù)制到外部臨時(shí)的存儲(chǔ)單元中,加const修飾沒(méi)有任何價(jià)值。例如不要把函數(shù)intGetInt(void)寫(xiě)成constintGetInt(void)。同理不要把函數(shù)AGetA(void)寫(xiě)成constAGetA(void),其中A為用戶自定義的如果返回值不是內(nèi)部數(shù)據(jù)類型,將函數(shù)AGetA(void)改寫(xiě)為constA&GetA(void)的確能提高效率。但此時(shí)千萬(wàn)千萬(wàn)要小心,一定要搞清楚函數(shù)究竟是想返回一個(gè)對(duì)象的“拷貝”還是僅返回“別名”就可以了,否則程序會(huì)出錯(cuò)。見(jiàn)6.2節(jié)“返回值的規(guī)則”?!艉瘮?shù)返回值采用“引用傳遞”的場(chǎng)合并不多,這種方式一般只出現(xiàn)在類的賦值函數(shù)Aa,b,c;//a,b,c為A的對(duì)象a=b=c;//正常的鏈?zhǔn)劫x值(a=b)=c//不正常的鏈?zhǔn)劫x值,但合法如果將賦值函數(shù)的返回值加const修飾,那么該返回值的內(nèi)容不允許被改動(dòng)。上例中,語(yǔ)句a=b=c仍然正確,但是語(yǔ)句(a=b)=c則是非法的。任何不會(huì)修改數(shù)據(jù)成員的函數(shù)都應(yīng)該聲明為const類型。如果在編寫(xiě)const成員函數(shù)時(shí),不慎修改了數(shù)據(jù)成員,或者調(diào)用了其它非const成員函數(shù),編譯器將指出錯(cuò)誤,以下程序中,類stack的成員函數(shù)GetCount僅用于計(jì)數(shù),從邏輯上講GetCount應(yīng)當(dāng){intGetCount(void)const;//const {++m_num;//編譯錯(cuò)誤,企圖修改數(shù)據(jù)成員m_numPop();//編譯錯(cuò)誤,企圖調(diào)用非const函數(shù)}const成員函數(shù)的聲明看起來(lái)怪怪的:const關(guān)鍵字只能放在函數(shù)聲明的尾部,大概11.2提高程序的效率程序的時(shí)間效率是指運(yùn)行速度,空間效率是指程序占用內(nèi)存或者外存的狀況。全局效率是指站在整個(gè)系統(tǒng)的角度上考慮的效率,局部效率是指站在模塊或函數(shù)角●【規(guī)則11-2-1】不要一味地追求程序的效率,應(yīng)當(dāng)在滿足正確性、可靠性、健壯性、可讀性等質(zhì)量因素的前提下,設(shè)法提高程序的效率。11.3一些有益的建議我們經(jīng)常會(huì)把“==”誤寫(xiě)成“=”,象“IⅡ”、“&&”、“<=”、“>=”這類符號(hào)也很

【建議11-3-2】變量(指針、數(shù)組)被創(chuàng)建之后應(yīng)當(dāng)及時(shí)把它們初始化,以防止把[Eckel]BruceEckel,ThinkinginC++[Maguire]SteveMaguire,WritingCleanCode(編程精粹,姜靜波等譯),電子工業(yè)出版[Summit]SteveSummit,CProgrammingFAQs,Addison-Wesl審查項(xiàng)結(jié)論頭文件是否使用了ifndef/define/endif預(yù)處理塊?頭文件中是否只存放“聲明”而不存放“定義”審查項(xiàng)結(jié)論“{”和“}”是否各占一行并且對(duì)齊于同一列?If、for、while、do等語(yǔ)句自占一行,不論執(zhí)行語(yǔ)句多類結(jié)構(gòu)的public,protected,private順序是否在所有的審查項(xiàng)結(jié)論.審查項(xiàng)結(jié)論(1)將布爾變量直接與TRUE、FALSE或者1、0進(jìn)(2)將浮點(diǎn)變量用“==”或“!=”與任何數(shù)字比較。(3)將指針變量用“==”或“!=”與NULL比較。如果循環(huán)體內(nèi)存在邏輯判斷,并且循環(huán)次數(shù)很大,是是否忘記寫(xiě)switch的default分支?審查項(xiàng)結(jié)論是否使用含義直觀的常量來(lái)表示那些將在程序中多審查項(xiàng)結(jié)論函數(shù)名字與返回值類型在語(yǔ)義上是否沖突?當(dāng)用輸出參數(shù)獲得,而錯(cuò)誤標(biāo)志用retur在函數(shù)體的“入口處”,是否用asser使用濫用了assert?例如混淆非法情況與錯(cuò)誤情return語(yǔ)句是否返回指向“棧內(nèi)存”“引用”?wheneveryouneed”審查項(xiàng)結(jié)論值是否為NULL?(防止使用指針值為NULL的內(nèi)存)是否忘記為數(shù)組和動(dòng)態(tài)內(nèi)存賦初值?(防止將未被初始化的內(nèi)存作為右值使用)動(dòng)態(tài)內(nèi)存的申請(qǐng)與釋放是否配對(duì)?(防止內(nèi)存泄漏)(2)用free或delete釋放了內(nèi)存之后,忘記將指針是否將malloc/free和new/delete混淆使用?malloc語(yǔ)句是否正確無(wú)誤?例如字節(jié)數(shù)是在創(chuàng)建與釋放動(dòng)態(tài)對(duì)象數(shù)組時(shí),new/de審查項(xiàng)結(jié)論是否濫用內(nèi)聯(lián)函數(shù)?例如函數(shù)體內(nèi)的代碼比較長(zhǎng),函是否用內(nèi)聯(lián)函數(shù)取代了宏代碼?審查項(xiàng)結(jié)論是否違背編程規(guī)范而讓C++編譯器自動(dòng)為類產(chǎn)生四個(gè)缺省的函數(shù):(1)缺省的無(wú)參數(shù)構(gòu)造函數(shù);(2)缺省的拷貝構(gòu)造函數(shù);(3)缺省的析構(gòu)函數(shù);(4)缺省的賦值函數(shù)。構(gòu)造函數(shù)中是否遺漏了某些初始化工作?是否正確地使用構(gòu)造函數(shù)的初始化表?是否錯(cuò)寫(xiě)、錯(cuò)用了拷貝構(gòu)造函數(shù)和賦值函數(shù)?放原有內(nèi)存資源;(3

溫馨提示

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

評(píng)論

0/150

提交評(píng)論