類與對象本教案改變了教學(xué)次序_第1頁
類與對象本教案改變了教學(xué)次序_第2頁
類與對象本教案改變了教學(xué)次序_第3頁
類與對象本教案改變了教學(xué)次序_第4頁
類與對象本教案改變了教學(xué)次序_第5頁
已閱讀5頁,還剩105頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

類與對象本教案改變了教學(xué)次序第一頁,共一百一十頁,2022年,8月28日第五章類與對象5.1類與對象

5.5運(yùn)算符的重載

5.4構(gòu)造函數(shù)和析構(gòu)函數(shù)(下)

5.3引用

5.2從面向過程到面向?qū)ο?/p>

5.9全局對象與類接口

5.10面向?qū)ο蟮某绦蛟O(shè)計(jì)和Windows編程

5.8結(jié)構(gòu)和聯(lián)合

5.7靜態(tài)成員

5.6友元

5.11對象與類的識別

5.4構(gòu)造函數(shù)和析構(gòu)函數(shù)(上)第二頁,共一百一十頁,2022年,8月28日5.1

類與對象

對象的創(chuàng)建與使用名字空間域和類域5.1.1C++類的定義5.1.2 成員函數(shù)的定義

第三頁,共一百一十頁,2022年,8月28日5.1.1C++類的定義

在C++中,類是一種數(shù)據(jù)類型。數(shù)組和枚舉類型是由用戶自己定義,自己按規(guī)則構(gòu)造出來的,但其基本組成單位(數(shù)據(jù)成員)都是同一種數(shù)據(jù)類型。然而客觀事物是復(fù)雜的,要描述它必須從多方面進(jìn)行,也就是用不同的數(shù)據(jù)類型來描述不同的方面。如商場中的商品可以這樣描述:

商品名稱(用字符串描述),該商品數(shù)量(用整型數(shù)描述),該商品單價(jià)(用浮點(diǎn)數(shù)描述),該商品總價(jià)(用浮點(diǎn)數(shù)描述)。這里用了屬于三種不同數(shù)據(jù)類型的四個(gè)數(shù)據(jù)成員(datamember)來描述一種商品。第四頁,共一百一十頁,2022年,8月28日5.1.1C++類的定義在C++中可以這樣表述:classCGoods{public:charName[21]; //對于中文可用wchar_tname[11] intAmount; floatPrice; floatTotal_value;};

//最后的分號不可少,這是一條說明語句上面的表述中,關(guān)鍵字class是數(shù)據(jù)類型說明符,指出下面說明的是類。標(biāo)識符CGoods是商品這個(gè)類的類型名?;ɡㄌ栔惺菢?gòu)成類體的一系列的成員,關(guān)鍵字public是一種訪問限定符,表示其后所列為公共成員,就是說可以在外部對這些成員進(jìn)行訪問。第五頁,共一百一十頁,2022年,8月28日5.1.1C++類的定義訪問限定符(accessspecifier)有三種:public(公共的),private(私有的)和protected(保護(hù)的),其中后兩種說明的成員是不能從外部進(jìn)行訪問的。每種說明符可在類體中使用多次。它們的作用域是從該說明符出現(xiàn)開始到下一個(gè)說明符之前或類體結(jié)束之前結(jié)束。如果在類體起始點(diǎn)無訪問說明符,系統(tǒng)默認(rèn)定義為私有(private)。訪問說明符private(私有的)和protected(保護(hù)的)體現(xiàn)了類具有封裝性(Encapsulation)。第六頁,共一百一十頁,2022年,8月28日5.1.1C++類的定義

定義一個(gè)類的一般格式為:class類名{《《private:》

成員表1;》《public:

成員表2;》《protected:

成員表3;》};//最后的分號不可少;注意:所有說明都有分號其中“class類名”稱為類頭(classhead)?;ɡㄌ栔械牟糠址Q為類體(classbody),類體中定義了類成員表(classmemberlist)。類定義的更關(guān)鍵部分是對數(shù)據(jù)成員的操作。這可以用函數(shù)來完成。

第七頁,共一百一十頁,2022年,8月28日5.1.1C++類的定義這樣描述一種商品的完整方式如下:classCGoods{private:charName[21];int Amount;floatPrice;float Total_value;public:voidRegisterGoods(char*,int,float);//輸入數(shù)據(jù)

void CountTotal(void);//計(jì)算商品總價(jià)值

void GetName(char*);//讀取商品名

int GetAmount(void);//讀取商品數(shù)量

float GetPrice(void);//讀取商品單價(jià)

float GetTotal_value(void);//讀取商品總價(jià)值};第八頁,共一百一十頁,2022年,8月28日5.1.1C++類的定義

這樣在類中引進(jìn)了成員函數(shù)(memberfunction)或函數(shù)成員,也就是函數(shù)也成了數(shù)據(jù)(類)中的一員。類把數(shù)據(jù)(事物的屬性)和函數(shù)(事物的行為——操作)封裝為一個(gè)整體。還應(yīng)注意到:四個(gè)數(shù)據(jù)成員被說明成私有的,而六個(gè)函數(shù)成員被說明成公有的;這就是說如果從外部對四個(gè)數(shù)據(jù)成員進(jìn)行操作的話,只能通過六個(gè)公有函數(shù)來完成,數(shù)據(jù)受到了良好的保護(hù),不易受副作用的影響。公有函數(shù)集定義了類的接口(interface)。類是一種數(shù)據(jù)類型,定義時(shí)系統(tǒng)并不為類分配存儲空間,所以不能對類的數(shù)據(jù)成員初始化。當(dāng)然類中的任何數(shù)據(jù)成員也不能使用關(guān)鍵字extern、auto或register限定其存儲類型。成員函數(shù)可以直接使用類定義中的任一成員,可以處理數(shù)據(jù)成員,也可調(diào)用函數(shù)成員。

第九頁,共一百一十頁,2022年,8月28日5.1.2 成員函數(shù)的定義

在前面的小結(jié)中,只對成員函數(shù)作了一個(gè)聲明,或者講只給出了函數(shù)的原型,并沒有對函數(shù)進(jìn)行定義。函數(shù)定義通常在類的說明之后進(jìn)行,其格式如下:返回值類型

類名::函數(shù)名(參數(shù)表){……}//函數(shù)體其中運(yùn)算符“::”稱為作用域解析運(yùn)算符(scoperesolutionoperator),它指出該函數(shù)是屬于哪一個(gè)類的成員函數(shù)。當(dāng)然也可以在類的定義中直接定義函數(shù)。但系統(tǒng)處理方法不一樣。第十頁,共一百一十頁,2022年,8月28日5.1.2 成員函數(shù)的定義類CGoods的函數(shù)可以如下定義:voidCGoods::RegisterGoods(char*name,intamount,floatprice){

//char*是指向字符的指針類型說明,name現(xiàn)可理解為字符串strcpy(Name,name);//字符串拷貝函數(shù)Amount=amount;Price=price;}voidCGoods::CountTotal(void){Total_value=Price*Amount;}voidCGoods::GetName(char*name){strcpy(name,Name);}intCGoods::GetAmount(void){return(Amount);}floatCGoods::GetPrice(void){return(Price);}floatCGoods::GetTotal_value(void){return(Total_value);}第十一頁,共一百一十頁,2022年,8月28日

對象是類的實(shí)例(instance),正如在前幾章稱變量是數(shù)據(jù)類型的實(shí)例一樣。聲明一種數(shù)據(jù)類型只是告訴編譯系統(tǒng)該數(shù)據(jù)類型的結(jié)構(gòu)形式,并沒有預(yù)定內(nèi)存,或者講并沒有創(chuàng)建了可用來存放數(shù)據(jù)的變量。類只是一個(gè)樣板,以此樣板可以在內(nèi)存中開辟出一個(gè)個(gè)同樣結(jié)構(gòu)的實(shí)例——對象。創(chuàng)建類的對象可以有兩種常用方法。第一種是直接定義類的實(shí)例——對象:CGoodsCar;這個(gè)定義創(chuàng)建了CGoods類的一個(gè)對象Car,同時(shí)為它分配了屬于它自己的存儲塊,用來存放數(shù)據(jù)和對這些數(shù)據(jù)實(shí)施操作的成員函數(shù)(代碼)。與變量定義一樣,一個(gè)對象只在定義它的域中有效。第二種是采用動(dòng)態(tài)創(chuàng)建類的對象的方法,將在第七章中學(xué)習(xí)。所謂動(dòng)態(tài)指在程序運(yùn)行時(shí)建立對象。而前一種是在編譯時(shí)(程序運(yùn)行前)建立。

對象的創(chuàng)建與使用第十二頁,共一百一十頁,2022年,8月28日有兩種方法可存儲對象。

數(shù)據(jù)區(qū)代碼區(qū)對象1數(shù)據(jù)區(qū)代碼區(qū)對象2數(shù)據(jù)區(qū)代碼區(qū)對象n......圖5.1各對象完全獨(dú)立地安排內(nèi)存的方案數(shù)據(jù)區(qū)對象1數(shù)據(jù)區(qū)對象2數(shù)據(jù)區(qū)對象n......圖5.2各對象的代碼區(qū)共用的方案公共代碼區(qū)圖5.1是系統(tǒng)為每一個(gè)對象分配了全套的內(nèi)存,包括安放成員數(shù)據(jù)的數(shù)據(jù)區(qū)和安放成員函數(shù)的代碼區(qū)。但區(qū)別同一個(gè)類的各個(gè)不同的對象的屬性是由數(shù)據(jù)成員決定的,不同對象的數(shù)據(jù)成員的內(nèi)容是不一樣的;而行為(操作)是用函數(shù)來描述的,這些操作的代碼對所有的對象都是一樣的。圖5.2僅為每個(gè)對象分配一個(gè)數(shù)據(jù)區(qū),代碼區(qū)(放成員函數(shù)的區(qū)域)為各對象類共用。圖5.1對應(yīng)的是在類說明中定義函數(shù),而圖5.2對應(yīng)的是在類說明外部定義函數(shù)。第十三頁,共一百一十頁,2022年,8月28日5.1.3 對象的創(chuàng)建與使用下例給出對象使用的規(guī)則【例5.1】商品類對象應(yīng)用實(shí)例:#include<iostream.h>#include<iomanip.h>#include<string.h>//省略了類定義voidmain(){CGoods car;charstring[21];int number;floatpr;如果通過使用關(guān)鍵字inline,則系統(tǒng)也會(huì)自動(dòng)采用內(nèi)聯(lián)擴(kuò)展方法實(shí)現(xiàn),這時(shí)每個(gè)對象都有該函數(shù)一份獨(dú)立的拷貝。如RegisterGoods()函數(shù)可定義為:inlinevoidCGoods::RegisterGoods(char*name,intamount,floatprice){strcpy(Name,name);Amount=amount;Price=price;}則每個(gè)對象都有RegisterGoods()函數(shù)一份獨(dú)立的拷貝。第十四頁,共一百一十頁,2022年,8月28日成員名Name[21];Amount;Price;Total_value;10minicar5210minicar52string[21]numberPrminicar52minicarcout<<“請輸入汽車型號:”

;cin.getline(string,20);//輸入串長必須小于20cout<<“請依次輸入汽車數(shù)量與單價(jià):”

;cin>>number>>pr;car.RegisterGoods(string,number,pr);car.CountTotal();string[0]=’\0’;//字符串string清零car.GetName(string);//string賦值car.Namecout<<setw(20)<<string<<setw(5)<<car.GetAmount();//Acout<<setw(10)<<car.GetPrice()<<setw(20)<<car.GetTotal_value()<<endl;//B}第十五頁,共一百一十頁,2022年,8月28日5.1.3 對象的創(chuàng)建與使用注意:

因?yàn)閳D5.1的內(nèi)存分配方法明顯不合理,在具體的C++平臺中也僅當(dāng)類的成員函數(shù)不包括循環(huán)等復(fù)雜結(jié)構(gòu),并這些成員函數(shù)的函數(shù)體在類定義內(nèi)部直接定義時(shí),才用內(nèi)聯(lián)擴(kuò)展方式實(shí)現(xiàn)。上面所述對象的存儲方式是物理的,這是由計(jì)算機(jī)來完成的,它并不影響類在邏輯上的封裝性。程序設(shè)計(jì)是一個(gè)邏輯的概念,是由人來完成的。從程序員的角度看,邏輯上各對象是完全獨(dú)立的,不必去管物理上是怎樣存儲的,所以類的封裝在邏輯上是完善的。但若知道物理存儲方式,可加深對類與對象的理解,這也是程序員必備的知識。第十六頁,共一百一十頁,2022年,8月28日5.1.3 對象的創(chuàng)建與使用對象使用的規(guī)則很簡單,只要在對象名后加點(diǎn)號(點(diǎn)操作符,成員訪問運(yùn)算符(memberaccessoprator)之一),再加成員數(shù)據(jù)或成員函數(shù)名就可以了。但是這些成員必須是公有的成員,只有公有成員才能在對象的外面對它進(jìn)行訪問。如果在上面的例子中的A行和B行寫成:cout<<setw(20)<<car.Name<<setw(5)<<car.Amount;cout<<setw(10)<<car.Price<<setw(20)<<car.Total_value<<endl;那就錯(cuò)了,因?yàn)檫@里對象car的4個(gè)數(shù)據(jù)成員全是私有的,在外部是不能訪問的,必須用對象car所帶的公有函數(shù)進(jìn)行訪問。第十七頁,共一百一十頁,2022年,8月28日*5.2 從面向過程到面向?qū)ο?供學(xué)生閱讀:

