Visual C++基礎(chǔ)教程課件:類和對象_第1頁
Visual C++基礎(chǔ)教程課件:類和對象_第2頁
Visual C++基礎(chǔ)教程課件:類和對象_第3頁
Visual C++基礎(chǔ)教程課件:類和對象_第4頁
Visual C++基礎(chǔ)教程課件:類和對象_第5頁
已閱讀5頁,還剩144頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

類和對象3.1面向?qū)ο蟪绦蛟O(shè)計概述

3.2類與對象

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

3.4對象數(shù)組和對象指針

3.5靜態(tài)成員

3.6友元3.7對象成員

習(xí)題

3.1面向?qū)ο蟪绦蛟O(shè)計概述3.1.1面向?qū)ο蟪绦蛟O(shè)計的產(chǎn)生在面向?qū)ο蟪绦蛟O(shè)計出現(xiàn)之前,程序設(shè)計人員廣泛使用的是面向過程的程序設(shè)計方法。20世紀(jì)60年代產(chǎn)生的結(jié)構(gòu)化程序設(shè)計思想,為使用面向過程的方法解決復(fù)雜問題提供了有力手段,并且成為20世紀(jì)70年代至80年代最主要、最通用的程序設(shè)計方法。結(jié)構(gòu)化程序設(shè)計方法采用自頂向下、逐步求精的方式對復(fù)雜問題進行逐步分解,將一個復(fù)雜任務(wù)分解成若干個功能模塊,然后根據(jù)功能模塊設(shè)計用于保存數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),編寫過程或函數(shù)對這些數(shù)據(jù)進行操作。完成各功能模塊的過程在功能上相對獨立,而在數(shù)據(jù)的處理上又相互聯(lián)系。結(jié)構(gòu)化程序設(shè)計是一種面向過程的程序設(shè)計方法,它的核心是過程,程序通常是按照過程來組織的,即利用代碼對數(shù)據(jù)進行處理。在面向過程的程序設(shè)計中,數(shù)據(jù)與操作數(shù)據(jù)的過程是分離的。當(dāng)數(shù)據(jù)結(jié)構(gòu)改變時,所有相關(guān)的處理過程都要進行相應(yīng)的修改,因此代碼的可重用性差。另外,面向過程程序設(shè)計將系統(tǒng)分解為若干功能模塊,系統(tǒng)是實現(xiàn)模塊功能的過程的集合。由于用戶的需求和軟、硬件技術(shù)的不斷發(fā)展變化,按照功能劃分設(shè)計的系統(tǒng)模塊必然是易變的和不穩(wěn)定的。特別是隨著系統(tǒng)規(guī)模的擴大,程序的復(fù)雜性也進一步增加,修改程序的困難也增大,系統(tǒng)的可維護性較差。3.1.2面向?qū)ο蟪绦蛟O(shè)計的基本概念和特征面向?qū)ο蟪绦蛟O(shè)計思想更加符合人們認(rèn)識現(xiàn)實世界的方式。現(xiàn)實世界是由各種各樣的事物組成的,包括有形的事物和無形的事物。例如植物、人、汽車、計算機等是有形的事物,思想、一項計劃等是無形的事物。人們認(rèn)識現(xiàn)實世界是從認(rèn)識現(xiàn)實世界中的一些具體的事物開始的?,F(xiàn)實世界中的每個事物都有各自的屬性和行為,屬性表示事物的靜態(tài)特征,行為表示事物的動態(tài)特征,屬性和行為是一個不可分割的整體。人們通過研究事物的屬性和行為而認(rèn)識事物。簡單的事物之間相互作用、聯(lián)系和組合可以構(gòu)成復(fù)雜的事物。在面向?qū)ο蟪绦蛟O(shè)計中,用對象模擬現(xiàn)實世界中的事物,以對象為基本單位,分析、設(shè)計和實現(xiàn)一個系統(tǒng)。與面向過程程序設(shè)計以過程組織程序不同,面向?qū)ο蟪绦蛟O(shè)計以數(shù)據(jù)為中心來描述系統(tǒng),按照數(shù)據(jù)來組織程序,其基本思想是數(shù)據(jù)決定對代碼的訪問。面向?qū)ο蟪绦蛟O(shè)計將表示屬性特征的數(shù)據(jù)和對數(shù)據(jù)進行操作的方法封裝在一起,構(gòu)成一個完整的對象。對同類型對象抽象出共性,形成類。類中的大多數(shù)屬性數(shù)據(jù),只能用本類的方法進行處理。對象通過一些外部接口與其它對象發(fā)生聯(lián)系,對象與對象之間通過消息進行通信。類是具有相同屬性特征和行為特征的一組對象的集合,它規(guī)定了這些對象的公共屬性和行為方法。類和對象之間的關(guān)系是抽象和具體的關(guān)系,類是對多個對象進行綜合抽象的結(jié)果,對象是類的實例。例如,汽車是一個類,行駛在公路上的一輛汽車則是一個對象。

面向?qū)ο蟪绦蛟O(shè)計中,程序是由一系列相互作用的對象構(gòu)成的,對象之間通過發(fā)送消息實現(xiàn)通信。消息是一個對象向另一個對象發(fā)出的服務(wù)請求,如果用戶或其它對象向該對象提出服務(wù)請求,便可以稱為向該對象發(fā)送消息。消息傳遞的語法結(jié)構(gòu)隨系統(tǒng)的不同而不同,一般由如下幾部分組成:接收消息的對象,又被稱為目標(biāo)對象。請求對象的方法。一個或多個參數(shù)。面向?qū)ο蟪绦蛟O(shè)計方法具有三個最基本的特征:封裝性、繼承性和多態(tài)性。

(1)封裝性。封裝性是一種程序設(shè)計機制,將對象的屬性(數(shù)據(jù))和行為(對數(shù)據(jù)操作的代碼)組合成一個有機的整體,同時決定哪些屬性和行為用于表示對象的內(nèi)部狀態(tài),對外界是隱藏的;哪些屬性和行為對外界是可見的,是提供給外界的接口。外界只能通過對象提供的外部接口操作對象。

(2)繼承性。繼承是一種連接類與類的層次模型,是指一個新類可以從現(xiàn)有的類派生而來。通過繼承,新類(子類或派生類)擁有了現(xiàn)有類(父類或基類)的特征,包括屬性和行為,同時可以修改或增加新的屬性和行為,使新類更適合具體的需要。這樣,在設(shè)計新類時,只需對新類增加的內(nèi)容或是對現(xiàn)有類內(nèi)容的修改設(shè)計代碼??梢姡^承性簡化了類的設(shè)計,提高了軟件的重用性,大大提高了軟件的開發(fā)效率和系統(tǒng)的可靠性。

(3)多態(tài)性。多態(tài)性是指允許不同類的對象對同一消息做出不同的響應(yīng)。例如,同樣的加法,將兩個時間加在一起和將兩個整數(shù)加在一起的內(nèi)涵完全不同。利用多態(tài)性,可以在基類和派生類中使用同樣的函數(shù)名,定義不同的操作,從而實現(xiàn)“一個接口,多種方法”。至于在某種條件下應(yīng)該選擇哪種方法,則留給編譯器來完成,程序員無需人工完成這種選擇,只需記住并使用這個通用接口就行了。多態(tài)性增強了軟件的靈活性和重用性。3.2類與對象3.2.1類的定義

C++中,類的定義一般包括說明部分和實現(xiàn)部分。說明部分用來說明類的成員,包括數(shù)據(jù)成員的名字和數(shù)據(jù)類型、成員函數(shù)的原型或?qū)崿F(xiàn)。實現(xiàn)部分包括各成員函數(shù)的定義?!纠?.1】類定義示例。定義一個用來描述平面上坐標(biāo)點的類Point。

