



版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
編程高手之路--C++入門ー——變量和常量編譯環(huán)境設(shè)置好以后,就可以正式學(xué)習(xí)c語言了。ー、變量(一)、變量類型和表示方法1.什么是變量?一句話,變量是存儲數(shù)據(jù)的值的空間。由于數(shù)值的類型有多種,有整數(shù)、小數(shù)(浮點數(shù))、字符等等,那么對應(yīng)的變量就有整型變量、浮點型變量、字符型變量。變量還有其他的具體分類。整型變量還可具體分為無符號型、長整型和短整型。浮點型也可分為單精度型、雙精度型和長雙精度型。此外還可以分為靜態(tài)變量、外部變量、寄存器變量和自動存儲變量。這些數(shù)據(jù)類型我們在本節(jié)和后面的章節(jié)中都會陸陸續(xù)續(xù)介紹。那么變量我們總要給它取個名字吧,這個名字我們叫做標(biāo)識符。標(biāo)識符的命名有一定的規(guī)則:.標(biāo)識符只能由字母、數(shù)字和下劃線三類字符組成.第一個字符必須是字母(第一個字符也可以是下劃線,但被視作系統(tǒng)自定義的標(biāo)識符).大寫字母和小寫字母被認(rèn)為是兩個不同的字符,如A和a是兩個不同的標(biāo)識符.標(biāo)識符可以任意長,但只有前32位有效。有些舊的C版本對外部標(biāo)識符的限制為6位。這是由于鏈接程序的限制所總成的,而不是C語言本身的局限性.標(biāo)識符不能是C的關(guān)鍵字.從上面的規(guī)則中,有個關(guān)鍵字的概念。那么什么叫關(guān)鍵字呢?從表面字意上也可以看出,關(guān)鍵字是C語言本身某些特性的ー個表示,是唯一的代表某ー個意思的。下面列出ANS!標(biāo)準(zhǔn)定義的32個C語言的關(guān)鍵字,這些關(guān)鍵字在以后的學(xué)習(xí)中基本上都會用到,到時再說它們的各自用法。autobreakcasecharconstcontinuedefaultdodoubleelseenumexternfloatforgotoifintlongregisterreturnshortsignedsizeofstaticstructswitchtypedefunionunsignedvoidvolatilewhileC語言還包括一些不能用做標(biāo)識符的擴(kuò)展關(guān)鍵字。asmcdecl_cs_ds_esfarhugeinterruptnearpascal_ss所以在以后的學(xué)習(xí)中,在給變量命名時要避開這些關(guān)鍵字。.TurboC2.0規(guī)定所有變量在使用前都必須加以說明。一條變量說明語句由數(shù)據(jù)類型和其后的ー個或多個變量名組成。變量說明的形式如下:類型〈變量表》;這里類型是指TurboC2.0的有效數(shù)據(jù)類型。變量表是ー個或多個標(biāo)識符名,每個標(biāo)識符之間用,分隔。(二)、整型變量整型變量是用來存儲整數(shù)的。整型變量又可具體分為好幾種,最基本的整型變量是用類型說明符血聲明的符號整型,形式如下:intCounter;這里int是類型說明符,Counter是變量的名字。整型變量可以是有符號型、無符號型、長型、短型或象上面定義的普通符號整型。整型是16位的,長整型是32位,短整型等價于整型。以下是幾種整型變量的聲明示例:longintAmount;/?長整型*/longAmount;/?長整型,等價于上面?/signedintTotal;/*有符號整型?/signedTotal;/*有符號整型,等價于上面?/unsignedintOffset;/*無符號整型?/unsignedOffset;/*無符號整型,等價于上面?/shortintSmallAmt;/?短整型?/shortSmallAmt;/?短整型,等價于上面*/unsignedshortintMonth;/?無符號短整型?/unsignedshortMonth;/?無符號短整型,等價于上面?/從上面的示例可以看出,當(dāng)定義長整型、短整型、符號整型或無符號整型時,可以省略關(guān)鍵字int。注明:.用signed對整型變量進(jìn)行有符號指定是多余的,因為除非用unsigned指定為無符號型,否則整型都是有符號的。.當(dāng)一個變量有幾重特性時,聲明關(guān)鍵字的順序可以任意。以下幾種聲明是等價的:unsignedlongT1;longunsignedT2;unsignedlongintT3;unsignedintlongT4;longunsignedintT5;longintunsignedT6;intunsignedlongT7;intlongunsignedT8;(三)、浮點類型變量顧名思義,浮點類型變量是用來存儲帶有小數(shù)的實數(shù)的。C語言中有三種不同的浮點類型,以下是對這三種不同類型的聲明示例:floatAmount;/?單精度型?/doubleBigAmount;/?雙精度型?/longdoubleReallyBigAmount;/?長雙精度型?/這里Amount,BigAmount,ReallyBigAmount都是變量名。浮點型都是有符號的。(四)、字符型變量字符型變量中所存放的字符是計算機(jī)字符集中的字符。對于PC機(jī)上運行的C系統(tǒng),字符型數(shù)據(jù)用8位單字節(jié)的ASCII碼表示。程序用類型說明符char來聲明字符型變量:charch;這條聲明語句聲明了一個字符型變量,標(biāo)識符為ch。當(dāng)以這種形式聲明變量之后,程序可以在表達(dá)式中引用這個變量,關(guān)于語句和表達(dá)式的知識在后面將會介紹。字符數(shù)據(jù)類型事實上是8位的整型數(shù)據(jù)類型,可以用于數(shù)值表達(dá)式中,與其他的整型數(shù)據(jù)同樣使用。在這種情況下,字符型變量可以是有符號的,也可以是無符號的。對于無符號的字符型變量可以聲明為:unsignedcharch;除非聲明為無符號型,否則在算術(shù)運算和比較運算中,字符型變量一般作為8位有符號整型變量處理。還有其他的如指針型變量,void型變量,以后再介紹。二、常量常量的意思就是不可改變的量,是ー個常數(shù)。同變量一樣,常量也分為整型常量、浮點型常量、字符型常量,還有字符串常量、轉(zhuǎn)義字符常量和地址常量。(一)、整型常量整型常量可以是長整型、短整型、有符號型、無符號型。在TubboC2.0里有符號整型常量的范圍從ー32768到32767,無符號整型的為0到65535;有符號長整型的范圍為-2147483648到2147483647〇無符號長整型的范圍為0到4294967295c短整型同字符型??梢灾付ㄒ粋€整型常量為二進(jìn)制、ハ進(jìn)制或十六進(jìn)制,如以下語句:-129,0x12伯,0177常量的前面有符號Ox,這個符號表示該常量是十六進(jìn)制表示。如果前面的符號只有一個字母0,那么表示該常量是八進(jìn)制。有時我們在常量的后面加上符號L或者U,來表示該常量是長整型或者無符號整型:22388L,0x4efb2L,40000U后綴可以是大寫,也可以是小寫。(二)、浮點型常量ー個浮點型常量由整數(shù)和小數(shù)兩部分構(gòu)成,中間用十進(jìn)制的小數(shù)點隔開。有些浮點樹非常大或者非常小,用普通方法不容易表示,可以用科學(xué)計數(shù)法或者指數(shù)方法表示。下面是ー個實例:3.1416,1.234E-30,2.47E201注意在C語言中,數(shù)的大小也有一定的限制。對于float型浮點數(shù),數(shù)的表示范圍為-3.402823E38到3.402823E38,其中一1.401298E-45到1.401298E-45不可見。double型浮點型常數(shù)的表示范圍為ー1.79E308到1.79E308,其中-4.94E-324到4.94E-324不可見。在浮點型常量里我們也可以加上后綴。FloatNumber=1.6E10F;/*有符號浮點型?/LongDoubleNumber=3.45L;/?長雙精度型?/后綴可大寫也可小寫。說明:.浮點常數(shù)只有一種進(jìn)制(十進(jìn)制)。.所有浮點常數(shù)都被默認(rèn)為double。.絕對值小于1的浮點數(shù),其小數(shù)點前面的零可以省略。如:0.22可寫為.22,-0.0015E-3可寫為-.0015E-3。.TurboC默認(rèn)格式輸出浮點數(shù)時,最多只保留小數(shù)點后六位(三)、字符型常量字符型常量所表示的值是字符型變量所能包含的值。我們可以用ASCH表達(dá)式來表示一個字符型常量,或者用單引號內(nèi)加反斜杠表示轉(zhuǎn)義字符。AJ\x2fJ\013,;其中:\x表示后面的字符是十六進(jìn)制數(shù),'0表示后面的字符是ハ進(jìn)制數(shù)。注意:在TurboC2.0中,字符型常量表示數(shù)的范圍是ー128到127,除非你把它聲明為unsigned,這樣就是〇到255。(四)、字符串常量字符串常量就是ー串字符,用雙引號括起來表示。Hello,World!\nEnterselection:\aError!!!(五)、轉(zhuǎn)義字符上面我們見到的'x,\n,\a等等都是叫轉(zhuǎn)義字符,它告訴編譯器需要用特殊的方式進(jìn)行處理。下面給出所有的轉(zhuǎn)義字符和所對應(yīng)的意義:轉(zhuǎn)義字符描述V單引號'雙引號W反斜杠'0空字符\0nnnハ進(jìn)制數(shù)\a聲音符\b退格符\f換頁符\n換行符\r回車符\t水平制表符\v垂直制表符\x十六進(jìn)制符它們的具體用法我們到講輸出語句時再介紹。(六)、地址常量我們前面說的變量是存儲數(shù)據(jù)的空間,它們在內(nèi)存里都有對應(yīng)的地址。在C語言里可以用地址常量來引用這些地址,如下:&Counter,∑&是取地址符,作用是取出變量(或者函數(shù))的地址。在后面的輸入語句和指針里還會說明。這ー節(jié)所講到的變量和常量知識可以說是在一切程序中都要用到,特別是變量的聲明和命名規(guī)則。編程高手之路ーC++入門ーーー類的轉(zhuǎn)換C++的內(nèi)部數(shù)據(jù)類型遵循隱式類型轉(zhuǎn)換規(guī)則。假設(shè)某個表達(dá)市中使用了一個短整型變量,而編譯器根據(jù)上下文認(rèn)為這兒需要是的長整型,則編譯器就會根據(jù)類型轉(zhuǎn)換規(guī)則自動把它轉(zhuǎn)換成長整型,這種隱式轉(zhuǎn)換出現(xiàn)在賦值、參數(shù)傳遞、返回值、初始化和表達(dá)式中。我們也可以為類提供相應(yīng)的轉(zhuǎn)換規(guī)則。對ー個類建立隱式轉(zhuǎn)換規(guī)則需要構(gòu)造ー個轉(zhuǎn)換函數(shù),該函數(shù)作為類的成員,可以把該類的對象和其他數(shù)據(jù)類型的對象進(jìn)行相互轉(zhuǎn)換。聲明了轉(zhuǎn)換函數(shù),就告訴了編譯器,當(dāng)根據(jù)句法判定需要類型轉(zhuǎn)換時,就調(diào)用函數(shù)。有兩種轉(zhuǎn)換函數(shù)。ー種是轉(zhuǎn)換構(gòu)造函數(shù):另ー種是成員轉(zhuǎn)換函數(shù)。需要采用哪種轉(zhuǎn)換函數(shù)取決于轉(zhuǎn)換的方向。ー、轉(zhuǎn)換構(gòu)造函數(shù)當(dāng)ー個構(gòu)造函數(shù)僅有一個參數(shù),且該參數(shù)是不同于該類的ー個數(shù)據(jù)類型,這樣的構(gòu)造函數(shù)就叫轉(zhuǎn)換構(gòu)造函數(shù)。轉(zhuǎn)換構(gòu)造函數(shù)把別的數(shù)據(jù)類型的對象轉(zhuǎn)換為該類的ー個對象。和其他構(gòu)造函數(shù)ー樣,如果聲明類的對象的初始化表同轉(zhuǎn)換構(gòu)造函數(shù)的參數(shù)表相匹配,該函數(shù)就會被調(diào)用。當(dāng)在需要使用該類的地方使用了別的數(shù)據(jù)類型,便宜器就會調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)進(jìn)行轉(zhuǎn)換。#includeiostream.h#includetime.h#includestdio.hclassDate{intmo,da,yr;public:Date(time_t);voiddisplayQ;};voidDate::display(){charyear[5];if(yr<10)sprint鶴year,0%d,yr);elsesprintf^year,%d,yr);cout?MO?'??year;Date::Date(time_tnow)tm*tim=localtime(&now);da=tim->tmmday;mo=tim->tmmon+1;yr=tim->tmyear;if(yr>=100)yr-=100;}intmain()(time_tnow=time(0);Datedt(now);dt.display();return0;}本程序先調(diào)用time。函數(shù)來獲取當(dāng)前時間,并把它賦給time」對象;然后程序通過調(diào)用Date類的轉(zhuǎn)換構(gòu)造函數(shù)來創(chuàng)建一個Date對象,該對象由timet對象轉(zhuǎn)換而來。timet對象先傳遞給localtime。函數(shù),然后返回一個指向tm結(jié)構(gòu)(time.h文件中聲明)的指針,然后構(gòu)造函數(shù)把結(jié)構(gòu)中的日月年的數(shù)值拷貝給Date對象的數(shù)據(jù)成員,這就完成了從timet對象到Date對象的轉(zhuǎn)換。二、成員轉(zhuǎn)換函數(shù)成員轉(zhuǎn)換函數(shù)把該類的對象轉(zhuǎn)換為其他數(shù)據(jù)類型的對象。在成員轉(zhuǎn)換函數(shù)的聲明中要用到關(guān)鍵字operator。這樣聲明ー個成員轉(zhuǎn)換函數(shù):operatoraaa();在這個例子中,aaa就是要轉(zhuǎn)換成的數(shù)據(jù)類型的說明符。這里的類型說明符可以是任何合法的C++類型,包括其他的類。如下來定義成員轉(zhuǎn)換函數(shù);Classname:operatoraaa()類名標(biāo)識符是聲明了該函數(shù)的類的類型說明符。上面定義的Date類并不能把該類的對象轉(zhuǎn)換回time_t型變量,但可以把它轉(zhuǎn)換成一個長整型值,計算從2000年1月1日到現(xiàn)在的天數(shù)。#includeiostream.hclassDateintmo,da,yr;public:Date(intm,intd,inty){mo=m;da=d;yr=y;}operatorint();〃聲明};Date::operatorint()〃定義{staticintdys[]={31,28,31,30,31,30,31,31,30,31,30,31};intdays=yr-2000;days*=365;days+=(yr-2000)/4;fdr(inti=0;i<MO-l;I++)days+=dys[i];days+=da;returndays;}intmain()(Datenow(12,24,2003);intsince=now;cout?SINCE?ENDL;return0;三、類的轉(zhuǎn)換上面兩個例子都是C++類對象和內(nèi)部數(shù)據(jù)對象之間的相互轉(zhuǎn)換。也可以定義轉(zhuǎn)換函數(shù)來實現(xiàn)兩個類對象之間的相互轉(zhuǎn)換。#includeiostream,hclassCustomDate{public:intda,yr;CustomDate(intd=O,inty=0){da=d;yr=y;}voiddisplay()]cout?YR?'-,?DA?ENDL;1};classDateintmo,da,yr;public:Date(intm=0,intd=O,inty=O){mo=m;da=d;yr=y;}Date(constCustomDate&);〃轉(zhuǎn)換構(gòu)造函數(shù)operatorCustomDate();〃成員轉(zhuǎn)換函數(shù)voiddisplay(){cout?MO?'?<<da<?yr?endl;}};staticintdys[]={31,28,31,30,31,30,31,31,30,31,30,31);Date::Date(constCustomDate&jd)[yr=jd.yr;da=jd.da;fbr(mo=0;mo<l1;mo++)if(da>dys[mo])da-=dys[mo];elsebreak;mo++;Date二operatorCustomDate()CustomDatecd(0,yr);fbr(inti=0;icd.da+=da;returncd;}intmain()(Datedt(12,24,3);CustomDatecd;cd=dt;//調(diào)用成員轉(zhuǎn)換函數(shù)cd.display();dt=cd;//調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)dt.display();return0;這個例子中有兩個類CustomDate和Date,CustomDate型日期包含年份和天數(shù)。這個例子沒有考慮閏年情況。但是在實際構(gòu)造ー個類時,應(yīng)該考慮到所有問題的可能性。在Date里中具有兩種轉(zhuǎn)換函數(shù),這樣,當(dāng)需要從Date型變?yōu)镃ustomDate型十,可以調(diào)用成員轉(zhuǎn)換函數(shù);反之可以調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)。不能既在Date類中定義成員轉(zhuǎn)換函數(shù),又在CustomDate類里定義轉(zhuǎn)換構(gòu)造函數(shù)。那樣編譯器在進(jìn)行轉(zhuǎn)換時就不知道該調(diào)用哪ー個函數(shù),從而出錯。四、轉(zhuǎn)換函數(shù)的調(diào)用C++里調(diào)用轉(zhuǎn)換函數(shù)有三種形式:第一種是隱式轉(zhuǎn)換,例如編譯器需要一個Date對象,而程序提供的是CustomDate對象,編譯器會自動調(diào)用合適的轉(zhuǎn)換函數(shù)。另外兩種都是需要在程序代碼中明確給出的顯式轉(zhuǎn)換。C++強(qiáng)制類型轉(zhuǎn)換是ー種,還有一種是顯式調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)和成員轉(zhuǎn)換函數(shù)。下面的程序給出了三中轉(zhuǎn)換形式:#includeiostream.hclassCustomDate(public:intda,yr;CustomDate(intd=O,inty=0){da=d;yr=y;}voiddisplay(){cout<<YR?,-'?DA<<ENDL;)};classDate(intmo,da,yr;public:Date(intm,intd,inty){mo=m;da=d;yr=y;}operatorCustomDate(););Date:operatorCustomDate()(staticintdys[]={31,28,31,30,31,30,31,31,30,31,30,31);CustomDatecd(0,yr);fdr(inti=O;icd.da+=da;returncd;)intmain(){Datedt(11,17,89);CustomDatecd;cd=dt;cd.display();cd=(CustomDate)dt;cd.display();cd=CustomDate(dt);cd.displayQ;return0;五、轉(zhuǎn)換發(fā)生的情形上面的幾個例子都是通過不能類型對象之間的相互賦值來調(diào)用轉(zhuǎn)換函數(shù),還有幾種調(diào)用的可能:參數(shù)傳遞初始化返回值表達(dá)式語句這些情況下,都有可能調(diào)用轉(zhuǎn)換函數(shù)。下面的程序不難理解,就不分析了。#includeiostream,hclassCustomDate{public:intda,yr;CustomDate(){}CustomDate(intd,inty){da=d;yr=y;}voiddisplay()Jcout?YR?,-'?DA?ENDL;);classDateintmo,da,yr;public:Date(intm,intd,inty){mo=m;da=d;yr=y;}operatorCustomDate();};Date二operatorCustomDate(){staticintdys[]={31,28,31,30,31,30,31,31,30,31,30,31);CustomDatecd(0,yr);fbr(inti=0;icd.da+=da;returncd;classTesterJCustomDatecd;public:explicitTester(CustomDatec){cd=c;}voiddisplay(){cd.display();}};voiddispdate(CustomDatecd){cd.display();}CustomDatertndate()(Datedt(9,ll,l);returndt;)intmain()]Datedt(12,24,3);CustomDatecd;cd=dt;cd.displayQ;dispdate(dt);Testerts(dt);ts.display();cd=rtndate();cd.display();return0;六、顯式構(gòu)造函數(shù)注意上面Tester類的構(gòu)造函數(shù)前面有一個explicit修飾符。如果不加上這個關(guān)鍵字,那么在需要把CustomDate對象轉(zhuǎn)換成Tester對象時,編譯器會把該函數(shù)當(dāng)作轉(zhuǎn)換構(gòu)造函數(shù)來調(diào)用。但是有時候,并不想把這種只有一個參數(shù)的構(gòu)造函數(shù)用于轉(zhuǎn)換目的,而僅僅希望用它來顯式地初始化對象,此時,就需要在構(gòu)造函數(shù)前加explicito如果在聲明了Tester對象以后使用了下面的語句將導(dǎo)致一個錯誤:ts弓d;//error這個錯誤說明,雖然Tester類中有一個以Date型變量為參數(shù)的構(gòu)造函數(shù),編譯器卻不會把它看作是從Date到Tester的轉(zhuǎn)換構(gòu)造函數(shù),因為它的聲明中包含了explicit修飾符。七、表達(dá)式內(nèi)部的轉(zhuǎn)換在表達(dá)式內(nèi)部,如果發(fā)現(xiàn)某個類型和需要的不一致,就會發(fā)生錯誤。數(shù)字類型的轉(zhuǎn)換是很簡單,這里就不舉例了。下面的程序是把Date對象轉(zhuǎn)換成長整型值。#includeiostream,hclassDate{intmo,da,yr;public:Date(intm,intd,inty)[mo=m;da=d;yr=y;}operatorlong();};Date::operatorlong()staticintdys[]={31,28,31,30,31,30,31,31,30,31,30,31);longdays=yr;days*=365;days+=(yr-l900)/4;〃從!900年1月1日開始計算for(inti=0;idays+=da;returndays;}intmain()(Datetoday(12,24,2003);constlongott=123;longsum=ott+today;cout?OTT?+today?="?sum;<return0;)在表達(dá)式中,當(dāng)需要轉(zhuǎn)換的對象可以轉(zhuǎn)換成某個數(shù)字類型,或者表達(dá)式調(diào)用了作用于某個類的重載運算符時,就會發(fā)生隱式轉(zhuǎn)換。運算符重載以后再學(xué)習(xí)。編程高手之路ーC++入門一類的其他幾點問題ー、拷貝構(gòu)造函數(shù)拷貝構(gòu)造函數(shù)在下列情況下被調(diào)用:用已經(jīng)存在的對象去初始化同一個類的另ー個對象;在函數(shù)的參數(shù)中,以傳值方式傳遞類對象的拷貝;類對象的值被用做函數(shù)的返回值??截悩?gòu)造函數(shù)和前面說到的轉(zhuǎn)換構(gòu)造函數(shù)有些相似。轉(zhuǎn)換構(gòu)造函數(shù)是把ー個類的對象轉(zhuǎn)化為另ー個類的對象;拷貝構(gòu)造函數(shù)是用ー個已經(jīng)存在的對象的值實例化該類的一個新對象。不同對象間的初始化和賦值的區(qū)別:賦值操作是在兩個已經(jīng)存在的對象間進(jìn)行的;而初始化是要創(chuàng)建一個新的對象,并且其初值來源于另ー個已存在的對象。編譯器會區(qū)別這兩種情況,賦值的時候調(diào)用重載的賦值運算符,初始化的時候調(diào)用拷貝構(gòu)造函數(shù)。如果類中沒有拷貝構(gòu)造函數(shù),則編譯器會提供ー個默認(rèn)的。這個默認(rèn)的拷貝構(gòu)造函數(shù)只是簡單地復(fù)制類中的每個成員。#includeiostream.h#includestring.hclassDateintmo,da,yr;char*month;public:Date(intm=0,intd=0,inty=0);Date(constDate&);?Date。;voiddisplay()const;);Date::Date(intm,intd,inty)(staticchar*mos[]={January,February,March,April,May,June,July,August,September,October,November,December);mo=m;da=d;yr=y;if(m!=0)(month=newchar[strlen(mos[m-1])+1];strcpy(month,mos[m-l]);elsemonth=0;)Date::Date(constDate&dt){mo=dt.mo;da=dt.da;yr=dt.yr;if(dt.month!=0)(month=newchar[strlen(dt.month)+1];strcpy(month,dt.month);}elsemonth=0;1Date::-Date()delete[]month;voidDate::display()constif(month!=0)cout?month?**?da?,?yr?std二endl;}intmain()(Datebirthday(6,24,1940);birthday.display();Datenewday=birthday;newday.display();Datelastday(birthday);lastday.display();return0;本例中,用到了兩次拷貝構(gòu)造函數(shù)。ー個是使用普通的C++初始化變量的語句:Datenewday=birthday;另ー個是使用構(gòu)造函數(shù)的調(diào)用約定,即把初始化值作為函數(shù)的參數(shù):Datelastday(birthday);二、類的引用在函數(shù)參數(shù)和返回值中,如果一定要使用傳值方式,那么使用類對象的引用,是ー個提高效率的方法。類的數(shù)據(jù)成員也可以是一個引用,但必須注意:第一,ー個引用必須初始化。通常一個類對象并不會像結(jié)構(gòu)那樣用大括號來初始化,而是調(diào)用構(gòu)造函數(shù)。因此在構(gòu)造函數(shù)里必須初始化類當(dāng)中的引用成員。第二,引用是ー個別名。盡管類里面的引用在使用方式上看起來和類的一般數(shù)據(jù)成員沒有什么區(qū)別,但是作用在其上的操作,實際上是對用來初始化它的那么對象進(jìn)行的。#includeiostream,hclassDateintda,mo,yr;public:Date(intd,intm,inty){da=d;mo=m;yr=y;}voidDisplayOconst{cout?da?71?mo?7,?yr;}};classTime(inthr,min,sec;public:Time(inth,intm,ints){hr=h;min=m;sec=s;}voidDisplayOconst{cout?hr??min??sec;});classDateTimeJconstDate&dt;constTime&tm;public:DateTime(constDate&d,constTime&t):dt(d),tm(t)//empty)voidDisplay()const{dt.Display();cout?'tm.Display();1};intmain(){Datetoday(7,4,2004);Timenow(15,20,0);DateTimedtm(today,now);dtm.Display();return0;我們來看看這個程序中DateTime的構(gòu)造函數(shù)的格式:冒號操作符引出了一個參數(shù)初始化表。必須使用這種格式來初始化引用數(shù)據(jù)成員,而不可以在函數(shù)體內(nèi)來進(jìn)行初始化工作。如果構(gòu)造函數(shù)像上例ー樣不是內(nèi)聯(lián)的,那么最好不要在類聲明中構(gòu)造函數(shù)的原型上使用冒號和初始化值表,而是像下面這樣,把參數(shù)初始化表放在定義構(gòu)造函數(shù)的地方:ClassDateTime{constDate&dt;constTime&tm;public:DateTime(constDate&d,constTime&t);)DateTime::DateTime(constDate&d,constTime&t):dt(d),tm(t)[//empty}可以使用構(gòu)造函數(shù)的參數(shù)初始化表來初始化任何數(shù)據(jù)成員。特別是常量數(shù)據(jù)成員,和引用ー樣,只能在參數(shù)初始化表里進(jìn)行初始化,這是因為不可以在構(gòu)造函數(shù)內(nèi)部為常量數(shù)據(jù)成員賦值。當(dāng)ー個類含有引用數(shù)據(jù)成員時,一旦引用被實例化和初始化以后,就無法修改它的值,所以該類不可能徹底地重載賦值運算符函數(shù)。三、構(gòu)造函數(shù)的參數(shù)初始化表如果類對象的某些數(shù)據(jù)成員沒有載構(gòu)造函數(shù)內(nèi)部被初始化,那么必須使用構(gòu)造函數(shù)的參數(shù)初始化表對他們進(jìn)行初始化。否則,編譯器不止到該如何初始化這些還等著在構(gòu)造函數(shù)內(nèi)部賦值的成員。我們習(xí)慣用參數(shù)初始化表來初始化所有數(shù)據(jù)成員。classDate(intmo,da,yr;public:Date(intm=0,intd=O,inty=O););classEmployeeJintempno;Datedatehired;public:Employee(inten,Date&dh);};可以用下面兩種方法編寫Employee類的構(gòu)造函數(shù):Employee::Employee(inten,Date&dt){empno=en;datehired=dh;}或者;Employee二Employee(inten,Date&dt):empno(en),datehired(dh)(//empty1雖然這兩種方法效果是ー樣的,但是根據(jù)Date對象默認(rèn)構(gòu)造函數(shù)的復(fù)雜性的不同,這兩種形式的效率差別是很大的。四、對const修飾符的簡單說明如果ー個對象被聲明為常量,那么該對象就不可以調(diào)用類當(dāng)中任何非常量型的成員函數(shù)(除了被編譯器隱式調(diào)用的構(gòu)造函數(shù)和析構(gòu)函數(shù))??聪旅娴拇a;#includeiostream.hclassDate{intmonth,day,year;public:Date(intm,d,y):month(m),day(d),year(y){}voiddisplay()(cout?MOTH?*??year?endl;intmain()[constDatedt(4,7,2004);dt.display();//errorreturn0;}這個程序盡管編譯時沒有問題,但運行時卻出錯了。這是因為常量對象不能調(diào)用非常量函數(shù)。編譯器只看函數(shù)的聲明,而不在乎函數(shù)的具體實現(xiàn)。實際上函數(shù)的實現(xiàn)可以在程序中的任何地方,也可以是在另一個源代碼文件中,這就超過了編譯器的當(dāng)前可見范圍。//date.hclassDate{intmonth,day,year;public:Date(intm,d,y);voiddisplay();};//date.cpp#includeiostream.h#includedate.hDate::Date(intm,d,y):month(m),day(d),year(y){}voidDate::display()cout?MONTH?,??year?endl;//program.cpp#includeiostream,h#includedate.cppintmain(){constDatedt(4,7,2004);dt.display();return0;1解決出錯的問題有兩個方法:第一是聲明display。函數(shù)為常量型的//indate.hvoiddisplay。const//intdate.cppvoidDate::display()const(cout?MONTH?'??year?endl;另ー個解決方式就是省略掉Date對象聲明里的const修飾符。Datedt(4,7,2004);還有另ー個容易出錯的地方:voidabc(constDate&dt){dt.display();//error提示display沒有const修飾符}函數(shù)abc()聲明了一個Date對象的常量引用,這說明該函數(shù)不會修改傳遞進(jìn)來的參數(shù)的值。如果Date::display()函數(shù)不是常量型的,那么在函數(shù)abc()里就不能調(diào)用它,因為編譯器會認(rèn)為Date::display()函數(shù)有可能會修改常量的值。不論類對象是否是常量型的,它必須修改某個數(shù)據(jù)成員的值時,ANSI委員會設(shè)立了mutable關(guān)鍵字。五、可變的數(shù)據(jù)成員假設(shè)需要統(tǒng)計某個對象出現(xiàn)的次數(shù),不管它是否是常量。那么類當(dāng)中就應(yīng)該有一個用來計數(shù)的整型數(shù)據(jù)成員。只要用mutable修飾符來聲明該數(shù)據(jù)成員,ー個常量型的成員函數(shù)就可以修改它的值。#includeiostream.hclassAValueintval;mutableintrptct;public:AValue(intv):val(v),rptct(O){}?AValue。(cout?<rptct?reportedwas?val?>}voidreport。const;);voidAValue::report。const[rptct++;cout?val?endl;1intmain。constAValueaval(123);aval.report();aval.report();aval.report();return0;1編程高手之路ー-C++入門ー類對象數(shù)組和靜態(tài)成員一、類對象數(shù)組類的對象和C++其他數(shù)據(jù)類型一樣,也可以為其建立數(shù)組,數(shù)組的表示方法和結(jié)構(gòu)ー樣。#includeiostream.hclassDate(intmo,da,yr;public:Date(intm=0,intd=0,inty=0){mo=m;da=d;yr=y;}voiddisplay()const{cout?MO?'}};intmain(){Datedates[2];Datetoday(12,31,2003);dates[0]=today;dates[O].display();dates[l].display();return0;)1.類對象數(shù)組和默認(rèn)構(gòu)造函數(shù)在前面已經(jīng)說過,不帶參數(shù)或者所有參數(shù)都有默認(rèn)值的構(gòu)造函數(shù)叫做默認(rèn)構(gòu)造函數(shù)。如果類中沒有構(gòu)造函數(shù),編譯器會自動提供ー個什么都不做的公共默認(rèn)構(gòu)造函數(shù)。如果類當(dāng)中至少有一個構(gòu)造函數(shù),編譯器就不會提供默認(rèn)構(gòu)造函數(shù)。如果類當(dāng)中不含默認(rèn)構(gòu)造函數(shù),則無法實例化其對象數(shù)組。因為實例花類對象數(shù)組的格式不允許用初始化值來匹配某個構(gòu)造函數(shù)的參數(shù)表。上面的程序中,main()函數(shù)聲明了一個長度為2的Date對象數(shù)組,還有一個包含初始化值的單個Date對象。接著把這個初始化的Date對象賦值給數(shù)組中第一個對象,然后顯示兩個數(shù)組元素中包含的日期。從輸出中可以看到,第一個日期是有效日期,而第二個顯示的都是0。當(dāng)聲明了某個類的對象數(shù)組時,編譯器會為每個元素都調(diào)用默認(rèn)構(gòu)造函數(shù)。下面的程序去掉了構(gòu)造函數(shù)的默認(rèn)參數(shù)值,并且增加了一個默認(rèn)構(gòu)造函數(shù)。#includeclassDate{intmo,da,yr;public:Date();Date(intm,intd,inty){mo=m;da=d;yr=y;}voiddisplay()const{cout?MO?*}};Date::Date()cout?<endl;mo=0;da=0;yr=0;)intmain(){Datedates[2];Datetoday(12,31,2003);dates[0]=today;dates[O].display();dates[l].display();return0;運行程序,輸出為:DateconstructorrunningDateconstructorrunning12/31/20030/0/0從輸出中可以看出,Date。這個默認(rèn)構(gòu)造函數(shù)被調(diào)用了兩次。.類對象數(shù)組和析構(gòu)函數(shù)當(dāng)類對象離開作用域時,編譯器會為每個對象數(shù)組元素調(diào)用析構(gòu)函數(shù)。#includeiostream,hclassDate{intmo,da,yr;public:Date(intm=0,intd=O,inty=0){mo=m;da=d;yr=y;}~Date(){cout?<endl;}voiddisplay()const{cout?MO?*}};intmain()]Datedates[2];Datetoday(12,31,2003);dates[0]=today;dates[O].display();dates[l].display();return0;運行程序,輸出為:12/31/20030/0/0DatedestructorrunningDatedestructorrunningDatedestructorrunning表明析構(gòu)函數(shù)被調(diào)用了三次,也就是dates[0],dates[1],today這三個對象離開作用域時調(diào)用的。二、靜態(tài)成員可以把類的成員聲明為靜態(tài)的。靜態(tài)成員只能存在唯一的實例。所有的成員函數(shù)都可以訪問這個靜態(tài)成員。即使沒有聲明類的任何實例,靜態(tài)成員也已經(jīng)是存在的。不過類當(dāng)中聲明靜態(tài)成員時并不能自動定義這個變量,必須在類定義之外來定義該成員。.靜態(tài)數(shù)據(jù)成員靜態(tài)數(shù)據(jù)成員相當(dāng)于ー個全局變量,類的所有實例都可以使用它。成員函數(shù)能訪問并且修改這個值。如果這個靜態(tài)成員是公有的,那么類的作用域之內(nèi)的所有代碼(不論是在類的內(nèi)部還是外部)都可以訪問這個成員。下面的程序通過靜態(tài)數(shù)據(jù)成員來記錄鏈表首項和末項的地址。#includeiostream,h#includestring.hclassListEntry{public:staticListEntry*firstentry;private:staticListEntry*lastentry;char*listvalue;ListEntry*nextentry;public:ListEntry(char*);?ListEntry。{delete[]listvalue;}ListEntry*NextEntry()const{returnnextentry;};voiddisplay()const{cout?LISTVALUE<);ListEntry*ListEntry::firstentry;ListEntry*ListEntry::lastentry;ListEntry::ListEntry(char*s){if(firstentry==O)firstentry=this;if(lastentry!=O)lastentry->nextentry=this;lastentry=this;listvalue=newchar[strlen(s)+l];strcpy(listvalue,s);nextentry=O;)intmain(){while(1)]cout?\nEnteraname('end'whendone):;charname[25];cin?name;if(strncmp(name,end,3)=0)break;newListEntry(name);ListEntry*next=ListEntry::firstentry;while(next!=0){next->display();ListEntry*hold=next;next=next->NextEntry();deletehold;)return0;1程序首先顯示提示信息,輸入一串姓名,以end作為結(jié)束標(biāo)志。然后按照輸入順序來顯示姓名。構(gòu)造函數(shù)將表項加入鏈表,用new運算符來聲明一個表項,但并沒有把new運算符返回的地址賦值給某個指針,這是因為構(gòu)造函數(shù)會把該表項的地址賦值給前一個表項的nextentry指針。這個程序和前面將的逆序輸出的程序都不是最佳方法,最好的方法是使用類模板,這在后面再介紹。main()函數(shù)取得ListEntry::firstentry的值,開始遍歷鏈表,因此必需把ListEntry::firstentry設(shè)置成公有數(shù)據(jù)成員,這不符合面向?qū)ο蟪绦虻募s定,因為這里數(shù)據(jù)成員是公有的。.靜態(tài)成員函數(shù)成員函數(shù)也可以是靜態(tài)的。如果一個靜態(tài)成員函數(shù)不需要訪問類的任何實例的成員,可以使用類名或者對象名來調(diào)用它。靜態(tài)成員通常用在只需要訪問靜態(tài)數(shù)據(jù)成員的情況下。靜態(tài)成員函數(shù)沒有this指針,因為它不能訪問非靜態(tài)成員,所以它們不能把this指針指向任何東西。下面的程序中,ListEntry類中加入了一個靜態(tài)成員函數(shù)FirstEntry()?它從數(shù)據(jù)成員firstentry獲得鏈表第一項的地址,在這兒,firstentry已經(jīng)聲明為私有數(shù)據(jù)成員了。#includeiostream,h#includestring.hclassListEntry{staticListEntry*firstentry;staticListEntry*lastentry;char*listvalue;ListEntry*nextentry;public:ListEntry(char*);?ListEntry。{delete[]listvalue;}staticListEntry*FirstEntry(){returnfirstentry;}ListEntry*NextEntry()const{returnnextentry;};voiddisplay()const{cout?LISTVALUE<};ListEntry*ListEntry::firstentry;ListEntry*ListEntry::lastentry;ListEntry::ListEntry(char*s)(iRfirstentry==O)firstentry=this;iRlastentry!=O)lastentry->nextentry=this;lastentry=this;Iistvalue=newchar[strlen(s)+l];strcpy(listvalue,s);nextentry=0;intmain()Jwhile(1)cout?\nEnteraname('end'whendone):;charname[25];cin?name;if(strncmp(name,end,3)=0)break;newListEntry(name);)ListEntry*next=ListEntry::FirstEntry();while(next!=0){next->display();ListEntry*hold=next;next=next->NextEntry();deletehold;)return0;}函數(shù)ListEntry::FirstEntry()是靜態(tài)的,返回靜態(tài)數(shù)據(jù)成員firstentry的值。.公有靜態(tài)成員如果ー個靜態(tài)成員象上面程序ー樣是公有的,那么在整個程序中都可以訪問它??梢栽谌魏蔚胤秸{(diào)用公有景泰成員函數(shù),而且不需要有類的實例存在。但公有靜態(tài)成員函數(shù)不完全是全局的,它不僅僅存在于定義類的作用域內(nèi)。在這個作用域里面,只要在函數(shù)名前加上類名和域解析運算符::就可以調(diào)用該函數(shù)。編程高手之路ーC++入門ー類和堆ー、構(gòu)造函數(shù)和析構(gòu)函數(shù)前面的例子已經(jīng)運用了new和delete來為類對象分配和釋放內(nèi)存。當(dāng)使用new為類對象分配內(nèi)存時,編譯器首先用new運算符分配內(nèi)存,然后調(diào)用類的構(gòu)造函數(shù);類似的,當(dāng)使用delete來釋放內(nèi)存時,編譯器會首先調(diào)用淚的析構(gòu)函數(shù),然后再調(diào)用delete運算符。#includeiostream.hclassDate(intmo,da,yr;public:Date(){cout<?Date。{cout<intmain()Date*出=newDate;cout?<endl;deletedt;return0;}程序定義了一個有構(gòu)造函數(shù)和析構(gòu)函數(shù)的Date類,這兩個函數(shù)在執(zhí)行時會顯示一條信息。當(dāng)new運算符初始化指針dt時,執(zhí)行了構(gòu)造函數(shù),當(dāng)delete運算符釋放內(nèi)存時,又執(zhí)行了析構(gòu)函數(shù)。程序輸出如下:DateconstructorProcessthedateDatedestructor二、堆和類數(shù)組前面提到,類對象數(shù)組的每個元素都要調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)。下面的例子給出了一個錯誤的釋放類數(shù)組所占用的內(nèi)存的例子。#includeiostream.hclassDate{intmo,da,yr;public:Date(){cout<?Date。{cout<)intmain(){Date*dt=newDate[5];cout?<endl;deletedt;〃這兒return0;指針dt指向一個有五個元素的數(shù)組。按照數(shù)組的定義,編譯器會讓new運算符調(diào)用Date類的構(gòu)造函數(shù)五次。但是delete被調(diào)用時,并沒有明確告訴編譯器指針指向的Date對象有幾個,所以編譯時,只會調(diào)用析構(gòu)函數(shù)一次。下面是程序輸出;DateconstructorDateconstructorDateconstructorDateconstructorDateconstructorProcessthedateDatedestructor為了解決這個問題,C++允許告訴delete運算符,正在刪除的那個指針時指向數(shù)組的,程序修改如下:#includeiostream.hclassDate(intmo,da,yr;public:Date(){cout<~Date(){cout<intmain()Date*dt=newDate[5];cout?<endl;delete[]dt;〃這丿しreturn0;最終輸出為:DateconstructorDateconstructorDateconstructorDateconstructorDateconstructorProcessthedateDatedestructorDatedestructorDatedestructorDatedestructorDatedestructor三、重載new和delete運算符前面已經(jīng)介紹了如何用new和delete運算符函數(shù)來動態(tài)第管理內(nèi)存,在那些例子中使用的都是全局的new和delete運算符。我們可以重載全局的new和delete運算符,但這不是好的想法,除非在進(jìn)行低級的系統(tǒng)上或者嵌入式的編程。但是,在某個類的內(nèi)部重載new和delete運算符時可以的。這允許ー個類有它自己的new和delete運算符。當(dāng)ー個類需要和內(nèi)存打交道時,采用這種方法來處理其中的細(xì)節(jié),可以獲得很搞的效率,同時避免了使用全局new和delete運算符帶來的額外開銷。因為全局堆操作時調(diào)用操作系統(tǒng)函數(shù)來分配和釋放內(nèi)存,這樣效率很低。如果確定某個類在任何時候,其實例都不會超過ー個確定的值,那么就可以一次性為類的所有實例分配足夠的內(nèi)存,然后用該類的new和delete運算符來管理這些內(nèi)存。下面的程序說明了如何對new和delete進(jìn)行重載。#includeiostream.h#includestring.h#includestddef.h#includenew.hconstintmaxnames=5;classNames{charname[25];staticcharNames::pool[];staticboolNames::inuse[maxnames];public:Names(char*s){stmcpy(name,s,sizeof(name));}void*operatornew(size_t)throw(bad_alloc);voidoperatordelete(void*)throw();voiddisplay()const{cout?NAME<};charNames::pool[maxnames*sizeofifNames)];boolNames::inuse[maxnames];void*Names:operatornew(sizet)throw(badalloc)for(intp=0;pif(!inuse[p])inuse[p]=true;returnpool+p*sizeof(NaiTies);)1throwbad_alloc();)voidNames::operatordelete(void*p)throw(){if(p!=0)inuse[((char*)p-pool)/sizeof(Names)]=false;1intmain()(Names*nm[maxnames];inti;fbr(i=O;icout?ENDL?<i+1?:;charname[25];cin?name;nm[i]=newNames(name);fbr(i=O;i{nm[i]->display();deletenm[i];}return0;上面的程序提示輸入5個姓名,然后顯示它們。程序中定義了名為Names的類,它的構(gòu)造函數(shù)初始化對象的name值。這個類定義了自己的new和delete運算符。這是因為程序能保證不會一次使用超過maxnames個姓名,所以可以通過重載默認(rèn)的new和delete運算符來提高運行速度。Names類中的內(nèi)存池是ー個字符數(shù)組,可以同時容納程序需要的所有姓名。與之相關(guān)的布爾型數(shù)組inuse為每個姓名記錄了一個true和false值,指出內(nèi)存中的對應(yīng)的項是否正在使用。重載的new運算符在內(nèi)存池中尋找ー個沒有被使用的項,然后返回它的地址。重載的delete運算符則標(biāo)記那些沒有被使用的項。在類定義中重載的new和delete運算符函數(shù)始終是靜態(tài)的,并且沒有和對象相關(guān)的this指針。這是因為編譯器會在調(diào)用構(gòu)造函數(shù)之前調(diào)用new函數(shù),在調(diào)用析構(gòu)函數(shù)后調(diào)用delete函數(shù)。new函數(shù)是在類的構(gòu)造函數(shù)之前被調(diào)用的。因為這時內(nèi)存中還不存在類的對象而且構(gòu)造函數(shù)也沒有提供任何初始化值,所以它不可以訪問類的任何成員。同理,delete運算符是在析構(gòu)函數(shù)之后被調(diào)用的,所以它也不可以訪問類的成員。四、異常監(jiān)測和異常處理.檢測異常上面的例子還缺少必要的保護(hù)機(jī)制。比如,重載的delete運算符函數(shù)并沒有檢查它的參數(shù),確認(rèn)其是否落在內(nèi)存池內(nèi)部。如果你絕對相信自己編的程序中不會傳遞錯誤的指針值給delete運算符,那么可以省掉合法性檢查以提高效率,特別是在優(yōu)先考慮效率的程序中。否則應(yīng)該使用預(yù)編譯的條件語句。在軟件的測試版本中加入這些檢測,在正式的發(fā)行版本中去掉這些檢查。.重載new和delete中的異常處理上面的兩個重載運算符函數(shù)都是用了異常處理。異常處理是C++的新內(nèi)容之一,目前還沒有講到。在這里不必關(guān)心它是如何工作的。上面程序中,當(dāng)試圖分配超過內(nèi)存池容量的Names緩沖區(qū),重載的new運算符函數(shù)就會拋出異常,終止程序。五、重載new口和delete[]對于上面的程序,假如有下面的語句:Names*nms=newNamesfl0]delete[]nms;那么,這些語句會調(diào)用全局new和delete運算符,而不是重載過的new和delete。為了重載能為對象數(shù)組分配內(nèi)存的new和delete運算符,必須像下面的程序ー樣,對new口和delete[]也進(jìn)行重載。#includeiostream.h#includestring.h#includestddef.h#includenew.hconstintmaxnames=5;classNamescharname[25];staticcharNames::pool[];staticboolNames::inuse[maxnames];public:Names(char*s){stmcpy(name,s,sizeof(name));}void*operatornew(sizet)throw(badalloc);voidoperatordelete(void*)throw();voiddisplay()const{cout?NAME<);charNames::pool[maxnames*sizeof{Names)];boolNames::inuse[maxnames];void*Names::operatornew[](size_tsize)throw(badalloc)(intelements=size/sizeof(Names);intp="l;inti=0;while((i{if(!inuse[i])p=i;++i;)//Notenoughroom.if((p==-l)||((maxnames-p)for(intx=0;xreturnpool+p*sizeof(Names);)voidNames::operatordelete[](void*b)throw(){if(b!=O){intp=((char*)b-pool)/sizeof(Names);intelements=inuse[p];for(inti=0;i}}intmain()(Names*np=newNames[maxnames];inti;for(i=O;icout?ENDL<?i+l?>charname[25];cin?name;*(np+i)=name;}fbr(i=O;idisplay();delete[]np;return0;)重載new口和deleteU要比重載new和delete考慮更多的問題。這是因為new口運算符時為數(shù)組分配內(nèi)存,所以它必須記住數(shù)組的大小,重載的deleteロ運算符オ能正確地把緩沖區(qū)釋放回內(nèi)存池。上面的程序采用的方法比較簡單,吧原來存放緩沖區(qū)使用標(biāo)志的布爾型數(shù)組換成一個整型數(shù)組,該數(shù)組的每個元素記錄new口運算符分配的緩沖區(qū)個數(shù),而不再是ー個簡單的true〇當(dāng)deleteロ運算符函數(shù)需要把緩沖區(qū)釋放回內(nèi)存池時,它就會用該數(shù)組來確認(rèn)釋放的緩沖區(qū)個數(shù)。編程高手之路--C++入門一私有數(shù)據(jù)成員和友元ー、私有數(shù)據(jù)成員的使用.取值和賦值成員函數(shù)面向?qū)ο蟮募s定就是保證所有數(shù)據(jù)成員的私有性。一般我們都是通過公有成員函數(shù)來作為公共接口來讀取私有數(shù)據(jù)成員的。某些時候,我們稱這樣的函數(shù)為取值和賦值函數(shù)。取值函數(shù)的返回值和傳遞給賦值函數(shù)的參數(shù)不必ーー匹配所有數(shù)據(jù)成員的類型。#includeiostream,hclassDate]intmo,da,yr;public:Date(intm,intd,inty){mo=m;da=d;yr=y;}intgetyear()const{returnyr;}voidsetyear(inty){yr=y;});intmain()Date出(4,1,89);cout?<dt.getyear()?endl;dt.setyear(97);cout?<dt.getyear();return0;}上面的例子很簡單,不分析了。要養(yǎng)成這樣的習(xí)慣,通過成員函數(shù)來訪問和改變類中的數(shù)據(jù)。這樣有利于軟件的設(shè)計和維護(hù)。比如,改變Date類內(nèi)部數(shù)據(jù)的形式,但仍然用修改過的getyear()和setyear()來提供訪問接口,那么使用該類就不必修改他們的代碼,僅需要重新編譯程序即可。.常量成員函數(shù)注意上面的程序中g(shù)etyear()被聲明為常量型,這樣可以保證該成員函數(shù)不會修改調(diào)用他的對象。通過加上const修飾符,可以使訪問對象數(shù)據(jù)的成員函數(shù)僅僅完成不會引起數(shù)據(jù)變動的那些操作。如果程序聲明某個Date對象為常量的話,那么該對象不得調(diào)用任何非常量型成員函數(shù),不論這些函數(shù)是否真的試圖修改對象的數(shù)據(jù)。只有把那些不會引起數(shù)據(jù)改變的函數(shù)都聲明為常量型,オ可以讓常量對象來調(diào)用。.改進(jìn)的成員轉(zhuǎn)換函數(shù)下面的程序改進(jìn)了從Date對象到CustomDate對象的成員轉(zhuǎn)換函數(shù),用取值和賦值函數(shù)取代了使用公有數(shù)據(jù)成員的做法。(以前的程序代碼在上一帖中)#includeiostream,hclassCustomDate{intda,yr;public:CustomDate(){}CustomDate(intd,inty){da=d;yr=y;}voiddisplay()const{cout?YR?,-'?DA?ENDL;}intgetday()const{returnda;}voidsetday(intd){da=d;}};classDateintmo,da,yr;public:Date(intm,intd,inty){mo=m;da=d;yr=y;}operatorCustomDate()const;};Date:operatorCustomDate()const{staticintdys[]={31,28,31,30,31,30,31,31,30,31,30,31);CustomDatecd(0,yr);intday=da;fbr(inti=0;icd.setday(day);returncd;1intmain()(Datedt(11,17,89);CustomDatecd;cd=dt;cd.displayQ;return0;注意上面的程序中Date::operatorCustomDate。聲明為常量型,因為這個函數(shù)沒有改變調(diào)用它對象的數(shù)據(jù),盡管它修改了一個臨時CustomDate對象并將其作為函數(shù)返回值。二、友元前面已經(jīng)說過了,私有數(shù)據(jù)成員不能被類外的其他函數(shù)讀取,但是有時候類會允許ー些特殊的函數(shù)直接讀寫其私有數(shù)據(jù)成員。關(guān)鍵字friend可以讓特定的函數(shù)或者別的類的所有成員函數(shù)對私有數(shù)據(jù)成員進(jìn)行讀寫。這既可以維護(hù)數(shù)據(jù)的私有性,有可以保證讓特定的類或函數(shù)能夠直接訪問私有數(shù)據(jù)。.友元類ー個類可以聲明另ー個類為其友元,這個友元的所有成員函數(shù)都可以讀寫它的私有數(shù)據(jù)。#includeiostream,hclassDate;classCustomDateintda,yr;public:CustomDate(intd=O,inty=0){da=d;yr=y;}voiddisplay()const{cout?YR?'-'?DA?ENDL;}friendDate;〃這兒};classDate(intmo,da,yr;public:Date(intm,intd,inty){mo=m;da=d;yr=y;}operatorCustomDate();};Date::operatorCustomDate()(staticintdysロ={31,28,31,30,31,30,31,31,30,31,30,31);CustomDatecd(0,yr);for(inti=0;icd.da+=da;returncd;intmain()Datedt(l1,17,89);CustomDatecd(dt);cd.display();return0;}在上面的程序中,有這樣一句friendDate;該語句告訴編譯器,Date類的所有成員函數(shù)有權(quán)訪問CustomDate類的私有成員。因為Date類的轉(zhuǎn)換函數(shù)需要知道CustomDate類的每個數(shù)據(jù)成員,所以真?zhèn)€Date類都被聲明為CustomDate類的友兀。.隱式構(gòu)造函數(shù)上面程序?qū)ustomDate的構(gòu)造函數(shù)的調(diào)用私有顯示該類需要如下的ー個轉(zhuǎn)換構(gòu)造函數(shù):CustomDate(Date&dt);但是唯一的—構(gòu)造函數(shù)是:CustomDate(intd=O;inty=0);這就出現(xiàn)了問題,編譯器要從Date對象構(gòu)造ー個CustomDate對象,但是CustomDate類中并沒有定義這樣的轉(zhuǎn)換構(gòu)造函數(shù)。不過Date類中定義了一個成員轉(zhuǎn)換函數(shù),它可以把Date對象轉(zhuǎn)換成CustomDate對象。于是編譯器開始搜索CustomDate類,看其是否有一個構(gòu)造函數(shù),能從ー個已存在的CustomDate的對象創(chuàng)建新的CustomDate對象。這種構(gòu)造函數(shù)叫拷貝構(gòu)造函數(shù)??截悩?gòu)造函數(shù)也只有一個參數(shù),該參數(shù)是它所屬的類的ー個對象,由于CustomDate類中沒有拷貝構(gòu)造函數(shù),于是編譯器就會產(chǎn)生一個默認(rèn)的拷貝構(gòu)造函數(shù),該函數(shù)簡單地把已存在的對象的每個成員拷貝給新對象。現(xiàn)在我們已經(jīng)知道,編譯器可以把Date對象轉(zhuǎn)換成CustomDate對象,也可以從已存在的CustomDate對象生成ー個新的CustomDate對象。那么上面提出的問題,編譯器就是這樣做的:它首先調(diào)用轉(zhuǎn)換函數(shù),從Date對象創(chuàng)建一個隱藏的、臨時的、匿名的CustomDate對象,然后用該臨時對象作為參數(shù)調(diào)用默認(rèn)拷貝構(gòu)造函數(shù),這就生成了一個新的CustomDate對象。.預(yù)引用上面的例子中還有這樣一句classDate;這個語句叫做預(yù)引用。它告訴編譯器,類Date將在后面定義。編譯器必須知道這個信號,因為CustomDate類中引用了Date類,而Date里也引用了CustomDate類,必須首先聲明其中之ー。使用了預(yù)引用后,就可以聲明未定義的類的友元、指針和引用。但是不可以使用那些需要知道預(yù)引用的類的定義細(xì)節(jié)的語句,如聲明該類的ー個實例或者任何對該類成員的引用。.顯式友元預(yù)引用也可以不使用預(yù)引用,這只要在聲明友元的時候加上關(guān)鍵自class就行了。#includeiostream.hclassCustomDate(intda,yr;public:CustomDate(intd=0,inty=0){da=d;yr=y;}voiddisplay()const{cout?YR?'-,?DA?ENDL;}friendclassDate;//i^JL,去掉前面的預(yù)引用);classDate};Date:operatorCustomDate()intmain().友元函數(shù)通常,除非真的需要,否則并不需要把整個類都設(shè)為另ー個類的友元,只需挑出需要訪問當(dāng)前類私有數(shù)據(jù)成員的成員函數(shù),將它們設(shè)置為該類的友元即可。這樣的函數(shù)稱為友元函數(shù)。下面的程序限制了CustomDate類數(shù)據(jù)成員的訪問,Date類中只有需要這些數(shù)據(jù)的成員函數(shù)オ有權(quán)讀寫它們。#includeiostream.hclassCustomDate;classDateintmo,da,yr;public:Date(constCustomDate&);voiddisplay()const{cout?MO?'??yr?endl;}};classCustomDate(intda,yr;public:CustomDate(intd=0,inty=0){da=d;yr=y;}friendDate::Date(constCustomDate&);};Date::Date(constCustomDate&cd)(staticintdysロ={31,28,31,30,31,30,31,31,30,31,30,31);yr=cd.yr;da=cd.da;fbr(mo=0;mo<l1;mo++)if(da>dys[mo])da-=dys[mo];elsebreak;mo++;intmain(){Datedt(CustomDate(123,89));dt.display();return0;).匿名對象上面main()函數(shù)中Date對象調(diào)用CustomDate類的構(gòu)造函數(shù)創(chuàng)建了一個匿名CustomDate對象,然后用該對象創(chuàng)建了一個Date對象。這種用法在C++中是經(jīng)常出現(xiàn)的。.非類成員的友元函數(shù)有時候友元函數(shù)未必是某個類的成員。這樣的函數(shù)擁有類對象私有數(shù)據(jù)成員的讀寫權(quán),但它并不是任何類的成員函數(shù)。這個特性在重載運算符時特別有用。非類成員的友元函數(shù)通常被用來做為類之間的紐帶。ー個函數(shù)如果被兩個類同時聲明為友元,它就可以訪問這兩個類的私有成員。下面的程序說明了一個可以訪問兩個類私有數(shù)據(jù)成員的友元函數(shù)是如何將在兩個類之間架起橋梁的。#includeiostream,hclassTime;classDate(intmo,da,yr;public:Date(intm,intd,inty){mo=m;da=
溫馨提示
- 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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 光伏培訓(xùn)課件講解
- 公司結(jié)算培訓(xùn)課件模板
- 2026屆天津市河北區(qū)化學(xué)高一第一學(xué)期期中教學(xué)質(zhì)量檢測模擬試題含解析
- 財務(wù)會計培訓(xùn)課件
- 工程創(chuàng)優(yōu)工作培訓(xùn)課件
- 網(wǎng)絡(luò)文學(xué)IP全產(chǎn)業(yè)鏈開發(fā)中的網(wǎng)絡(luò)文學(xué)與虛擬現(xiàn)實版權(quán)運營
- 康復(fù)醫(yī)療器械市場2025年創(chuàng)新產(chǎn)品市場細(xì)分與市場發(fā)展報告
- 綠色裝配式木結(jié)構(gòu)建筑項目2025年綠色建筑環(huán)保性能可行性研究報告
- 倉庫上崗技巧培訓(xùn)課件
- 純水系統(tǒng)培訓(xùn)課件
- 2025年長春市事業(yè)單位招聘考試綜合類專業(yè)能力測試試卷(管理類)
- 2025年工業(yè)和信息化部所屬事業(yè)單位招聘28人筆試模擬試題及答案詳解一套
- 2025年全國國家版圖知識競賽測試題庫(中小學(xué)組)及參考答案詳解【完整版】
- 風(fēng)力發(fā)電項目投資計劃書
- 2025年康復(fù)理療師專項能力證書考試真題卷(后附答案和解析)
- 2025年度食堂餐具設(shè)備升級改造采購合同
- 河北公物拍賣管理辦法
- 供排水調(diào)度工公司招聘筆試題庫及答案
- 政府隱性債務(wù)管理課件
- 中國人力資源管理軟件行業(yè)市場深度分析及投資策略咨詢報告
- 戀愛行為學(xué)課件
評論
0/150
提交評論