上世紀(jì)六十年代中后期軟件危機(jī)發(fā)生之后,面向過程(procedure-oriented)的結(jié)構(gòu)化程序設(shè)計(jì)(structuredprogramming,SP)成為主流。結(jié)構(gòu)化程序設(shè)計(jì)的提出與發(fā)展是伴隨軟件日益龐大和復(fù)雜進(jìn)行的,但是當(dāng)軟件復(fù)雜到一定的程度后,結(jié)構(gòu)化程序設(shè)計(jì)也不能滿足需要。當(dāng)軟件規(guī)模超過一定的尺度后,采用結(jié)構(gòu)化程序設(shè)計(jì),其開發(fā)和維護(hù)就越來越難控制。其根本的原因就在于面向過程的結(jié)構(gòu)化程序設(shè)計(jì)的方法與現(xiàn)實(shí)世界(包括主觀世界和客觀世界)往往都不一致,結(jié)構(gòu)化程序設(shè)計(jì)的思想往往很難貫徹到底。第十八頁,共一百一十頁,2022年,8月28日5.2 從面向過程到面向?qū)ο?/p>

在結(jié)構(gòu)化程序設(shè)計(jì)中,采用的是“自頂向下,逐步細(xì)化(divideandconquer,stepwiserefinement)”的思想。它的具體操作方法是模塊化,是按功能來分的,所以也稱功能塊。也就是從一般事物中抽象出來的操作,在C++中稱為一個(gè)函數(shù),一個(gè)函數(shù)解決一個(gè)問題,即實(shí)現(xiàn)一個(gè)功能或一個(gè)操作。當(dāng)程序規(guī)模和復(fù)雜性達(dá)到一定程度時(shí)不可避免地引入大量的全局變量,優(yōu)良的模塊化沒法堅(jiān)持到底。在模塊化的思想中已經(jīng)出現(xiàn)了封裝的概念,這個(gè)封裝是把數(shù)據(jù)封裝到模塊中,即局部變量。但這是很不徹底的,因?yàn)槟K是功能的抽象,而數(shù)據(jù)則是具有其個(gè)性的,一但發(fā)生那怕是一點(diǎn)變化,抽象的功能模塊就不再適用了。如一個(gè)管理軟件,管理規(guī)則變化了,則管理模塊以及所有與之有聯(lián)系的模塊都必須更改,必須重新進(jìn)行功能抽象,必須重新建立模塊間聯(lián)系的規(guī)則。可維護(hù)性差成了制約結(jié)構(gòu)化程序設(shè)計(jì)應(yīng)用的瓶頸。第十九頁,共一百一十頁,2022年,8月28日5.2 從面向過程到面向?qū)ο?/p>