classPoint

{

private: intx,y;

public: voidSetPoint(inta,intb) { x=a; y=b; } intGetX() { returnx; } intGetY() { returny; } voidShowPoint() { cout<<"("<<x<<","<<y<<")"<<endl; }

};

對于平面上一個坐標(biāo)點,其基本屬性是坐標(biāo)位置,因此在類中說明了兩個數(shù)據(jù)成員x和y用來保存坐標(biāo)值。對坐標(biāo)點的操作,一般有設(shè)置坐標(biāo)、顯示坐標(biāo),另外,需要返回坐標(biāo)的值,因此,在類Point中包含了對坐標(biāo)操作的成員函數(shù)SetPoint、GetX、GetY和ShowPoint。將數(shù)據(jù)和對數(shù)據(jù)的操作封裝在類Point中,就形成了一個完整的整體。類定義的一般形式如下:

class類名

{ private:

私有數(shù)據(jù)成員和成員函數(shù)的說明或?qū)崿F(xiàn)

public:

公有數(shù)據(jù)成員和成員函數(shù)的說明或?qū)崿F(xiàn)

protected:

保護數(shù)據(jù)成員和成員函數(shù)的說明或?qū)崿F(xiàn)

};

//實現(xiàn)部分 各成員函數(shù)的實現(xiàn)

類的定義由關(guān)鍵字class開始,其后是用戶定義的類名;花括號內(nèi)的部分稱為類體,用來聲明類的成員;最后的分號表示類聲明的結(jié)束。關(guān)鍵字private、public和protected稱為訪問權(quán)限控制符,用來設(shè)置數(shù)據(jù)成員和成員函數(shù)的訪問屬性。在定義成員時,如果省略了訪問權(quán)限控制符,則其訪問屬性默認(rèn)為private。類中的每一個成員都有確定的訪問屬性。關(guān)鍵字private下說明的數(shù)據(jù)成員和成員函數(shù)是類的私有成員,它們只能被本類的成員函數(shù)或友元函數(shù)訪問。將成員說明為私有的,可以起到隱藏成員的作用,提高安全性。關(guān)鍵字public下說明的數(shù)據(jù)成員和成員函數(shù)是類的公有成員,它們可以被本類的成員函數(shù)訪問,也可以被其它類的成員函數(shù)或程序中的其它函數(shù)(通過對象)訪問,它們是類的外部接口。關(guān)鍵字protected下說明的數(shù)據(jù)成員和成員函數(shù)是類的保護成員,它們可以由本類的成員函數(shù)訪問,也可以由其派生的派生類的成員函數(shù)訪問,但不允許程序中的其它函數(shù)訪問。保護成員的具體訪問規(guī)則在第4章中詳細(xì)介紹?!纠?.2】類的定義示例。

classEmployee

{ charName[20]; charSex; intAge;

public: voidRegisterInfo(char*name,charsex,intage); voidShowInfo();

};在本類中省略了關(guān)鍵字private,則三個數(shù)據(jù)成員缺省為私有成員。類中對成員函數(shù)只給出了函數(shù)原型,沒有函數(shù)的實現(xiàn)。說明:

(1)類定義中的訪問權(quán)限控制符private、public和protected可以按任意順序出現(xiàn)任意次。建議將相同訪問權(quán)限的成員歸類放在一起,以使程序結(jié)構(gòu)更清晰。至于私有成員和公有成員在類中出現(xiàn)的先后順序,不同的程序員有不同的主張。主張將私有成員放在前面的程序員認(rèn)為,由于成員的缺省訪問權(quán)限是private,一旦程序員忘記使用控制符private,類的數(shù)據(jù)仍然能得到保護。主張將公有成員放在前面的程序員認(rèn)為,公有成員是類提供的外部接口,是用戶所關(guān)心的,便于閱讀。

(2)類中說明的數(shù)據(jù)成員可以是任何數(shù)據(jù)類型,包括自身類的指針或引用,但不能是自身類的對象。

(3)在類中說明的任何成員不能使用extern、auto和register關(guān)鍵字進行修飾。

(4)不允許在類定義中對數(shù)據(jù)成員進行初始化。例如,下述類定義是錯誤的:

classPoint

{

private: intx,y=5;

//錯誤,類定義時對數(shù)據(jù)成員進行了初始化

//……

};因為類描述的是這個類所有對象共同的屬性,只有具體的對象才能確定屬性的值。另外,類定義是一種數(shù)據(jù)類型的定義,類作為數(shù)據(jù)類型,它本身不占用存儲空間,只有在定義了類類型的變量后,系統(tǒng)才為其分配存儲空間。3.2.2成員函數(shù)的定義類的定義一般包括說明部分和實現(xiàn)部分。說明部分用來說明類的成員,包括數(shù)據(jù)成員的名字和數(shù)據(jù)類型、成員函數(shù)的原型或?qū)崿F(xiàn)。對于成員函數(shù),可以只在說明部分給出函數(shù)原型,其定義可以在類的實現(xiàn)部分實現(xiàn),也可以在類的說明部分給出。在類的實現(xiàn)部分,成員函數(shù)的定義與普通函數(shù)的定義形式基本相同,但必須在成員函數(shù)名前加上類名和作用域運算符“::”,說明成員函數(shù)所屬的類。在實現(xiàn)部分,成員函數(shù)定義的形式如下:數(shù)據(jù)類型類名::函數(shù)名(參數(shù)表)

{

函數(shù)體

}【例3.3】例3.2中類Employee的完整定義。

#include<iostream.h>

#include<string.h>

//說明部分

classEmployee

{ charName[20]; charSex; intAge;

public: voidRegisterInfo(char*name,charsex,intage); voidShowInfo();

};

//實現(xiàn)部分

voidEmployee::RegisterInfo(char*name,charsex,intage)

{ strcpy(Name,name); Sex=sex; Age=age;

}

voidEmployee::ShowInfo()

{ cout<<Name<<'\t'<<Sex<<'\t'<<Age<<endl;

}從這個例子中可以看出,雖然函數(shù)RegisterInfo()和ShowInfo()是在類體外的實現(xiàn)部分定義的,但它們與數(shù)據(jù)成員屬于同一個類Employee,它們可以直接使用類中的數(shù)據(jù)成員Name、Sex和Age。3.2.3對象的定義與使用

1.對象的定義定義一個對象,與定義一個一般變量的格式相同。定義對象的一般格式如下: 類名對象名表;其中,“類名”是定義的對象所屬類的類名,“對象名表”中可以有一個或多個對象名,多個對象名之間用逗號分隔。對象名表中可以是一般的對象名,也可以是指向?qū)ο蟮闹羔樏驅(qū)ο蟮囊?,還可以是對象數(shù)組名。例如,定義類Employee的對象的格式如下所示:

Employeeempl,*pempl,AllEmpl[32];即定義了類Employee的一般對象empl、指向類Employee的對象的指針*pempl和對象數(shù)組AllEmpl。數(shù)組AllEmpl中的每個元素都是對象。

2.對象的使用對象的使用實際上是對象中成員的使用。對象成員是指該對象所屬類中定義的成員,包括數(shù)據(jù)成員和成員函數(shù),其訪問形式與結(jié)構(gòu)變量成員的訪問形式相同。通過一般對象訪問對象成員的格式如下: 對象名.數(shù)據(jù)成員名 對象名.成員函數(shù)名(參數(shù)表)其中,運算符“.”稱為成員選擇運算符。通過指向?qū)ο蟮闹羔樤L問對象成員的格式如下: 對象指針名->數(shù)據(jù)成員名 對象指針名->成員函數(shù)名(參數(shù)表)或