對象的概念是面向?qū)ο蠹夹g(shù)的核心所在。面向?qū)ο蠹夹g(shù)中的對象就是現(xiàn)實(shí)世界中某個(gè)具體的物理實(shí)體在計(jì)算機(jī)邏輯中的映射和體現(xiàn)。比如你所擁有的一部移動(dòng)電話,它是現(xiàn)實(shí)世界中的一個(gè)實(shí)體。它由天線、發(fā)射部件、接收部件、顯示屏、按鍵、專用集成電路芯片及外殼組成;它有著其實(shí)在的功能,可以打電話,可以發(fā)短消息,可以存儲、輸入和編輯各種個(gè)人信息,甚至可以上網(wǎng)。這樣一個(gè)實(shí)體可以在計(jì)算機(jī)世界中映射為一個(gè)計(jì)算機(jī)可以理解、可以操縱、具有前面所敘述的屬性和操作的對象。又如你們所擁有的一輛自行車,它由車架、車輪、腳踏和傳動(dòng)機(jī)構(gòu)、變速機(jī)構(gòu)等組成,它具有代步功能,它可以進(jìn)行變速騎行,特別要強(qiáng)調(diào)的是它有一些特征可以把你的這輛自行車與其他自行車區(qū)分開來,其中最重要的是鋼印號。這些都可以在面向?qū)ο蟮某绦蛑杏脤ο蠹捌鋵傩院筒僮髂M出來。第二十頁,共一百一十頁,2022年,8月28日5.2 從面向過程到面向?qū)ο髮ο箢愑?jì)算機(jī)世界實(shí)體抽象類別現(xiàn)實(shí)世界客觀世界抽象抽象實(shí)例化映射主觀世界圖5.3對象、實(shí)體與類

現(xiàn)實(shí)世界中的實(shí)體可以抽象出類別的概念。對應(yīng)于計(jì)算機(jī)世界就有一個(gè)類(class)的概念,因?yàn)轭愂且粋€(gè)抽象的概念的對應(yīng)體,所以計(jì)算機(jī)不給它分配內(nèi)存,只給對象分配內(nèi)存。圖5.3表達(dá)了計(jì)算機(jī)世界與現(xiàn)實(shí)世界之間的對應(yīng)關(guān)系。第二十一頁,共一百一十頁,2022年,8月28日5.4 構(gòu)造函數(shù)和析構(gòu)函數(shù)

定義對象時(shí),按現(xiàn)在已學(xué)過的知識無法進(jìn)行初始化,即無法對數(shù)據(jù)成員進(jìn)行賦初值的過程。數(shù)據(jù)成員,從封裝的目的出發(fā),應(yīng)該多為私有的,要對它們進(jìn)行初始化,看來必須用一個(gè)公有函數(shù)來進(jìn)行。同時(shí)這個(gè)函數(shù)應(yīng)該在且僅在定義對象時(shí)自動(dòng)執(zhí)行一次,否則就不是初始化了。在C++程序設(shè)計(jì)語言中這個(gè)函數(shù)稱為構(gòu)造函數(shù)。必須指出:調(diào)用構(gòu)造函數(shù)也是建立對象的唯一方法(聯(lián)合例外,見5.8)。第二十二頁,共一百一十頁,2022年,8月28日

5.4 構(gòu)造函數(shù)和析構(gòu)函數(shù)

5.

4.

1 構(gòu)造函數(shù)的定義與使用

5.

4.

3 析構(gòu)函數(shù)的定義5.

4.

4 成員對象與構(gòu)造函數(shù)5.

4.

2 拷貝構(gòu)造函數(shù)

第二十三頁,共一百一十頁,2022年,8月28日

對于對象的初始化,采用構(gòu)造函數(shù)(constructor)。當(dāng)需要對對象進(jìn)行初始化時(shí),總是編寫一個(gè)或一組構(gòu)造函數(shù)。構(gòu)造函數(shù)是特殊的公有成員函數(shù),其特征如下:1.函數(shù)名與類名相同。2.構(gòu)造函數(shù)無函數(shù)返回類型說明。注意是沒有而不是void,即什么也不寫,也不可寫void!實(shí)際上構(gòu)造函數(shù)有返回值,返回的就是構(gòu)造函數(shù)所創(chuàng)建的對象,見5.5節(jié)。3.在程序運(yùn)行時(shí),當(dāng)新的對象被建立,該對象所屬的類的構(gòu)造函數(shù)自動(dòng)被調(diào)用,在該對象生存期中也只調(diào)用這一次。4.構(gòu)造函數(shù)可以重載。嚴(yán)格地講,說明中可以有多個(gè)構(gòu)造函數(shù),它們由不同的參數(shù)表區(qū)分,系統(tǒng)在自動(dòng)調(diào)用時(shí)按一般函數(shù)重載的規(guī)則選一個(gè)執(zhí)行。構(gòu)造函數(shù)的定義與使用

第二十四頁,共一百一十頁,2022年,8月28日

構(gòu)造函數(shù)的定義與使用5.構(gòu)造函數(shù)可以在類中定義,也可以在類外定義。6.如果類說明中沒有給出構(gòu)造函數(shù),則C++編譯器自動(dòng)給出一個(gè)缺省的構(gòu)造函數(shù):

類名(void){}但只要我們定義了一個(gè)構(gòu)造函數(shù),系統(tǒng)就不會(huì)自動(dòng)生成缺省的構(gòu)造函數(shù)。缺省的構(gòu)造函數(shù),也可以由程序員自己來編,只要構(gòu)造函數(shù)是無參的或者只要各參數(shù)均有缺省值的,C++編譯器都認(rèn)為是缺省的構(gòu)造函數(shù),并且缺省的構(gòu)造函數(shù)只能有一個(gè)。如果對象的數(shù)據(jù)成員全為公有的,也可以在對象名后加“=”加“{}”,在花括號中順序填入全體數(shù)據(jù)成員的初始值.第二十五頁,共一百一十頁,2022年,8月28日

構(gòu)造函數(shù)的定義與使用

下面編寫5.1節(jié)中商品類CGoods的構(gòu)造函數(shù)??梢杂萌齻€(gè)參數(shù)來實(shí)現(xiàn)對4個(gè)數(shù)據(jù)成員的初始化:Cgoods(char*name,intamount,floatprice){strcpy(Name,name);Amount=amount;Price=price;Total_value=price*amount;

}在實(shí)際應(yīng)用時(shí),也可用兩個(gè)參數(shù):貨名和單價(jià),這時(shí)構(gòu)造函數(shù)為:Cgoods(char*name,floatprice){strcpy(Name,name);Price=price;Amount=0;Total_value=0.0;}這兩個(gè)構(gòu)造函數(shù)同時(shí)被說明(重載)。如果定義對象時(shí)的格式為:CGoodsCar1(“夏利2000”,30,98000.0);則調(diào)用了CGoods中的第一個(gè)構(gòu)造函數(shù),相當(dāng)于自動(dòng)調(diào)用:CGoods(“夏利2000”,30,98000.0);

第二十六頁,共一百一十頁,2022年,8月28日

構(gòu)造函數(shù)的定義與使用如果定義對象時(shí)的格式為:CGoodsCar2(“桑塔那2000”,164000.0);則調(diào)用的是第二個(gè)構(gòu)造函數(shù),參數(shù)為兩個(gè)。定義對象初始化時(shí)也可以把構(gòu)造函數(shù)顯式表示出來如:CGoodsCar1=CGoods(“夏利2000”,30,98000.0);如果還希望初始化時(shí),不帶任何參數(shù),可作如下定義:CGoods(){Name[0]=‘\0’;Price=0.0; Amount=0;Total_value=0.0;}但是定義對象時(shí)不能加括號。例如:CGoodsCar3,Car4();Car3是類CGoods的對象,定義時(shí)調(diào)用不帶參數(shù)的構(gòu)造函數(shù)。但是Car4()不是一個(gè)對象,而是一個(gè)不帶參數(shù)的函數(shù),它的返回值是類CGoods的對象。

第二十七頁,共一百一十頁,2022年,8月28日5.4.3 析構(gòu)函數(shù)的定義

當(dāng)一個(gè)對象定義時(shí),C++自動(dòng)調(diào)用構(gòu)造函數(shù)建立該對象并進(jìn)行初始化,那么當(dāng)一個(gè)對象的生命周期結(jié)束時(shí),C++也會(huì)自動(dòng)調(diào)用一個(gè)函數(shù)注銷該對象并進(jìn)行善后工作,這個(gè)特殊的成員函數(shù)即析構(gòu)函數(shù)(destructor):1.

構(gòu)函數(shù)名與類名相同,但在前面加上字符‘~’,如~CGoods()。2.

析構(gòu)函數(shù)無函數(shù)返回類型,與構(gòu)造函數(shù)在這方面是一樣的。但析構(gòu)函數(shù)不帶任何參數(shù)。3.一個(gè)類有一個(gè)也只有一個(gè)析構(gòu)函數(shù),這與構(gòu)造函數(shù)不同。析構(gòu)函數(shù)可以缺省。4.對象注銷時(shí),系統(tǒng)自動(dòng)調(diào)用析構(gòu)函數(shù)。第二十八頁,共一百一十頁,2022年,8月28日5.

3

引用

在有關(guān)函數(shù)的學(xué)習(xí)中,我們知道C++函數(shù)中參數(shù)的傳遞方式是傳值。在函數(shù)域中為參數(shù)重新分配內(nèi)存,而把實(shí)參的數(shù)值傳遞到新分配的內(nèi)存中。它的優(yōu)點(diǎn)是有效避免函數(shù)的副作用,在函數(shù)調(diào)用中不會(huì)無意中修改了實(shí)參的值。但如果就是要求改變實(shí)參的值,怎么辦呢?再者,如果參數(shù)是一些簡單的數(shù)據(jù)類型,占據(jù)內(nèi)存不多,重新分配內(nèi)存問題不大;如果實(shí)參是一個(gè)復(fù)雜的對象,重新分配內(nèi)存會(huì)引起程序執(zhí)行效率大大下降,怎么辦呢?在C++中有一種新的導(dǎo)出型數(shù)據(jù)類型—引用(reference)可以解決上面的難題。引用又稱別名(alias)。第二十九頁,共一百一十頁,2022年,8月28日5.

3

引用引用是一種非常特殊的數(shù)據(jù)類型,它不是定義一個(gè)新的變量,而是給一個(gè)已經(jīng)定義的變量重新起一個(gè)別名,也就是C++系統(tǒng)不為引用類型變量分配內(nèi)存空間。引用主要用于函數(shù)之間的數(shù)據(jù)傳遞。引用定義的格式為:類型&引用變量名=已定義過的變量名;例如:doublenumber;double&newnum=number;newnum是新定義的引用類型變量,它是變量number的別名,內(nèi)存分配見下圖。

number稱為引用newnum的關(guān)聯(lián)變量。“&”(仍讀作ampersand)在這里是引用的說明符。必須注意number和newnum都是double類型。如在程序中修改了newnum也就是修改了number,兩位一體。第三十頁,共一百一十頁,2022年,8月28日5.

3

引用

Xyd1d2temp1.4142.7181.4142.7181.414【例5.2】引用作為函數(shù)的形參使用一個(gè)函數(shù)來交換兩個(gè)數(shù)據(jù)。內(nèi)存分配見圖5.5。#include<iostream.h>

voidswap(double&d1,double&d2){doubletemp;temp=d1;d1=d2;d2=temp;}voidmain(void){doublex,y;cout<<"請輸入x和y的值"<<'\n';cin>>x>>y;swap(x,y);cout<<"x="<<x<<'\t'<<"y="<<y<<'\n';}圖5.5參數(shù)d1、d2為引用時(shí)內(nèi)存分配示意

第三十一頁,共一百一十頁,2022年,8月28日5.3引用

圖5.6普通返回

圖5.7引用返回【例5.3】采用不同返回方式的求正方形面積函數(shù)的比較引用可以作為函數(shù)的返回值。一般函數(shù)返回值時(shí),要生成一個(gè)臨時(shí)變量作為返回值的拷貝,而用引用作為返回值時(shí),不生成值的拷貝?!纠?.3】采用不同返回方式的求正方形面積函數(shù)的比較。#include<iostream.h>doubletemp;doublefsqr1(doublea){temp=a*a;returntemp;}double&fsqr2(doublea){temp=a*a;returntemp;}voidmain(){doublex=fsqr1(5.5);//第一種情況

doubley=fsqr2(5.5);//第二種情況

cout<<"x="<<x<<'\t‘<<"y="<<y<<endl;}運(yùn)行結(jié)果為:x=30.25y=30.25運(yùn)行結(jié)果一樣,但在內(nèi)存中的活動(dòng)卻不同。

第三十二頁,共一百一十頁,2022年,8月28日5.

3

引用*一個(gè)聲明返回值為引用的函數(shù)可以作為左值??蛇x內(nèi)容?!纠?.4】統(tǒng)計(jì)學(xué)生成績,分?jǐn)?shù)在80分以上的為A類,60分以上,80分以下的為B類,60分以下為C類。#include<iostream.h>int&level(intgrade,int&typeA,int&typeB,int&typeC){ if(grade>=80)returntypeA; elseif(grade>=60)returntypeB; elsereturntypeC;}voidmain(){ inttypeA=0,typeB=0,typeC=0; intstudent=9; intarray[9]={90,75,83,66,58,40,80,85,71}; for(inti=0;i<student;i++)

level(array[i],typeA,typeB,typeC)++;//函數(shù)調(diào)用為左值

cout<<"A類學(xué)生數(shù):"<<typeA<<endl; cout<<"B類學(xué)生數(shù):"<<typeB<<endl; cout<<"C類學(xué)生數(shù):"<<typeC<<endl;}第三十三頁,共一百一十頁,2022年,8月28日5.

3

引用注意:1.對數(shù)組只能引用數(shù)組元素,不能引用數(shù)組(數(shù)組名本身為地址)。2.不能定義引用的引用(引用也是地址),所以當(dāng)函數(shù)的參數(shù)為引用時(shí),引用不能作實(shí)參。3.const引用:引用在內(nèi)部存放的是被引用對象的地址,不可尋址的值是不能引用的;當(dāng)引用作為形參時(shí),實(shí)參也不能使用不可尋址的值,更不可能進(jìn)行類型轉(zhuǎn)換(如:實(shí)數(shù)轉(zhuǎn)換為整數(shù))。但是const引用不同,它是只讀的,為了絕對保證不會(huì)發(fā)生誤改,編譯器實(shí)現(xiàn)const引用時(shí),生成一個(gè)臨時(shí)對象,引用實(shí)際上指向該臨時(shí)對象,但用戶不能訪問它。所以const引用可以實(shí)現(xiàn)不可尋址的值(包括字面常量)的引用,例如:doubledval=1024;constint&ri=dval;是正確的,編譯器將其轉(zhuǎn)換為:doubledval=1024;inttemp=dval;constint&ri=temp;因有臨時(shí)對象,引用和類型轉(zhuǎn)換都實(shí)現(xiàn)了。當(dāng)const引用作為形參時(shí),實(shí)參也能使用不可尋址的值,并能進(jìn)行類型轉(zhuǎn)換(如例5_7后的說明和例5_7_1:調(diào)用構(gòu)造函數(shù)將實(shí)數(shù)轉(zhuǎn)換為復(fù)數(shù))。第三十四頁,共一百一十頁,2022年,8月28日5.4.2 拷貝構(gòu)造函數(shù)

同一個(gè)類的對象在內(nèi)存中有完全相同的結(jié)構(gòu),如果作為一個(gè)整體進(jìn)行復(fù)制或稱拷貝是完全可行的。這個(gè)拷貝過程只需要拷貝數(shù)據(jù)成員,而函數(shù)成員是共用的(只有一份拷貝)。在建立對象時(shí)可用同一類的另一個(gè)對象來初始化該對象,這時(shí)所用的構(gòu)造函數(shù)稱為拷貝初始化構(gòu)造函數(shù)(CopyConstructor)。對于CGoods類,可以定義拷貝構(gòu)造函數(shù)為:CGoods(CGoods&cgd){Strcpy(Name,cgd.Name);Price=cgd.price;Amount=cgd.Amount;Total_value=cgd.Total_value;}實(shí)驗(yàn)六

5.4.4成員對象與構(gòu)造函數(shù)

5.1.4名字空間域與類域第三十五頁,共一百一十頁,2022年,8月28日5.4.2 拷貝構(gòu)造函數(shù)

這里必須注意拷貝構(gòu)造函數(shù)的參數(shù)——同類(class)的對象采用的是引用的方式。如果把一個(gè)真實(shí)的類對象作為參數(shù)傳遞到拷貝構(gòu)造函數(shù),會(huì)引起無窮遞歸。所以必須將拷貝構(gòu)造函數(shù)的參數(shù)定義為一個(gè)類的對象的引用。通常情況下,這種按成員語義支持已經(jīng)足夠。但在某些情況下,它對類與對象的安全性和處理的正確性還不夠,這時(shí)就要求類的設(shè)計(jì)者提供特殊的拷貝構(gòu)造函數(shù)和拷貝賦值操作符的定義。

系統(tǒng)會(huì)自動(dòng)提供,稱為缺省的按成員語義支持的拷貝構(gòu)造函數(shù),每個(gè)類成員被依次拷貝,亦稱為缺省的按成員初始化。按成員作拷貝是通過依次拷貝每個(gè)數(shù)據(jù)成員實(shí)現(xiàn)的,而不是對整個(gè)類對象按位拷貝。賦值運(yùn)算符“=”稱缺省的按成員拷貝賦值操作符,同類對象之間可以用“=”直接拷貝。

第三十六頁,共一百一十頁,2022年,8月28日5.4.2 拷貝構(gòu)造函數(shù)