(*對象指針名).數(shù)據(jù)成員名

(*對象指針名).成員函數(shù)名(參數(shù)表)其中,運算符“->”也稱為成員選擇運算符。例如:

Employeeempl,*pempl=&empl; empl.RegisterInfo("Wang",'m',28); pempl->ShowInfo();另外,通過對象的引用訪問成員的方法與通過一般對象訪問對象成員的方法相同。例如:

Employeeempl; Employee&rempl=empl; rempl.RegisterInfo("Zhang",'f',32);【例3.4】本例定義一個日期類,并定義類的對象,幫助理解對象的定義和成員的訪問方法。

#include<iostream.h>

//類定義的說明部分

classDate

{

private: intyear,month,day;

public: voidSetDate(inty,intm,intd) { year=y; month=m; day=d; } intIsLeapYear();

//判斷給定的日期中年份是否是閏年

voidShowDate();

}; //類體結(jié)束,別忘記分號

//類定義的實現(xiàn)部分

intDate::IsLeapYear()

{ return(year%4==0&&year%100!=0)||(year%400==0);

}

voidDate::ShowDate()

{ cout<<year<<'-'<<month<<'-'<<day<<endl;

}

voidmain()

{ Datedate1,date2,*pdate; //定義類Date的對象和指向?qū)ο蟮闹羔?/p>

date1.SetDate(2006,1,20); date2.SetDate(2004,5,8); date1.ShowDate(); cout<<date1.IsLeapYear()<<endl; pdate=&date2; pdate->ShowDate(); cout<<pdate->IsLeapYear()<<endl;

}程序運行結(jié)果如下:

2006-1-20

0

2004-5-8

1程序運行結(jié)果中,0和1分別表示2006年不是閏年,2004年是閏年。程序中定義了類Date的兩個對象date1、date2和一個指向?qū)ο蟮闹羔?,每個對象代表一個日期。通過對象訪問成員函數(shù)SetDate()給對象date1和date2的數(shù)據(jù)成員賦值,設(shè)定日期。通過對象date1訪問成員函數(shù)ShowDate()輸出date1的日期,通過指向?qū)ο蟮闹羔榩date訪問成員函數(shù)ShowDate()輸出對象date2的日期。

類定義中通過使用訪問權(quán)限控制符對不同成員設(shè)置了不同的訪問權(quán)限,類定義中設(shè)置的訪問權(quán)限只影響該類的對象對成員的訪問,不影響類內(nèi)部對成員的訪問。無論成員是何種訪問權(quán)限,在類的內(nèi)部都可以自由訪問和使用。但在類的外部(程序中)通過對象只能訪問對象的公有成員(包括公有成員函數(shù)和數(shù)據(jù)成員),不能訪問對象的私有成員。例如,在例3.4中定義的類Date中,數(shù)據(jù)成員year、month和day被說明為私有成員,它們可以被類中的成員函數(shù)SetDate()、ShowDate()和IsLeapYear()自由訪問,但不能被類的對象訪問。如果程序中有如下語句:

date1.year=2006;則編譯時會產(chǎn)生錯誤,因為通過對象不能訪問其私有成員。例3.4的程序中,對象訪問了公有成員函數(shù)SetDate()、ShowDate()和IsLeapYear()。3.2.4內(nèi)聯(lián)成員函數(shù)

1.隱式定義隱式定義是指在類的說明部分直接定義成員函數(shù)的函數(shù)體,這時,成員函數(shù)自動成為內(nèi)聯(lián)成員函數(shù)。例如,例3.1中定義Point類時,將成員函數(shù)SetPoint()、GetX()、GetY()和ShowPoint()直接定義在類的說明部分,則它們自動成為內(nèi)聯(lián)成員函數(shù)。

2.顯式定義為了保持程序的可讀性,在定義內(nèi)聯(lián)成員函數(shù)時,只在類的說明部分給出函數(shù)原型,而將函數(shù)定義放在類的實現(xiàn)部分。在顯示函數(shù)定義時,只需在返回類型前加上關(guān)鍵字inline即可。例如,例3.5就是將例3.1中的成員函數(shù)顯式定義成內(nèi)聯(lián)成員函數(shù)的格式。【例3.5】內(nèi)聯(lián)成員函數(shù)的顯式定義。

classPoint

{

private: intx,y;

public: voidSetPoint(int,int); intGetX(); intGetY(); voidShowPoint();

};

inlinevoidPoint::SetPoint(inta,intb)

{ x=a; y=b;

}

inlineintPoint::GetX()

{ returnx;

}

inlineintPoint::GetY()

{ returny;

}

inlinevoidPoint::ShowPoint()

{ cout<<"("<<x<<","<<y<<")"<<endl;

}3.2.5成員函數(shù)的重載和參數(shù)的默認(rèn)值類中的成員函數(shù)如果帶有參數(shù),則可以像普通函數(shù)一樣進行重載,也可以對參數(shù)設(shè)置默認(rèn)值。成員函數(shù)重載和設(shè)置默認(rèn)值的方法與普通函數(shù)相同?!纠?.6】分析下面程序的運行結(jié)果,了解成員函數(shù)重載和設(shè)置參數(shù)默認(rèn)值的用法。

#include<iostream.h>

classPoint

{

private: intx,y;

public: voidSetPoint(inta,intb) { x=a; y=b; } voidSetPoint(inta=0); intGetX() { returnx; } intGetY() { returny; } voidShowPoint() { cout<<"("<<x<<","<<y<<")"<<endl; }

};

voidPoint::SetPoint(inta)

{ x=y=a;

}

voidmain()

{ Pointp1,p2,p3; p1.SetPoint(); p2.SetPoint(5,8); p3.SetPoint(20); cout<<"p1:("<<p1.GetX()<<','<<p1.GetY()<<')'<<endl; cout<<"p2:"; p2.ShowPoint(); cout<<"p3:"; p3.ShowPoint();

}程序運行結(jié)果如下:

p1:(0,0)

p2:(5,8)

p3:(20,20)

類中對成員函數(shù)SetPoint()進行了重載,同時對成員函數(shù)SetPoint(int)的參數(shù)設(shè)置了默認(rèn)值。程序中的語句

p1.SetPoint();調(diào)用成員函數(shù)SetPoint(int),并使用參數(shù)默認(rèn)值0。語句

p2.SetPoint(5,8);調(diào)用成員函數(shù)SetPoint(int,int)。如果將成員函數(shù)的定義放在類的實現(xiàn)部分,則在設(shè)置參數(shù)默認(rèn)值時,可以在類定義說明部分的函數(shù)原型中指定默認(rèn)值,也可以在實現(xiàn)部分的函數(shù)定義中指定默認(rèn)值,但不能在兩處同時指定默認(rèn)值,即使默認(rèn)值相同也不行。一般習(xí)慣在函數(shù)原型中指定默認(rèn)值。3.3構(gòu)造函數(shù)和析構(gòu)函數(shù)3.3.1構(gòu)造函數(shù)構(gòu)造函數(shù)是類中特殊的成員函數(shù),其功能是在創(chuàng)建對象時使用給定的值來初始化對象。它有如下特點:

(1)構(gòu)造函數(shù)是成員函數(shù),可以定義在類的說明部分,也可以定義在類的實現(xiàn)部分。

(2)構(gòu)造函數(shù)的名字與類名相同,有任意類型的參數(shù),但不能指定返回類型,即使void類型也不行。