當(dāng)成員函數(shù)的參數(shù)為同一類(class)的對象或它的引用,在函數(shù)體內(nèi)使用參數(shù)對象的私有數(shù)據(jù)成員時(shí),可用對象名加成員訪問操作符點(diǎn)號進(jìn)行。從邏輯上講,每個(gè)對象有自己的成員函數(shù),訪問同類其他對象的私有數(shù)據(jù)成員應(yīng)通過該對象的公有函數(shù),不能直接訪問。但在物理上只有一個(gè)成員函數(shù)拷貝,所以直接訪問是合理的。其他實(shí)例可見5.5節(jié)。對本對象的數(shù)據(jù)成員不加點(diǎn)號,參見6.2節(jié)。注意,僅在成員函數(shù)中可以這樣做。下面來看一個(gè)實(shí)例。有一個(gè)程序段:CGoodCar1(“夏利2000”,30,98000.00);//調(diào)用三個(gè)參數(shù)的構(gòu)造函數(shù)CGoodCar2=Car1;//調(diào)用拷貝構(gòu)造函數(shù)CGoodCar3(Car1);//調(diào)用拷貝構(gòu)造函數(shù),Car1為實(shí)參這樣三個(gè)對象的初始化結(jié)果完全一樣。在類定義中如果沒有顯式給出構(gòu)造函數(shù)時(shí),并不是不用構(gòu)造函數(shù),而是由系統(tǒng)自動(dòng)調(diào)用缺省的構(gòu)造函數(shù)或缺省的拷貝構(gòu)造函數(shù)。如果有程序設(shè)計(jì)者定義的構(gòu)造函數(shù)(包括拷貝構(gòu)造函數(shù)),則按函數(shù)重載的規(guī)律,調(diào)用合適的構(gòu)造函數(shù)。第三十七頁,共一百一十頁,2022年,8月28日5.4.2 拷貝構(gòu)造函數(shù)拷貝構(gòu)造函數(shù)并不只是在同類的一個(gè)對象去初始化該類的另一個(gè)對象時(shí)使用,它還在另二個(gè)方面使用:1.當(dāng)函數(shù)的形參是類的對象,調(diào)用函數(shù)時(shí),進(jìn)行形參與實(shí)參結(jié)合時(shí)使用。這時(shí)要在內(nèi)存新建立一個(gè)局部對象,并把實(shí)參拷貝到新的對象中。理所當(dāng)然也調(diào)用拷貝構(gòu)造函數(shù)。2.當(dāng)函數(shù)的返回值是類對象,函數(shù)執(zhí)行完成返回調(diào)用者時(shí)使用。理由也是要建立一個(gè)臨時(shí)對象中,再返回調(diào)用者。為什么不直接用要返回的局部對象呢?因?yàn)榫植繉ο笤陔x開建立它的函數(shù)時(shí)就消亡了,不可能在返回調(diào)用函數(shù)后繼續(xù)生存,所以在處理這種情況時(shí),編譯系統(tǒng)會(huì)在調(diào)用函數(shù)的表達(dá)式中創(chuàng)建一個(gè)無名臨時(shí)對象,該臨時(shí)對象的生存周期只在函數(shù)調(diào)用處的表達(dá)式中。所謂return對象,實(shí)際上是調(diào)用拷貝構(gòu)造函數(shù)把該對象的值拷入臨時(shí)對象。如果返回的是變量,處理過程類似,只是不調(diào)用構(gòu)造函數(shù)。第三十八頁,共一百一十頁,2022年,8月28日實(shí)驗(yàn)六類與對象的實(shí)踐——范例1范例:設(shè)計(jì)一個(gè)程序,定義一個(gè)矩形類,包括數(shù)據(jù)成員和函數(shù)成員。要求有構(gòu)造函數(shù)、析構(gòu)函數(shù),完成賦值、修改、顯示等功能的接口,并編寫main函數(shù)測試,要求用一個(gè)對象初始化另一對象。[分析]

要確定一個(gè)矩形(四邊都是水平或垂直方向,不能傾斜),只要確定其左上角和右下角的x和y坐標(biāo)即可,因此應(yīng)包括四個(gè)數(shù)據(jù)成員,left,right,top,bottom,即左右上下四個(gè)邊界值。由構(gòu)造函數(shù)對數(shù)據(jù)成員賦值,賦值函數(shù)完成未初始化的矩形賦值,修改函數(shù)可以修改各數(shù)據(jù)成員,顯示函數(shù)則給出該矩形參數(shù)。classRectangle{ intleft,top,right,bottom;public: Rectangle(intl=0,intt=0,intr=0,intb=0);

//缺省構(gòu)造函數(shù)必須在此指定缺省實(shí)參

~Rectangle(){};//析構(gòu)函數(shù),在此函數(shù)體為空

voidAssign(intl,intt,intr,intb); voidSetLeft(intt){left=t;}//以下皆為內(nèi)聯(lián)成員函數(shù)

voidSetRight(intt){right=t;}voidSetTop(intt){top=t;} voidSetBottom(intt){bottom=t;} voidShow();};//將上述內(nèi)容保存為rect.h第三十九頁,共一百一十頁,2022年,8月28日實(shí)驗(yàn)六類與對象的實(shí)踐——范例1#include<iostream.h>#include“rect.h”//構(gòu)造函數(shù),帶缺省參數(shù),缺省值為全0,在聲明中指定

Rectangle::Rectangle(intl,intt,intr,intb){left=l;top=t;right=r;bottom=b;}voidRectangle::Assign(intl,intt,intr,intb){left=l;top=t;right=r;bottom=b;}voidRectangle::Show(){cout<<”left-toppointis(”<<left<<”,”<<top<<”)”<<’\n’;cout<<”right-bottompointis(”<<right<<”,”<<bottom<<”)”<<’\n’;}voidRectangle::Draw(CDC*pDC){pDC->Rectangle(left,top,right,bottom);}//將上述內(nèi)容保存為rect.cpp第四十頁,共一百一十頁,2022年,8月28日實(shí)驗(yàn)六類與對象的實(shí)踐——范例1#include<iostream.h>#include“rect.h”voidmain(){ Rectanglerect; rect.Show(); rect.Assign(100,200,300,400); rect.Show(); Rectanglerect1(0,0,200,200); rect1.Show(); Rectanglerect2(rect1); rect2.Show();}第四十一頁,共一百一十頁,2022年,8月28日*

名字空間域和類域

名字空間域內(nèi)容為閱讀,不列入課堂教學(xué)!

在C++中支持三種域:局部域、名字空間域和類域。

名字空間域是隨標(biāo)準(zhǔn)C++而引入的。它相當(dāng)于一個(gè)更加靈活的文件域(全局域),可以用花括號把文件的一部分括起來,并以關(guān)鍵字namespace開頭給它起一個(gè)名字,這就是名字空間。例如:namespacens1{floata,b,c;fun1(){……}…}花括號括起來的部分稱聲明塊。聲明塊中可以包括:類、變量(帶有初始化)、函數(shù)(帶有定義)等。在該域外使用域內(nèi)的成員時(shí),需加上名字空間名作為前綴,后面加上域操作符“::”,就像在類定義外定義成員函數(shù)一樣。這里添加了名字空間名稱的成員名被稱為限定修飾名(qualifiedname)。如:ns1::a,ns1::fun1()等等。最外層的名字空間域稱為全局名字空間域(globalnamespacescope),即文件域,對比例4.9在塊內(nèi)訪問全局量,只用域操作符“::”。第四十二頁,共一百一十頁,2022年,8月28日*

名字空間域和類域

與局部域分層次一樣,名字空間域也可分層嵌套,也同樣有分層屏蔽作用。例如:namespacecplusplus_primer{namespaceMatrixlib{ //名字空間嵌套

classmatrix{……} //名字空間類成員matrix …...}}訪問名字空間域中的成員,使用限定修飾名非常不方便,如訪問matrix,每次都寫:cplusplus_primer::Matrixlib::matrix太麻煩。使用using聲明可只寫一次限定修飾名。using聲明以關(guān)鍵字using開頭,后面是被限定修飾的(qualified)名字空間成員名:usingcplusplus_primer::Matrixlib::matrix;//名字空間類成員matrix的using聲明以后在程序中使用matrix時(shí),就可以直接使用成員名,而不必使用限定修飾名。 第四十三頁,共一百一十頁,2022年,8月28日*

名字空間域和類域

名字空間域的引入,主要是為了解決全局名字空間污染(globalnamespacepollution)問題,即防止程序中的全局實(shí)體名與C++各種庫中聲明的全局實(shí)體名沖突。C++庫是C++程序員編程最有力的工具,對于標(biāo)準(zhǔn)的和通用的部分,程序員不必也不應(yīng)該自己編寫,程序員的注意力應(yīng)集中在程序的個(gè)性部分。使用using指示符可以一次性地使名字空間中所有成員都可以直接被使用,比using聲明方便。using指示符以關(guān)鍵字using開頭,后面是關(guān)鍵字namespace,然后是名字空間名。標(biāo)準(zhǔn)C++庫中的所有組件都是在一個(gè)被稱為std的名字空間中聲明和定義的。在采用標(biāo)準(zhǔn)C++的平臺上使用標(biāo)準(zhǔn)C++庫中的組件,只要寫一個(gè)using指示符:usingnamespacestd;就可以直接使用標(biāo)準(zhǔn)C++庫中的所有成員。這是很方便的。名字空間可以不連續(xù),分為多段,但它們?nèi)允峭粋€(gè)名字空間。名字空間域不能定義在函數(shù)聲明、函數(shù)定義或類定義的內(nèi)部。第四十四頁,共一百一十頁,2022年,8月28日

名字空間域和類域

類體也定義了一個(gè)域稱為類域。在類域中說明的標(biāo)識符僅在該類的類域內(nèi)有效。必須加上“類名::”作限定修飾。類的實(shí)體——對象中的公有成員也可以在對象之外訪問,但必須使用成員訪問操作符“.”,對象名+“.”+成員名。定義類本身的目的就是要實(shí)現(xiàn)一個(gè)封裝性,對外是封閉的,對內(nèi)是開放的,在程序中并不總是需要用成員訪問符之類來引用類成員。多數(shù)程序代碼本身就在類域中,這些程序可以直接訪問類成員。類域作為課堂教學(xué)內(nèi)容,可在此處講,也可放在節(jié)之后節(jié)之前講,第四十五頁,共一百一十頁,2022年,8月28日

名字空間域和類域在類域中類成員在類體中被聲明的順序同樣很重要,后聲明的成員不能被先聲明的成員引用。但編譯器對名字(標(biāo)識符)的解析分兩步,第一步查找在聲明中用到的名字,包括數(shù)據(jù)成員和函數(shù)成員聲明中用到的參數(shù)類型,第二步才是函數(shù)成員體內(nèi)的名字。例如:classstring{ //字符串類

public:typedefintindex_type;//為易讀易懂直接用下標(biāo)型命名

charGetstringElement(index_typeelem){

//內(nèi)聯(lián)函數(shù),取串中第幾個(gè)元素

returnAstring[elem];} //

Astring未說明

private:charAstring[30]; //Astring后說明};表面上看是錯(cuò)的;實(shí)際上是對的。因?yàn)锳string名字的解析是在第一步,而內(nèi)聯(lián)函數(shù)使用它是在第二步。第四十六頁,共一百一十頁,2022年,8月28日

名字空間域和類域

在類域中不僅有數(shù)據(jù)和函數(shù)成員,也可以有類類型說明,可以稱為類成員,或成員類,其作用域?yàn)轭愑颍隽祟愓f明之外無效。例如嵌套類:classstudent{

public: classstudentID{ //成員類、嵌套類

intvalue; public: ……

};private:

studentIDid;

//嵌套類說明的私有對象

charname[20];}students1,s2;student::studentIDkk;系統(tǒng)并不為嵌套類分配內(nèi)存空間,即使在student類對象s1和s2中也不為嵌套類studentID分配內(nèi)存空間,而只為s1.id和s2.id分配內(nèi)存。在本例中嵌套類studentID在student類外也可以引用,同樣要加限定修飾(student::),因?yàn)閟tudentID為公有,如為私有則外部不可引用。嵌套類主要功能是防止全局類說明污染,防止類名重復(fù)現(xiàn)象。第四十七頁,共一百一十頁,2022年,8月28日5.4.4 成員對象與構(gòu)造函數(shù)

在定義類的對象時(shí)不僅要對對象進(jìn)行初始化,還要先對成員對象進(jìn)行初始化。對成員對象初始化,必須調(diào)用該成員對象的構(gòu)造函數(shù)來實(shí)現(xiàn)。C++中對含對象成員的類對象的構(gòu)造函數(shù)有特殊的格式:類名::構(gòu)造函數(shù)名(參數(shù)總表):對象成員1(參數(shù)表1),對象成員2(參數(shù)表2),……對象成員n(參數(shù)表n){……}冒號后用逗號隔開的為要初始化的對象成員,附在后面的參數(shù)表1,…,參數(shù)表n依次為調(diào)用相應(yīng)對象成員所屬的構(gòu)造函數(shù)時(shí)的實(shí)參表。這些表中的參數(shù)通常來自冒號前的參數(shù)總表。

【例5.5_1】含有成員對象的類的構(gòu)造函數(shù):#include<iostream.h>#include<string>classstudent{public:第四十八頁,共一百一十頁,2022年,8月28日classstudentID{longvalue;public:studentID(longid=0){value=id;cout<<"賦給學(xué)生的學(xué)號:"<<value<<endl; }~studentID(){cout<<"刪除學(xué)號:"<<value<<endl;}};//學(xué)號類定義,注意分號private:

studentIDid;charname[20];public:student(char*sname=“noname”,longsid=0):id(sid){//sname現(xiàn)暫看作字符串,char*是指向字符的指針類型

cout<<“學(xué)生名:”<<sname<<endl;strcpy(name,sname);}第四十九頁,共一百一十頁,2022年,8月28日這樣運(yùn)行結(jié)果為:賦給學(xué)生的學(xué)號:08002132學(xué)生名:朱明刪去學(xué)號:08002132在student構(gòu)造函數(shù)頭部的冒號表示要對對象成員的構(gòu)造函數(shù)進(jìn)行調(diào)用。但在構(gòu)造函數(shù)的聲明中,冒號及冒號以后部分必須略去。*以下供學(xué)生閱讀:不用這種格式將無法把學(xué)號傳遞給學(xué)號類對象,如例5_5:student(char*sname,longsid=0){

//sname現(xiàn)暫看作字符串,char*是指向字符的指針類型,見第6章;

cout<<"學(xué)生名:"<<sname<<endl;strcpy(name,sname);

studentIDid(sid);}//A目的是將學(xué)號轉(zhuǎn)給學(xué)號類對象,但未達(dá)目的5.4.4 成員對象與構(gòu)造函數(shù)第五十頁,共一百一十頁,2022年,8月28日5.4.4 成員對象與構(gòu)造函數(shù)這段程序希望通過構(gòu)造函數(shù)中的A行進(jìn)行初始化,把學(xué)生名“朱明”和學(xué)號08002132賦給對象SS。運(yùn)行結(jié)果:賦給學(xué)生的學(xué)號:0//首先調(diào)用對象ss的對象成員id的構(gòu)造函數(shù)學(xué)生名:朱明//對象ss的數(shù)據(jù)成員name賦給學(xué)生的學(xué)號:08002132

//ss構(gòu)造函數(shù)中建立的局部對象id中數(shù)據(jù)成員value初值刪去學(xué)號:08002132//局部對象id析構(gòu)刪去學(xué)號:0//對象成員id析構(gòu)這表明構(gòu)造函數(shù)中的A行并沒有把學(xué)號08002132賦給對象ss中的對象成員id,而是在構(gòu)造函數(shù)中構(gòu)造了一個(gè)名字也為id的studentID局部對象,它只在構(gòu)造函數(shù)中生存,構(gòu)造函數(shù)返回時(shí)該局部對象被析構(gòu)。而對象ss中的對象成員id則因?yàn)闃?gòu)造函數(shù)中沒有指定對它進(jìn)行初始化的值,所以系統(tǒng)按缺省方式調(diào)用了StudentID()建立了對象成員id,所以有第一條輸出“賦給學(xué)生的學(xué)號:0“。A行也可以寫為:

studentID::

studentID(sid);結(jié)果一樣,無法把學(xué)號傳過去,這時(shí)構(gòu)造函數(shù)建立的是一個(gè)無名的studentID局部對象。第五十一頁,共一百一十頁,2022年,8月28日5.4.4 成員對象與構(gòu)造函數(shù)對于不含對象成員的類對象的初始化,也可以套用以上的格式,把部分只需要直接賦初值的變量初始化寫在冒號的右邊:類名::構(gòu)造函數(shù)名(參數(shù)表):變量1(初值1),……,變量n(初值n){……}當(dāng)然也可以把一部分變量重新放回花括號中的函數(shù)體。冒號以后部分實(shí)際是函數(shù)體的一部分,所以在構(gòu)造函數(shù)的聲明中,冒號及冒號以后部分必須略去。第五十二頁,共一百一十頁,2022年,8月28日5.4 構(gòu)造函數(shù)和析構(gòu)函數(shù)