(3)構(gòu)造函數(shù)可以重載,即可以定義多個參數(shù)不同的構(gòu)造函數(shù),也可以沒有參數(shù)。

(4)構(gòu)造函數(shù)在定義對象時由系統(tǒng)自動調(diào)用。

(5)構(gòu)造函數(shù)應(yīng)說明為公有成員函數(shù),但一般情況下不能像其它公有成員函數(shù)那樣被顯式調(diào)用。【例3.7】構(gòu)造函數(shù)應(yīng)用示例。

#include<iostream.h>

classPoint

{

private: intx,y;

public: Point(int,int); Point() { cout<<"Defaultconstructorcalled"<<endl; x=y=0; } voidSetPoint(inta,intb) { x=a; y=b; } intGetX() { returnx; } intGetY() { returny; } voidShowPoint() { cout<<"("<<x<<","<<y<<")"<<endl; }

};

Point::Point(intm,intn)

{ x=m; y=n; cout<<"Constructorcalled"<<endl;

}

voidmain()

{ Pointp1; Pointp2(5,8); cout<<"p1:"; p1.ShowPoint(); cout<<"p2:"; p2.ShowPoint();

}程序運行結(jié)果如下:

Defaultconstructorcalled Constructorcalled p1:(0,0) p2:(5,8)

Point類中定義了兩個重載的構(gòu)造函數(shù),其中一個構(gòu)造函數(shù)沒有參數(shù),在類的說明部分定義,另一個帶有兩個參數(shù),在類的說明部分進行函數(shù)原型說明,定義在類的實現(xiàn)部分。在創(chuàng)建對象p1時,系統(tǒng)自動調(diào)用了不帶參數(shù)的構(gòu)造函數(shù);在創(chuàng)建對象p2時,系統(tǒng)自動調(diào)用了帶參數(shù)的構(gòu)造函數(shù),對象名后括號內(nèi)的數(shù)據(jù)是調(diào)用構(gòu)造函數(shù)的實參。利用構(gòu)造函數(shù)創(chuàng)建對象的一般形式為: 類名對象名(實參表);在構(gòu)造函數(shù)中對數(shù)據(jù)成員進行初始化時,可以有不同的形式。一是在構(gòu)造函數(shù)的函數(shù)體內(nèi)用賦值語句進行初始化,如例3.7中的構(gòu)造函數(shù);二是在構(gòu)造函數(shù)中使用初始化列表對數(shù)據(jù)成員進行初始化,例如,可以將例3.7中的構(gòu)造函數(shù)定義成如下形式:

Point::Point(intm,intn):x(m),y(n) { cout<<"Constructorcalled"<<endl; }其中,構(gòu)造函數(shù)頭部冒號后即為初始化列表。但對數(shù)組成員的初始化必須放在函數(shù)體內(nèi)。

在程序中可以使用new運算符來創(chuàng)建動態(tài)對象,此時,系統(tǒng)也會自動調(diào)用構(gòu)造函數(shù)。其一般語法形式為: 類名*指針名=new類名(實參表);當(dāng)類中有不帶參數(shù)的構(gòu)造函數(shù)時,類名后的括號和實參表可以省略,否則必須給出實參表。當(dāng)用new建立一個動態(tài)對象時,new首先為該對象分配存儲空間,然后自動調(diào)用構(gòu)造函數(shù)來初始化該對象,再返回這個對象所占存儲空間的地址。例如:

Point*p1=newPoint;將自動調(diào)用類Point中不帶參數(shù)的構(gòu)造函數(shù),對對象進行初始化。又如:

Point*p2=newPoint(5,8);將自動調(diào)用類Point中帶參數(shù)的構(gòu)造函數(shù),對對象進行初始化。使用new建立的動態(tài)對象在不用時必須用delete刪除,否則會造成內(nèi)存泄漏。全局對象的構(gòu)造函數(shù)在主函數(shù)main執(zhí)行之前被調(diào)用。例如:【例3.8】全局對象構(gòu)造函數(shù)的調(diào)用。

#include<iostream.h>

//這里插入例3.7中類Point的定義代碼

voidmain()

{ cout<<"Enteringmain"<<endl; Pointsta; cout<<"Exitingmain"<<endl;

}

Pointglobal(3,8);//定義全局對象程序運行結(jié)果如下:

Constructorcalled Enteringmain Defaultconstructorcalled Exitingmain【例3.9】構(gòu)造函數(shù)的定義及其默認(rèn)值示例。

#include<iostream.h>

classComplex

{

private: doublereal,imag;

public: Complex(doubler=0.0,doublei=0.0); doubleGetReal() { returnreal; } doubleGetImag() { returnimag; } voidDisplay() { cout<<real; if(imag>=0) cout<<'+'<<imag<<'i'<<endl; else cout<<imag<<'i'<<endl; }

};

Complex::Complex(doubler,doublei)

{ real=r; imag=i; cout<<"Constructorcalled!"<<endl;

}

voidmain()

{ Complexc1; Complexc2(3.2); Complexc3(5.5,-8.8); c1.Display(); c2.Display(); c3.Display();

}程序運行結(jié)果如下:

Constructorcalled! Constructorcalled! Constructorcalled! 0+0i 3.2+0i 5.5-8.8i程序中定義了三個對象。定義對象c1時,沒有傳遞參數(shù),因此構(gòu)造函數(shù)的參數(shù)都取默認(rèn)值。定義對象c2時,給出了一個實參,這個實參傳遞給形參r,形參i取默認(rèn)值。定義對象c3時,傳遞了兩個實參。由于構(gòu)造函數(shù)可以重載,因此在給構(gòu)造函數(shù)指定默認(rèn)值時,應(yīng)避免定義對象時產(chǎn)生二義性。例如:

classA { private: intx; public: A(); A(inti=0); }; voidmain() { Aob1(25); //正確,調(diào)用A(int) Aob2; //錯誤,產(chǎn)生二義性,編譯系統(tǒng)無法確定應(yīng)調(diào)用哪一個構(gòu)造函數(shù)

//…… }3.3.2缺省構(gòu)造函數(shù)不帶參數(shù)的構(gòu)造函數(shù)稱為缺省構(gòu)造函數(shù)。因為在沒有為類定義任何構(gòu)造函數(shù)的情況下,C++編譯器總要自動建立一個不帶參數(shù)的構(gòu)造函數(shù)。例如,例3.1中Point類沒有定義任何構(gòu)造函數(shù),則C++編譯器要為它自動產(chǎn)生一個如下形式的構(gòu)造函數(shù):

Point::Point() {}即它的函數(shù)體是空的。當(dāng)使用類Point建立對象時,對象的狀態(tài)是不確定的,即對象中的數(shù)據(jù)成員x和y的值不確定?!纠?.10】分?jǐn)?shù)類Fraction。

classFraction

{

private: intnumerator,denominator;

public: doubleGetValue() { returnnumerator/denominator; }

};當(dāng)使用類Fraction創(chuàng)建全局對象或靜態(tài)對象時,數(shù)據(jù)成員numerator和denominator的值被初始化為0,當(dāng)通過對象調(diào)用成員函數(shù)GetValue()時,就會產(chǎn)生分母為0的錯誤。因此,必須在類Fraction中定義一個不帶參數(shù)的構(gòu)造函數(shù),如下所示:

Fraction::Fraction() { numerator=0; denominator=1; }這種不帶參數(shù)的構(gòu)造函數(shù)稱為缺省構(gòu)造函數(shù)。說明:

(1)若類中沒有定義過任何形式的構(gòu)造函數(shù),則系統(tǒng)會自動生成缺省構(gòu)造函數(shù)。