對于不同作用域的對象類型,構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用如下:

1.對全局定義的對象,當(dāng)程序進(jìn)入入口函數(shù)main之前對象就已經(jīng)定義,這時(shí)要調(diào)用構(gòu)造函數(shù)。整個(gè)程序結(jié)束時(shí)調(diào)用析構(gòu)函數(shù)。

2.對于局部定義的對象,每當(dāng)程序控制流到達(dá)該對象定義處時(shí),調(diào)用構(gòu)造函數(shù)。當(dāng)程序控制走出該局部域時(shí),則調(diào)用析構(gòu)函數(shù)。

3.對于靜態(tài)局部定義的對象,在程序控制首次到達(dá)該對象定義處時(shí),調(diào)用構(gòu)造函數(shù)。當(dāng)整個(gè)程序結(jié)束時(shí)調(diào)用析構(gòu)函數(shù)。第五十三頁,共一百一十頁,2022年,8月28日5.4 構(gòu)造函數(shù)和析構(gòu)函數(shù)

在正確定義了構(gòu)造函數(shù)和析構(gòu)函數(shù)的前提下,在一個(gè)健康的程序中,每個(gè)創(chuàng)建的對象必然有一個(gè)而且只有一個(gè)撤消動(dòng)作。請讀者根據(jù)下面程序執(zhí)行結(jié)果,注意每個(gè)對象創(chuàng)建和撤消的對應(yīng)關(guān)系:例5.6演示了對象創(chuàng)建和撤消的對應(yīng)關(guān)系。請參看VC++平臺上的演示。注意:先建立的對象后撤銷。本例可見構(gòu)造函數(shù)和析構(gòu)函數(shù)使用頻繁,最好在類說明之外定義構(gòu)造函數(shù)和析構(gòu)函數(shù),以確保一個(gè)函數(shù)拷貝,避免代碼膨脹。第五十四頁,共一百一十頁,2022年,8月28日

5.5 運(yùn)算符的重載

運(yùn)算符的重載實(shí)際是一種特殊的函數(shù)重載,必須定義一個(gè)函數(shù),并告訴C++編譯器,當(dāng)遇到該重載的運(yùn)算符時(shí)調(diào)用此函數(shù)。這個(gè)函數(shù)叫做運(yùn)算符重載函數(shù),通常為類的成員函數(shù)。定義運(yùn)算符重載函數(shù)的一般格式:返回值類型類名::operator重載的運(yùn)算符(參數(shù)表) {……}operator是關(guān)鍵字,它與重載的運(yùn)算符一起構(gòu)成函數(shù)名。因函數(shù)名的特殊性,C++編譯器可以將這類函數(shù)識別出來。

C++中沒有復(fù)數(shù)類型,我們可以自己來定義一個(gè)復(fù)數(shù)類(class),同樣可以用+、-、*、/來進(jìn)行復(fù)數(shù)的算術(shù)運(yùn)算。第五十五頁,共一百一十頁,2022年,8月28日5.5 運(yùn)算符的重載例5.7定義了復(fù)數(shù)類,可完成復(fù)數(shù)基本運(yùn)算,并應(yīng)用它進(jìn)行復(fù)數(shù)運(yùn)算。請參看VC++平臺上的演示。classComplex{ double Real,Image;public:Complex(doubler=0.0,doublei=0.0):Real(r),Image(i){}//定義構(gòu)造函數(shù)

Complex(Complex&com){Real=com.Real;Image=com.Image;}//定義拷貝構(gòu)造函數(shù)

voidPrint(){cout<<"Real="<<Real<<'\t'<<"Image="<<Image<<'\n';}Complexoperator+(Complex);Complexoperator+(double);voidoperator=(Complex);voidoperator+=(Complex);doubleabs(void);Complexoperator*(Complex);Complexoperator/(Complex);};第五十六頁,共一百一十頁,2022年,8月28日在本例中重載了運(yùn)算符“+”、“=”、“+=”和“*”、“/”,以及求模(絕對值)的函數(shù)abs(),可以進(jìn)行復(fù)數(shù)運(yùn)算。首先來看“+”的重載。ComplexComplex::operator+(Complexc){

//顯式說明局部對象

ComplexTemp(Real+c.Real,Image+c.Image);

//注意:直接寫對象c的私有成員,不用調(diào)c的公有函數(shù)處理

returnTemp;}在做c=c2+c3時(shí),C++編譯器把表達(dá)式c2+c3解釋為:c2.operator+(c3);這樣一個(gè)函數(shù)調(diào)用過程,函數(shù)c2.operator創(chuàng)建一個(gè)局部的Complex對象Temp,把出現(xiàn)在表達(dá)式中的兩個(gè)Complex類對象c2和c3的實(shí)部之和及虛部之和暫存其內(nèi),然后把這個(gè)局部對象返回,賦給Complex類對象c(注意這里調(diào)用了拷貝構(gòu)造函數(shù)生成一個(gè)無名臨時(shí)對象過渡)。參見圖5.8。5.5 運(yùn)算符的重載第五十七頁,共一百一十頁,2022年,8月28日Temp.Real=Real+c2.Real;Temp.Image=Image+c3.Image;c=return(Temp);RealImagec3.Realc3.Image=+局部對象Temp當(dāng)前對象c2對象c3圖5.8顯式說明臨時(shí)對象的“+”運(yùn)算符執(zhí)行過程可以用隱式的臨時(shí)對象替換顯式的對象Temp,如例中重載的Operator+(double),它執(zhí)行復(fù)數(shù)與實(shí)數(shù)的加法。ComplexComplex::operator+(doubled){returnComplex(Real+d,Image);}//隱式說明局部對象在return后面跟的是一個(gè)表達(dá)式,在這里表達(dá)式中調(diào)用的是類的構(gòu)造函數(shù),構(gòu)造函數(shù)無返回說明,而不是無返回值,實(shí)際上編譯器為表達(dá)式中的構(gòu)造函數(shù)創(chuàng)立了一個(gè)臨時(shí)對象,臨時(shí)對象生命期就在該表達(dá)式中,返回值就是該臨時(shí)對象。

第五十八頁,共一百一十頁,2022年,8月28日

5.5 運(yùn)算符的重載

使用引用類型變量作為運(yùn)算符重載函數(shù)的參數(shù),可以提高復(fù)數(shù)類型運(yùn)算的效率。復(fù)數(shù)與復(fù)數(shù)相加的Operator+成員函數(shù)的最終形式:Complexcomplex::operator+(constcomplex&c){returncomplex(real+c.real,Image+c.Image);}這里采用complex對象的引用而不是對象本身,調(diào)用時(shí)不再重新分配內(nèi)存建立一個(gè)復(fù)制的對象,函數(shù)效率會(huì)更高。而在引用形式參數(shù)類型說明前加const關(guān)鍵字,表示被引用的實(shí)參是不可改變的,如程序員不當(dāng)心在函數(shù)體中重新賦值了被引用的實(shí)參,C++編譯器會(huì)認(rèn)為出錯(cuò)。

采用引用為參數(shù)時(shí),從理論上講實(shí)參必須為左值,不能為表達(dá)式,如【例5.7】中c=c+d是正確的,但c=c+0.5是不允許的,0.5不是左值,但在這種情況下VC++允許。

第五十九頁,共一百一十頁,2022年,8月28日5.5 運(yùn)算符的重載

在缺省的情況下,C++編譯器為每個(gè)類生成一個(gè)缺省的賦值操作,用于同類的兩個(gè)對象之間的相互賦值,缺省的語義是類成員逐個(gè)相互賦值。對復(fù)數(shù)類complex如果沒有重載賦值運(yùn)算符=,復(fù)數(shù)的賦值語義是:Complex&Complex::operator=(Complex&c){real=c.real;image=c.imagereturn*this;}//參見6.2節(jié)這種缺省的賦值操作格式對所有類是固定的,這種缺省的格式對復(fù)數(shù)是合適的,但對其他類缺省的賦值可能產(chǎn)生問題,那時(shí)需重載。對所有的類對象,賦值運(yùn)算符“=”即缺省的按成員拷貝賦值操作符(CopyAssignmentOperator),同類對象之間可以用“=”直接拷貝。因?yàn)槿笔〉馁x值操作返回一個(gè)復(fù)數(shù)的引用,所以它可以進(jìn)行連續(xù)賦值如:a=b=c=d;第六十頁,共一百一十頁,2022年,8月28日5.5 運(yùn)算符的重載