(2)若類中已定義過帶參數(shù)的構(gòu)造函數(shù),則系統(tǒng)不會再自動生成缺省構(gòu)造函數(shù)。如果需要,則要求顯式地定義缺省構(gòu)造函數(shù)。

(3)如果定義對象時沒有給定實參,則調(diào)用缺省構(gòu)造函數(shù)。

(4)有些情況下,類中必須定義缺省構(gòu)造函數(shù),如定義對象數(shù)組時。

3.3.3拷貝構(gòu)造函數(shù)拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),它具有一般構(gòu)造函數(shù)的全部特點。其作用是使用一個已經(jīng)存在的對象去初始化一個新建的同類對象??截悩?gòu)造函數(shù)除了具有一般構(gòu)造函數(shù)的特點外,它還具有以下特點:

(1)拷貝構(gòu)造函數(shù)只有一個參數(shù),并且是該類對象的引用。

(2)若類中沒有定義拷貝構(gòu)造函數(shù),則系統(tǒng)自動生成一個缺省拷貝構(gòu)造函數(shù),其作用是將已知對象的數(shù)據(jù)成員逐一賦值給新建對象的數(shù)據(jù)成員??截悩?gòu)造函數(shù)的一般形式為: 類名::類名(const類名&引用名) {函數(shù)體}【例3.11】拷貝構(gòu)造函數(shù)使用示例。

#include<iostream.h>

classPoint

{

private: intx,y;

public: Point(int,int); Point(Point&); //拷貝構(gòu)造函數(shù)原型

voidShowPoint() { cout<<"("<<x<<","<<y<<")"<<endl; }

};

Point::Point(intm,intn)

{ x=m; y=n; cout<<"Constructorcalled"<<endl;

}

Point::Point(Point&p) //拷貝構(gòu)造函數(shù)的定義

{ x=p.x; y=p.y; cout<<"Copyconstructorcalled"<<endl;

}

voidmain()

{ Pointp1(10,20);

//調(diào)用構(gòu)造函數(shù)

Pointp2(p1);

//顯式調(diào)用拷貝構(gòu)造函數(shù)初始化p2 Pointp3=p1;

//調(diào)用拷貝構(gòu)造函數(shù)初始化p3 cout<<"p1:"; p1.ShowPoint(); cout<<"p2:"; p2.ShowPoint(); cout<<"p3:"; p3.ShowPoint(); Pointp4(-20,30); p4=p1;

//對象之間的賦值,不調(diào)用拷貝構(gòu)造函數(shù)

cout<<"p4:"; p4.ShowPoint();

}程序運行結(jié)果如下:

Constructorcalled

Copyconstructorcalled

Copyconstructorcalled

p1:(10,20)

p2:(10,20)

p3:(10,20)

Constructorcalled

p4:(10,20)

Point類中定義了一個拷貝構(gòu)造函數(shù)。定義對象p2時,顯式調(diào)用拷貝構(gòu)造函數(shù)用p1初始化p2。定義對象p3時,采用賦值的方式用對象p1初始化p3,此時,系統(tǒng)也會自動調(diào)用拷貝構(gòu)造函數(shù)。但當(dāng)一個對象已定義完,用另一個同類的對象給其賦值時,則不會調(diào)用拷貝構(gòu)造函數(shù),例如,執(zhí)行語句

p4=p1;時,是實現(xiàn)對象之間的賦值,不是初始化,因此,不調(diào)用拷貝構(gòu)造函數(shù)??截悩?gòu)造函數(shù)在下列情況下也會被自動調(diào)用:

(1)當(dāng)對象作為函數(shù)參數(shù),函數(shù)調(diào)用實參值傳遞給形參時。

(2)當(dāng)對象作為函數(shù)的返回值,函數(shù)調(diào)用返回時。3.3.4析構(gòu)函數(shù)析構(gòu)函數(shù)也是類的一個特殊的成員函數(shù),當(dāng)一個對象的生存期結(jié)束時,系統(tǒng)將自動調(diào)用析構(gòu)函數(shù)。它主要用來在對象撤銷時做一些清理工作,例如,用delete運算符釋放類中用new分配給對象的存儲空間。一般來講,如果希望程序在對象撤銷之前自動完成某些操作,就可以將其放在析構(gòu)函數(shù)中。

析構(gòu)函數(shù)有如下特點:

(1)析構(gòu)函數(shù)名與類名相同,但在前面必須加一個波浪號“~”。

(2)析構(gòu)函數(shù)不帶任何參數(shù),因此,析構(gòu)函數(shù)不能重載。

(3)析構(gòu)函數(shù)沒有返回類型,即使指定void類型也不行。

(4)析構(gòu)函數(shù)必須說明為公有成員函數(shù)。

(5)析構(gòu)函數(shù)是成員函數(shù),可以定義在類的說明部分,也可以定義在類的實現(xiàn)部分。例3.12是為類Point添加析構(gòu)函數(shù)的示例?!纠?.12】析構(gòu)函數(shù)的定義和使用示例。

#include<iostream.h>

classPoint

{

private: intx,y;

public: Point(int,int); ~Point(); //析構(gòu)函數(shù)原型

voidShowPoint() { cout<<"("<<x<<","<<y<<")"<<endl; }

};

Point::Point(intm,intn)

{ x=m; y=n; cout<<"Constructorcalled"<<endl;

}

Point::~Point() //析構(gòu)函數(shù)的定義

{ cout<<"destructorcalled"<<endl;

}

voidmain()

{ Pointp(10,20); cout<<"p:"; p.ShowPoint(); cout<<"Exitingmain"<<endl;

}程序運行結(jié)果如下:

Constructorcalled

p:(10,20)

Exitingmain

destructorcalled在Point類中,析構(gòu)函數(shù)輸出一個字符串。從程序運行結(jié)果看,當(dāng)程序結(jié)束時,對象p的生存期結(jié)束,撤銷對象p時,系統(tǒng)自動調(diào)用了析構(gòu)函數(shù)。同缺省構(gòu)造函數(shù)一樣,若類中未定義析構(gòu)函數(shù),則編譯器也會自動為類生成如下形式的缺省析構(gòu)函數(shù): 類名::~類名() {}【例3.13】定義類String,用于保存一個字符串。

#include<iostream.h>

#include<string.h>

classString

{

private: char*str; intlen;

public: String() { str=NULL; len=0; } String(char*); ~String(); char*GetString(); intGetLength();

};

String::String(char*s)

{ str=newchar[strlen(s)+1]; strcpy(str,s); len=strlen(str); cout<<"Constructorcalled"<<endl;

}

String::~String()

{ deletestr; cout<<"Destructorcalled"<<endl;

}

char*String::GetString()

{ returnstr;

}

intString::GetLength()

{ returnlen;

}

voidmain()

{ Stringname("Zhang"); cout<<"Thestringis:"<<name.GetString()<<endl; cout<<"Length:"<<name.GetLength()<<endl;

}程序運行結(jié)果如下:

Constructorcalled

Thestringis:Zhang

Length:5

Destructorcalled在定義對象時,系統(tǒng)自動調(diào)用構(gòu)造函數(shù)為字符串分配了動態(tài)存儲空間;當(dāng)對象撤銷時,必須釋放分配的存儲空間,否則,將造成內(nèi)存泄漏。因此,在析構(gòu)函數(shù)中用運算符delete釋放分配的存儲空間。這是構(gòu)造函數(shù)和析構(gòu)函數(shù)常見的用法。析構(gòu)函數(shù)通常是系統(tǒng)自動調(diào)用的,在下列情況下,析構(gòu)函數(shù)將被自動調(diào)用:

(1)一個對象的生存期結(jié)束時。例如,在一個函數(shù)內(nèi)定義的對象,當(dāng)該函數(shù)結(jié)束時將釋放該對象,系統(tǒng)將自動調(diào)用析構(gòu)函數(shù)。

(2)使用new運算符創(chuàng)建的對象,在使用delete運算符釋放該對象時,系統(tǒng)將自動調(diào)用析構(gòu)函數(shù)。3.3.5構(gòu)造函數(shù)的類型轉(zhuǎn)換和類型轉(zhuǎn)換函數(shù)在一個表達式中,如果多個數(shù)據(jù)的類型不一致,則對于基本數(shù)據(jù)類型,系統(tǒng)一般可以進行自動類型的轉(zhuǎn)換;同時,用戶也可以使用強制類型轉(zhuǎn)換方式進行類型轉(zhuǎn)換。對于用戶自定義的類類型,有時希望在類類型的對象與其它數(shù)據(jù)類型之間進行轉(zhuǎn)換。這可以分為兩種情況:一是將其它數(shù)據(jù)類型轉(zhuǎn)換為類類型,這可以通過構(gòu)造函數(shù)來實現(xiàn);二是將類類型轉(zhuǎn)換為其它數(shù)據(jù)類型,這可以通過類型轉(zhuǎn)換函數(shù)來完成。

1.通過構(gòu)造函數(shù)進行類型轉(zhuǎn)換類的構(gòu)造函數(shù)具有類型轉(zhuǎn)換的功能,它可以將其它數(shù)據(jù)類型的值轉(zhuǎn)換為它所在類的類型的值。通常使用單參數(shù)的構(gòu)造函數(shù),將構(gòu)造函數(shù)參數(shù)的類型的值轉(zhuǎn)換為它所在類的類型的值。分析下面的程序?!纠?.14】構(gòu)造函數(shù)的類型轉(zhuǎn)換功能示例。

#include<iostream.h>

classExample

{

private: intnum;

public: Example(int); voidPrint(); ~Example();

};

Example::Example(intn)

{ num=n; cout<<"Initializingwith:"<<num<<endl;

}

Example::~Example()

{ cout<<"Destructing:"<<num<<endl;

}

voidExample::Print()

{ cout<<"num="<<num<<endl;

}

voidmain()

{ ExampleX(0); X=10; //① X.Print(); cout<<"------------------------"<<endl; X=Example(20); //② X.Print();

}程序運行結(jié)果為:

Initializingwith:0

Initializingwith:10

Destructing:10

num=10

------------------------

Initializingwith:20

Destructing:20

num=20

Destructing:20類Example中有一個參數(shù)類型為int的構(gòu)造函數(shù),它可以用來進行類型轉(zhuǎn)換,將int類型轉(zhuǎn)換為類Example類型。主函數(shù)的語句①中,對象X為類Example類型,賦值運算符右邊為int類型的數(shù),類型不匹配,系統(tǒng)自動調(diào)用構(gòu)造函數(shù)將10轉(zhuǎn)換成類Example類型的對象。這時建立了一個隱藏的臨時對象并由被轉(zhuǎn)換的值初始化,將這個對象賦值給對象X,之后馬上撤銷。語句②中,Example(20)是個強制類型轉(zhuǎn)換,將20轉(zhuǎn)換成類Example類型,這時也建立了一個臨時對象,將這個對象賦值給X,之后馬上撤銷。如果類Example中沒有定義單參數(shù)的構(gòu)造函數(shù),則主函數(shù)中的語句①和②都將是非法的,系統(tǒng)不能完成由int類型向類Example類型的轉(zhuǎn)換。

2.類型轉(zhuǎn)換函數(shù)單參數(shù)構(gòu)造函數(shù)能夠?qū)崿F(xiàn)從參數(shù)類型(一般為基本數(shù)據(jù)類型)向類類型的轉(zhuǎn)換,但是不能實現(xiàn)將類類型轉(zhuǎn)換為基本數(shù)據(jù)類型的功能。類型轉(zhuǎn)換函數(shù)能夠?qū)崿F(xiàn)將類類型向其它數(shù)據(jù)類型轉(zhuǎn)換的功能。類型轉(zhuǎn)換函數(shù)是在類中定義的一個非靜態(tài)成員函數(shù),其一般形式為:

class類名

{ operator數(shù)據(jù)類型() //類型轉(zhuǎn)換函數(shù)

{ 函數(shù)體 } };其中,“類名”是要轉(zhuǎn)換的源類類型,“數(shù)據(jù)類型”是要將“類名”的類類型轉(zhuǎn)換成的類型,即目標(biāo)類型,它既可以是系統(tǒng)定義的基本類型,也可以是用戶自定義的類型。【例3.15】類型轉(zhuǎn)換函數(shù)示例。

#include<iostream.h>

classComplex

{

private: doublereal,imag;

public: Complex(doubler,doublei) { real=r; imag=i; cout<<"Constructorcalled."<<endl; } operatordouble(); //類型轉(zhuǎn)換函數(shù)的函數(shù)原型

operatorint(); //類型轉(zhuǎn)換函數(shù)的函數(shù)原型 voidPrint() { cout<<real; if(imag>0)cout<<"+"<<imag; if(imag<=0)cout<<imag; cout<<'i'<<endl; }

};

Complex::operatordouble() //類型轉(zhuǎn)換函數(shù)的定義

{ cout<<"fromComplextodouble"<<endl; returnreal*imag;

}

Complex::operatorint() //類型轉(zhuǎn)換函數(shù)的定義

{ cout<<"fromComplextoint"<<endl; returnint(real);

}

voidmain()

{ Complexc1(3.2,2.4),c2(2.0,-2.0); c1.Print(); c2.Print(); inti; i=c1; //隱式調(diào)用類型轉(zhuǎn)換函數(shù)

cout<<"i="<<i<<endl; doubled; d=double(c2); //顯式調(diào)用類型轉(zhuǎn)換函數(shù)

cout<<"d="<<d<<endl;

}程序運行結(jié)果如下:

Constructorcalled.

Constructorcalled.

3.2+2.4i

2-2i

fromComplextoint

i=3

fromComplextodouble

d=-4類型轉(zhuǎn)換函數(shù)的使用可以是顯式調(diào)用方式,也可以是隱式調(diào)用方式。程序中調(diào)用了兩次類型轉(zhuǎn)換函數(shù):第一次采用隱式調(diào)用方式,將類Complex的對象轉(zhuǎn)換成int類型;第二次采用顯式的強制類型轉(zhuǎn)換方式,將類Complex的對象轉(zhuǎn)換成double類型。關(guān)于類型轉(zhuǎn)換函數(shù),有以下幾點需要說明:

(1)一個類中可以定義多個類型轉(zhuǎn)換函數(shù),只要能夠從“數(shù)據(jù)類型”中將它們區(qū)別開即可。

(2)類型轉(zhuǎn)換函數(shù)是類中定義的一個非靜態(tài)成員函數(shù),不能定義成友元函數(shù)。

(3)類型轉(zhuǎn)換函數(shù)既沒有參數(shù),也不顯式給出返回類型。