重載的運(yùn)算符“+=”標(biāo)準(zhǔn)算法是:Complex&Complex::operator+=(Complex&com){real+=com.real;image+=com.image;return*this;//參見6.2節(jié)}小結(jié):

1.運(yùn)算符重載函數(shù)的函數(shù)名必須為關(guān)鍵字Operator加一個(gè)合法的運(yùn)算符。在調(diào)用該函數(shù)時(shí),將右操作數(shù)作為函數(shù)的實(shí)參。第六十一頁,共一百一十頁,2022年,8月28日5.5 運(yùn)算符的重載

2.

當(dāng)用類的成員函數(shù)實(shí)現(xiàn)運(yùn)算符的重載時(shí),運(yùn)算符重載函數(shù)的參數(shù)(當(dāng)為雙目運(yùn)算符時(shí))為一個(gè)或(當(dāng)為單目運(yùn)算符時(shí))沒有。運(yùn)算符的左操作數(shù)一定是對象,因?yàn)橹剌d的運(yùn)算符是該對象的成員函數(shù),而右操作數(shù)是該函數(shù)的參數(shù),其類型并無嚴(yán)格限制。C++不允許重載三目運(yùn)算符。

3.

單目運(yùn)算符“++”和“--”存在前置與后置問題。前置“++”格式為:

返回類型類名::operator++(){……}

而后置“++”格式為:

返回類型類名::operator++(int){……}

后置“++”中的參數(shù)int僅用作區(qū)分,并無實(shí)際意義,可以給一個(gè)變量名,也可以不給變量名。

4.C++中只有極少數(shù)的運(yùn)算符不允許重載,表5.1中列出了不允許重載的運(yùn)算符。第六十二頁,共一百一十頁,2022年,8月28日5.5 運(yùn)算符的重載運(yùn)算符運(yùn)算符名稱禁止重載的理由?:三目條件運(yùn)算符C++中沒有定義三目運(yùn)算符的語法.成員操作符為保證成員操作符對成員訪問的安全性::作用域操作符該操作符右操作數(shù)不是表達(dá)式sizeof類型字長操作符該操作符的操作數(shù)為類型名,不是表達(dá)式表5.1C++中不允許重載的運(yùn)算符第六十三頁,共一百一十頁,2022年,8月28日5.5 運(yùn)算符的重載

在本小節(jié)中學(xué)習(xí)了有關(guān)運(yùn)算符重載的基礎(chǔ)知識,必須指出的是這只是初步的,由于所學(xué)知識有限,重載中不少問題還不能解決。如例5.7中:

c=c+d;語句,改為

c=d+c;因?yàn)閐不是Complex的對象,C++編譯器將無法找到合適的重載的“+”運(yùn)算符對應(yīng)的函數(shù),最終給出出錯(cuò)信息。

第六十四頁,共一百一十頁,2022年,8月28日5.6 友元在C++中友元(friend)函數(shù)允許在類外訪問該類中的任何成員,就象成員函數(shù)一樣。友元函數(shù)用關(guān)鍵字friend說明。下面用友元函數(shù)重載運(yùn)算符“+”,以實(shí)現(xiàn)c=d+c。下面用友元函數(shù)重載運(yùn)算符“+”,實(shí)現(xiàn)實(shí)數(shù)與復(fù)數(shù)的加法。第六十五頁,共一百一十頁,2022年,8月28日5.6 友元classComplex{……

friendComplexoperator+(double,Complex);

};

//opration+說明為類Complex類的有元函數(shù),

//friend只用于類說明中

……Complexoperator+(doubled,Complexc){

//注意友元不是成員函數(shù),也不加friendreturnComplex(d+c.Real,c.Image);}

//友元函數(shù)可以直接訪問私有成員voidmain(void){

……c=d+c1;c.print();}這里d+c被C++編譯器解釋為operator+(d,c),重載了友元函數(shù)。由此可知友元函數(shù)在特定場合可能是必不可少的。第六十六頁,共一百一十頁,2022年,8月28日5.6 友元友元函數(shù)重載運(yùn)算符+,有三種形式。另兩個(gè)的聲明為:friendComplexoperator+(Complex,Complex);friendComplexoperator+(Complex,double);則無論是復(fù)數(shù)與復(fù)數(shù)相加,還是實(shí)數(shù)與復(fù)數(shù)相加(不論實(shí)數(shù)在前還是在后)都可以用該運(yùn)算符三個(gè)重載函數(shù)之一。再進(jìn)一步,如果使用友元函數(shù)friendcomplexoperator+(complexc1,complexc2);無論是復(fù)數(shù)與復(fù)數(shù)相加,還是實(shí)數(shù)與復(fù)數(shù)相加(不論實(shí)數(shù)在前還是在后)都可以用該運(yùn)算符重載函數(shù)。因?yàn)橛欣?.7所定義的缺省的構(gòu)造函數(shù),實(shí)數(shù)會(huì)被強(qiáng)制轉(zhuǎn)換為虛部為零的復(fù)數(shù)。d+c1被解釋為operator+(complex(d),c1)。注意這里的兩個(gè)參數(shù)是傳值,在函數(shù)內(nèi)是建立了兩個(gè)復(fù)數(shù)對象,而把實(shí)參的值傳進(jìn)去,進(jìn)行運(yùn)算。參見圖5.9。第六十七頁,共一百一十頁,2022年,8月28日5.6 友元在這里友元函數(shù)可以有兩個(gè)參數(shù),而對應(yīng)的成員函數(shù)只有一個(gè)參數(shù),所以友元函數(shù)的使用可以更靈活、更方便。使用引用類型變量作為運(yùn)算符重載函數(shù)的參數(shù),以提高復(fù)數(shù)類型運(yùn)算的效率和可行性。Operator+友元函數(shù)的聲明可改進(jìn)為:friendComplexoperator+(constComplex&c1,constComplex&c2)這里采用Complex對象的引用而不是對象本身,調(diào)用時(shí)不再重新分配內(nèi)存建立一個(gè)復(fù)制的對象,函數(shù)效率會(huì)更高。加const,實(shí)參只讀,可防止實(shí)參被修改。

圖5.9友元函數(shù)operator+執(zhí)行過程內(nèi)存分配第六十八頁,共一百一十頁,2022年,8月28日5.6 友元用VC++演示【例5.7_1】用友元函數(shù)重載運(yùn)算符,實(shí)現(xiàn)復(fù)數(shù)的運(yùn)算。與【例5.7】比較。單目運(yùn)算符前“++”的成員函數(shù)重載方式如下:ComplexComplex::operator++(){return(++Real,++Image);}采用成員函數(shù)方式重載與使用都很方便。但采用友元方式則必須使用引用,因?yàn)楸皇┘印?+”運(yùn)算的是一個(gè)參數(shù)。友元函數(shù)重載后置“++”如下:friendComplexoperator++(Complex&c,int)//注意友元方式與前者的區(qū)別{return(c.Real++,c.Image++);}采用引用類型,后“++”是直接施加于實(shí)參。否則施加于拷貝,而實(shí)參不變。第六十九頁,共一百一十頁,2022年,8月28日友元函數(shù)注意點(diǎn):

1.

友元函數(shù)不是類的成員函數(shù),在函數(shù)體中訪問對象的成員,必須用對象名加運(yùn)算符“.”加對象成員名。這一點(diǎn)和一般函數(shù)一樣。但友元函數(shù)可以訪問類中的所有成員(公有的、私有的、保護(hù)的),一般函數(shù)只能訪問類中的共有成員。

2.

友元函數(shù)不受類中的訪問權(quán)限關(guān)鍵字限制,可以把它放在類的公有、私有、保護(hù)部分,但結(jié)果一樣。

3.

某類的友元函數(shù)的作用域并非該類作用域。如果該友元函數(shù)是另一類的成員函數(shù),則其作用域?yàn)榱硪活惖淖饔糜?,否則與一般函數(shù)相同。友元函數(shù)破壞了面向?qū)ο蟪绦蛟O(shè)計(jì)類的封裝性,所以友元函數(shù)如不是必須使用,則盡可能少用?;蛘哂闷渌侄伪WC封裝性。友元還有友元類概念:整個(gè)類可以是另一個(gè)類的友元。友元類的每個(gè)成員函數(shù)都是另一個(gè)類的友元函數(shù),都可訪問另一個(gè)類中的保護(hù)或私有數(shù)據(jù)成員。定義方法如下:classA{……friendclassB;//聲明B為A的友元類

……};第七十頁,共一百一十頁,20

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(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)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論