(4)類型轉(zhuǎn)換函數(shù)可以被派生類繼承。3.4對象數(shù)組和對象指針3.4.1對象數(shù)組對象數(shù)組是指數(shù)組元素都是對象的數(shù)組。創(chuàng)建對象數(shù)組的方法與創(chuàng)建其它數(shù)據(jù)類型的數(shù)組相同。與其它數(shù)據(jù)類型的數(shù)組相同,可以使用下標(biāo)或指向?qū)ο髷?shù)組的指針訪問對象數(shù)組元素。由于數(shù)組元素也是一個對象,因此通過這個對象可以訪問它的公有成員,一般形式如下: 數(shù)組名[整型表達式].數(shù)據(jù)成員名或 數(shù)組名[整型表達式].成員函數(shù)名(實參表)【例3.16】對象數(shù)組示例。

#include<iostream.h>

classMyClass

{

private: intx;

public: MyClass() { x=100; } voidSetValue(inti) { x=i; } intGetValue() { returnx; }

};

voidmain()

{ MyClassobs[4]; //定義對象數(shù)組

cout<<"Originalvalue:"; for(inti=0;i<4;i++) cout<<obs[i].GetValue()<<''; cout<<endl; for(i=0;i<4;i++) obs[i].SetValue(2*i); cout<<"Changedvalue:"; for(i=0;i<4;i++) cout<<obs[i].GetValue()<<''; cout<<endl;

}程序運行結(jié)果為:

Originalvalue:100100100100

Changedvalue:0246類MyClass中定義了一個缺省構(gòu)造函數(shù)。從程序運行結(jié)果看,當(dāng)建立對象數(shù)組而沒有使用初始化列表初始化數(shù)組時,系統(tǒng)自動調(diào)用缺省構(gòu)造函數(shù)對對象數(shù)組的每一個元素進行初始化。如果類中沒有定義構(gòu)造函數(shù),則系統(tǒng)自動為類生成函數(shù)體為空的缺省構(gòu)造函數(shù),此時,對象數(shù)組元素的狀態(tài)不確定。如果類中定義了帶參數(shù)的構(gòu)造函數(shù),則在定義數(shù)組時,可以利用初始化列表對對象數(shù)組進行初始化。【例3.17】對象數(shù)組的初始化示例。

#include<iostream.h>

classPoint

{

private: intx,y;

public: Point() { x=y=100; } Point(inti) { x=y=i; } Point(inti,intj) { x=i; y=j; } intGetX() { returnx; } intGetY() { returny; }

};

voidmain()

{ Pointobs1[4]={-1,-2,-3,-4}; Point

obs2[4]={Point(1,2),Point(3,4),Point(5,6),Point(7,8)}; Pointobs3[4]={Point(22,33),Point(44,55)}; inti; for(i=0;i<4;i++)

cout<<'('<<obs1[i].GetX()<<','<<obs1[i].GetY()<<")"; cout<<endl; for(i=0;i<4;i++)

cout<<'('<<obs2[i].GetX()<<','<<obs2[i].GetY()<<")"; cout<<endl; obs3[2]=Point(66,77); obs3[3]=Point(88,99); cout<<"Changedvalueofobs3:"; for(i=0;i<4;i++)

cout<<'('<<obs3[i].GetX()<<','<<obs3[i].GetY()<<")"; cout<<endl;

}程序運行結(jié)果為:

(-1,-1)(-2,-2)(-3,-3)(-4,-4)

(1,2)(3,4)(5,6)(7,8)

(22,33)(44,55)(100,100)(100,100)

Changedvalueofobs3:(22,33)(44,55)(66,77)(88,99)類Point中定義了三個構(gòu)造函數(shù),包括一個缺省構(gòu)造函數(shù)。在定義對象數(shù)組obs1時,使用初始化列表{-1,-2,-3,-4}對對象數(shù)組的4個元素進行了初始化。這種初始化方法實際上調(diào)用了帶一個參數(shù)的構(gòu)造函數(shù)。在C++中,帶一個參數(shù)的構(gòu)造函數(shù)具有從參數(shù)類型到類類型的類型轉(zhuǎn)換功能。語句

Pointobs1[4]={-1,-2,-3,-4};實際上是如下格式的簡化:

Pointobs1[4]={Point(-1),Point(-2),Point(-3),Point(-4)};

當(dāng)使用帶多個參數(shù)的構(gòu)造函數(shù)對對象數(shù)組進行初始化時,必須顯式調(diào)用構(gòu)造函數(shù)。例如,定義對象數(shù)組obs2時,調(diào)用構(gòu)造函數(shù)Point(int,int)進行初始化:

Point

obs2[4]={Point(1,2),Point(3,4),Point(5,6),Point(7,8)};當(dāng)指定的初始化列表中的數(shù)據(jù)少于對象數(shù)組元素的個數(shù)時,后面的元素自動調(diào)用缺省構(gòu)造函數(shù)進行初始化。例如,定義對象數(shù)組obs3時,對數(shù)組元素obs3[2]和obs3[3]自動調(diào)用了缺省構(gòu)造函數(shù)進行初始化。另外請注意,若類中沒有缺省構(gòu)造函數(shù),則定義對象數(shù)組時必須使用初始化列表對所有數(shù)組元素進行初始化。例如,如果將例3.17中Point類的缺省構(gòu)造函數(shù)刪除,則程序在編譯時會產(chǎn)生錯誤。3.4.2對象指針【例3.18】對象指針的使用示例。

#include<iostream.h>

classMyClass

{

private: intx;

public: MyClass() { x=100; } voidSetValue(inti) { x=i; } intGetValue() { returnx; }

};

voidmain()

{ MyClassob,*p; MyClassobs[3],*ps; p=&ob; p->SetValue(50); cout<<p->GetValue()<<endl; ps=obs; //讓指針ps指向一維數(shù)組,也可以用語句:ps=&obs[0]; for(inti=0;i<3;i++) { ps->SetValue(20*i); cout<<ps->GetValue()<<''; ps++; } cout<<endl;

}程序運行結(jié)果為:

50

02040

與其它數(shù)據(jù)類型的指針一樣,當(dāng)對象指針遞增或遞減時,指針指向下一個或上一個在存儲空間中相鄰的對象。3.4.3this指針

C++中,類中的每一個非靜態(tài)成員函數(shù)(靜態(tài)成員函數(shù)將在3.5節(jié)中介紹)都有一個隱含的this指針,該指針作為成員函數(shù)的隱含參數(shù)。例如,例3.18中類MyClass的成員函數(shù)SetValue()中并沒有this指針:

voidMyClass::SetValue(inti) { x=i; }但實際上編譯器會將this指針作為成員函數(shù)的參數(shù),即編譯器所認(rèn)識的成員函數(shù)SetValue()的定義形式如下:

voidMyClass::SetValue(MyClass*constthis,inti) { this->x=i; }

當(dāng)程序中的一個對象調(diào)用非靜態(tài)成員函數(shù)時,系統(tǒng)將該對象的地址傳遞給這個隱含參數(shù),即this指針指向調(diào)用成員函數(shù)的對象。例如,如下的函數(shù)調(diào)用:

MyClassob; ob.SetValue(5);編譯器將其轉(zhuǎn)換為:

SetValue(&ob,5);【例3.19】this指針使用示例。

#include<iostream.h>

classPoint

{

private: intx,y;

public: Point() { x=y=0; } Point(inti,intj) { x=i; y=j; } voidShowPoint(); voidCopy(Point&p);

};

voidPoint::ShowPoint()

{ cout<<'('<<x<<','<<y<<')'<<endl;

}

voidPoint::Copy(Point&p)

{ if(this==&p) return; *this=p;

}

voidmain()

{ Pointp1,p2(5,8); cout<<"OringinalPoint:"; p1.ShowPoint(); p1.Copy(p2); cout<<"ChangedPoint:"; p1.ShowPoint();

}程序運行結(jié)果如下:

OringinalPoint:(0,0)

ChangedPoint:(5,8)類中成員函數(shù)Copy的功能是將一個已知的對象賦值給調(diào)用該成員函數(shù)的對象。函數(shù)中if語句用來判斷兩個對象是否相同,若相同則不賦值,不相同則賦值。

this指針在Windows程序設(shè)計中使用得比較普遍。在程序中不能修改this指針。3.5靜態(tài)成員3.5.1靜態(tài)數(shù)據(jù)成員一般的數(shù)據(jù)成員在不同的對象內(nèi)有各自的拷貝,而靜態(tài)數(shù)據(jù)成員是類的所有對象共享的成員,無論類有多少個對象,它在內(nèi)存中只有一份拷貝,被存儲在公用內(nèi)存中。在類的說明部分,使用關(guān)鍵字static對靜態(tài)數(shù)據(jù)成員進行引用性說明;同時,必須在文件作用域的某個地方使用類名限定進行定義性說明,并且定義性說明只能進行一次。定義性說明一般放在類的實現(xiàn)部分,在進行定義性說明時可以進行初始化。格式如下: 數(shù)據(jù)類型類名::靜態(tài)數(shù)據(jù)成員=初始化表達式;

如果在進行定義性說明時沒有進行初始化,則系統(tǒng)自動將其初始化為0。【例3.20】靜態(tài)數(shù)據(jù)成員的定義和使用示例。

#include<iostream.h>

classtest

{

public: staticinti; //靜態(tài)數(shù)據(jù)成員的引用性說明

};

inttest::i; //對靜態(tài)數(shù)據(jù)成員進行定義性說明

voidmain()

{ cout<<test::i<<endl; test::i=20; cout<<test::i<<endl; testob1,ob2; cout<<ob1.i<<endl; ob1.i=40; cout<<ob2.i<<endl;

}程序運行結(jié)果如下:

0

20

20

40從程序運行結(jié)果可以看出,即使沒有建立對象,靜態(tài)成員也已經(jīng)存在。對于公有靜態(tài)數(shù)據(jù)成員,可以使用類名加作用域運算符“::”對靜態(tài)成員進行訪問,也可以通過對象進行訪問,但私有靜態(tài)數(shù)據(jù)成員不能被外部程序訪問。因為靜態(tài)成員不是對象的成員,是屬于整個類的,所以通過“類名::”進行訪問更好。在進行定義性說明時,不使用關(guān)鍵字static。3.5.2靜態(tài)成員函數(shù)在類中使用關(guān)鍵字static進行說明的成員函數(shù)稱為靜態(tài)成員函數(shù)。同樣,靜態(tài)成員函數(shù)是屬于整個類的。靜態(tài)成員函數(shù)的定義可以放在類的說明部分,也可以放在類的實現(xiàn)部分。對于公有靜態(tài)成員函數(shù),可以在程序中訪問,既可以使用類名限定進行訪問,也可以通過對象進行訪問: 類名::靜態(tài)成員函數(shù)(參數(shù)表)或 對象名.靜態(tài)成員函數(shù)(參數(shù)表)或 指向?qū)ο蟮闹羔?>靜態(tài)成員函數(shù)(參數(shù)表)【例3.21】靜態(tài)成員的使用示例。

#include<iostream.h>

classPoint

{

private: intx,y; staticintcount;

public: Point(inti,intj) { x=i; y=j; count++; } staticvoidDisplay(Point);

};

intPoint::count=0;

voidPoint::Display(Pointp)

{ cout<<"count="<<count<<",point:("<<p.x<<','<<p.y<<')‘

<<endl;

}

voidmain()

{ Pointp1(1,2); Point::Display(p1); Pointp2(3,4); p2.Display(p2); Pointp3[3]={Point(4,5),Point(6,7),Point(8,9)}; for(inti=0;i<3;i++) p3[i].Display(p3[i]);//也可以為:Point::Display(p3[i]);

}程序運行結(jié)果如下:

count=1,point:(1,2)

count=2,point:(3,4)

count=5,point:(4,5)

count=5,point:(6,7)

count=5,point:(8,9)

Point類中的靜態(tài)數(shù)據(jù)成員count用來對類的對象進行計數(shù),當(dāng)使用類Point建立一個對象時,通過構(gòu)造函數(shù)對count加1。靜態(tài)成員函數(shù)Display用來輸出點的坐標(biāo)和當(dāng)前類的對象數(shù)。從程序運行結(jié)果看,當(dāng)定義對象數(shù)組p3時,調(diào)用三次構(gòu)造函數(shù),count值為5,通過不同的對象數(shù)組元素輸出的count值相同。在定義和使用靜態(tài)成員函數(shù)時,應(yīng)注意以下幾點:

(1)若在類的實現(xiàn)部分定義靜態(tài)成員函數(shù),則前面不加關(guān)鍵字static。

(2)由于靜態(tài)成員函數(shù)沒有this指針,因此,在靜態(tài)成員函數(shù)中可以直接訪問類的靜態(tài)成員,但不能直接訪問類的非靜態(tài)成員。如果要訪問非靜態(tài)成員,只能通過對象或指向?qū)ο蟮闹羔樳M行。這時可以將類的對象作為靜態(tài)成員函數(shù)的參數(shù)。

(3)在程序中不能訪問類的私有和保護靜態(tài)成員函數(shù)。3.6友元類具有封裝性,類的私有成員一般只能通過該類的成員函數(shù)才能被訪問。這種封裝性隱藏了對象的部分成員,保證了對象的安全性。但封裝有時也會給編程帶來不便。另外,如果程序中要訪問對象的私有成員,就必須通過對象來調(diào)用公有成員函數(shù),而函數(shù)調(diào)用需要有時間和空間的開銷,故會影響程序的執(zhí)行效率。友元可以是一個函數(shù),稱為友元函數(shù),也可以是一個類,稱為友元類。3.6.1友元函數(shù)友元函數(shù)是在類中用關(guān)鍵字friend說明的函數(shù)。說明一個友元函數(shù)的一般形式為:

friend數(shù)據(jù)類型函數(shù)名(參數(shù)表);

友元函數(shù)具有如下特點:

(1)友元函數(shù)不是類的成員函數(shù),可以定義在類的說明部分,也可以定義在類的實現(xiàn)部分。在類的實現(xiàn)部分定義時,其定義格式與一般的普通函數(shù)相同。

(2)友元函數(shù)可以訪問類中的所有成員,包括私有成員。

(3)友元函數(shù)可以在類中的任何位置聲明。因為友元函數(shù)不是類的成員函數(shù),所以訪問權(quán)限控制符對其沒有影響,

(4)一個函數(shù)可以聲明為多個類的友元函數(shù)。【例3.22】友元函數(shù)的定義和使用示例。

#include<iostream.h>

#include<math.h>

classPoint

{

private: intx,y;

public: Point() { x=y=0; } Point(inti,intj) { x=i; y=j; } voidShowPoint() { cout<<'('<<x<<','<<y<<')'; }

frienddoubleDistance(Point,Point); //友元函數(shù)的聲明,定義在類的實現(xiàn)部分, //也可以直接在這里定義

};

doubleDistance(Pointa,Pointb) //友元函數(shù)的定義,與普通函數(shù)相同

{ intdx=a.x-b.y; intdy=a.y-b.y; returnsqrt(dx*dx+dy*dy);

}

voidmain()

{ Pointp1,p2(5,8); cout<<"thedistancebetween"; p1.ShowPoint(); cout<<"and"; p2.ShowPoint(); cout<<"is"<<Distance(p1,p2)<<endl;

}

溫馨提示

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

評論

0/150

提交評論