




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
類和對象及其封裝性第1頁,共143頁,2023年,2月20日,星期四本章要點
1類的定義及其類對象的封裝性
2類成員函數(shù)的定義
3類對象的定義和使用
4類的構(gòu)造函數(shù)和析構(gòu)函數(shù)
5類對象的動態(tài)創(chuàng)建和釋放
6類對象的賦值與復(fù)制
7與類對象有關(guān)的指針
8類的靜態(tài)成員
9類對象成員、類對象數(shù)組和類對象參數(shù)
10友元(友元函數(shù)、友元成員和友元類)
11類的只讀成員函數(shù)定義第2頁,共143頁,2023年,2月20日,星期四3.1類的定義及其類對象的封裝性無論采用哪種程序設(shè)計范型所設(shè)計的程序都是由數(shù)據(jù)處理這些數(shù)據(jù)的操作組成的。程序的運(yùn)行就是按照特定的結(jié)構(gòu)和流程將操作施加在相應(yīng)的數(shù)據(jù)上,完成程序需要實現(xiàn)的功能。在傳統(tǒng)設(shè)計范型中,數(shù)據(jù)是使用語言所提供的簡單數(shù)據(jù)類型和構(gòu)造數(shù)據(jù)類型(例如C語言中的結(jié)構(gòu)類型
struct)定義生成的;而操作是通過過程或函數(shù)的形式定義提供的。第3頁,共143頁,2023年,2月20日,星期四在面向?qū)ο笤O(shè)計范型中,使用了數(shù)據(jù)抽象的概念,即數(shù)據(jù)總是與允許施加它們的操作綁定在一起的。這就要求編程語言能夠提供符合數(shù)據(jù)抽象的預(yù)定義數(shù)據(jù)類型,特別需要提供能構(gòu)造符合數(shù)據(jù)抽象用戶自定義類型的構(gòu)造數(shù)據(jù)類型(例如C++
和Java
語言中的類類型class)。程序中的數(shù)據(jù)和操作都是由按數(shù)據(jù)抽象封裝起來的對象提供的。第4頁,共143頁,2023年,2月20日,星期四3.1.1C++的類類型定義
在C++中,用戶可以使用類類型關(guān)鍵字
class定義自己的抽象數(shù)據(jù)類型。這種定義方法和形式與使用結(jié)構(gòu)體類型關(guān)鍵字struct定義數(shù)據(jù)結(jié)構(gòu)類型十分相似。例如,可以用struct定義描述學(xué)生基本信息的數(shù)據(jù)結(jié)構(gòu)類型Student:
struct
Student { intnum; charname[20]; charsex; };第5頁,共143頁,2023年,2月20日,星期四同樣,可以用class定義描述學(xué)生基本信息和基本操作的數(shù)據(jù)類型Student:
class
Student
{ intnum; charname[20]; charsex;
public: voiddisplay() { cout<<“num:”<<num<<endl; cout<<“name:”<<name<<endl; cout<<“sex:”<<sex<<endl; } };第6頁,共143頁,2023年,2月20日,星期四比較兩種用戶自定義類型,它們的共同之處表現(xiàn)在:⑴類型定義首行的格式相同,由類型關(guān)鍵字(struct
或
class)與自定義類名組成。例如:“structStudent”和“classStudent”。⑵類型定義體都使用左花括號“{”表示開始,使用右花括號“}”表示結(jié)束,并用分號“;”表示整個自定義類型定義工作完成。⑶使用自定義類型(結(jié)構(gòu)或類)定義類型實體(結(jié)構(gòu)
變量或類對象)的格式相同。在面向?qū)ο蟮某绦蛟O(shè)計中通常將所有的類型(包括系統(tǒng)預(yù)定義的簡單數(shù)據(jù)類型)實體可以統(tǒng)一稱為對象。例如,可使用自定義結(jié)構(gòu)類型Student定義結(jié)構(gòu)對象:
Studentstud1,stud2;
也可使用自定義類類型Student定義類對象:
Studentstud1,stud2;第7頁,共143頁,2023年,2月20日,星期四在面向?qū)ο蟮某绦蛟O(shè)計中通常將所有的類型(包括系統(tǒng)預(yù)定義的簡單數(shù)據(jù)類型)實體可以統(tǒng)一稱為對象。例如,可使用自定義結(jié)構(gòu)類型Student定義結(jié)構(gòu)對象:
Studentstud1,stud2;
也可使用自定義類類型Student定義類對象:
Studentstud1,stud2;第8頁,共143頁,2023年,2月20日,星期四二者的不同之處表現(xiàn)在:⑴使用C
語言的struct
定義的結(jié)構(gòu)類型的定義體中只包含數(shù)據(jù)成員,而使用class定義類類型的定義體中既包含數(shù)據(jù)成員,還包含了操作這些數(shù)據(jù)的成員函數(shù)。注意,在C++
中,struct
定義能力被擴(kuò)展,也可以class定義一樣包含操作數(shù)據(jù)成員的成員函數(shù)。⑵結(jié)構(gòu)類型的成員的缺省訪問權(quán)限均為公有,即可以從結(jié)構(gòu)對象外直接訪問。例如:
Studentstud1; cout<<“num:”<<stud1.num<<endl;第9頁,共143頁,2023年,2月20日,星期四
cout<<“name:”<<<<endl; cout<<“sex:”<<stud1.sex<<endl;
而類類型的成員的缺省訪問權(quán)限均為私有,例如,類Student
中的數(shù)據(jù)成員就不能從類外直接訪問,而顯示這些數(shù)據(jù)只能通過調(diào)用成員函數(shù)
display實現(xiàn)。
Studentstud1; cout<<“name:”<<<<endl;//非法
stud1.display();第10頁,共143頁,2023年,2月20日,星期四3.1.2類類型成員的訪問權(quán)限為了實現(xiàn)類對象的封裝性(數(shù)據(jù)隱藏和提供訪問接口)類類型定義為類成員提供了私有和公有兩種基本訪問權(quán)限供用戶選擇。1私有成員①訪問權(quán)限:只限于類成員訪問。②關(guān)鍵字:private聲明或從定義體開始的缺省聲明。例如,下面的Student定義與前面的Student
定義等價的:第11頁,共143頁,2023年,2月20日,星期四
class
Student {
private: intnum; charname[20]; charsex;
public: voiddisplay(){ cout<<“num:”<<num<<endl; cout<<“name:”<<name<<endl; cout<<“sex:”<<sex<<endl; } };第12頁,共143頁,2023年,2月20日,星期四③私有段:從private:
開始至其它訪問權(quán)限聲明之間 所有成員組成的代碼段。例如Student
定義中從
private:
開始到public:
之間的所數(shù)據(jù)成員。④成員種類:數(shù)據(jù)成員和成員函數(shù)。2公有成員①訪問權(quán)限:允許類成員和類外的任何訪問。②關(guān)鍵字:public。③公有段:從public:
至其它成員聲明之間所有成員 組成的代碼段。④成員種類:數(shù)據(jù)成員和成員函數(shù)。第13頁,共143頁,2023年,2月20日,星期四使用私有成員來隱藏由類對象操作的數(shù)據(jù),然后提供相應(yīng)的公有成員函數(shù)來訪問和操作這些數(shù)據(jù),而訪問和操作這些數(shù)據(jù)實現(xiàn)細(xì)節(jié)通常是被隱藏起來的。除了私有和公有兩種基本訪問權(quán)限外,類類型定義還提供了允許類成員和派生類成員訪問,而不允許類外訪問的保護(hù)成員訪問權(quán)限(protected),以滿足實現(xiàn)繼承性的需要。第14頁,共143頁,2023年,2月20日,星期四為了使C++
語言所設(shè)計程序中的數(shù)據(jù)都能實現(xiàn)數(shù)據(jù)抽象,并能與C
語言設(shè)計的程序中的數(shù)據(jù)兼容,C++
仿照類類型定義的功能,對struct
定義的結(jié)構(gòu)體類型功能進(jìn)行如下擴(kuò)展:⑴定義體中也可以包括對數(shù)據(jù)成員進(jìn)行處理和操作的
成員函數(shù)。⑵添加了與類類型定義相同的成員訪問權(quán)限聲明功能,但仍然保留了缺省聲明表示成員的訪問權(quán)限為
公用的基本特點。擴(kuò)展后的struct
可以定義與類類型效果相同的結(jié)構(gòu)類型,例如:第15頁,共143頁,2023年,2月20日,星期四
struct
Student {
private: intnum; charname[20]; charsex;
public: voiddisplay(){ cout<<“num:”<<num<<endl; cout<<“name:”<<name<<endl; cout<<“sex:”<<sex<<endl; } };第16頁,共143頁,2023年,2月20日,星期四該結(jié)構(gòu)類型與先前使用class
定義的Student
類型的效果完全相同。請注意,這并不意味著可以用struct
替代class,因為使用class定義的類類型的缺省私有性質(zhì)能更方便、安全地實現(xiàn)面向?qū)ο蟪绦蛟O(shè)計對類對象的要求,因此強(qiáng)烈建議使用class建立數(shù)據(jù)類型;而只有希望所建立類型的全部成員都是公有訪問權(quán)限時,使用struct建立結(jié)構(gòu)類型比較方便。第17頁,共143頁,2023年,2月20日,星期四3.1.3類類型的構(gòu)造
類類型定義為所定義的數(shù)據(jù)類型建立了一個明確的邊界,類定義體中的私有成員(數(shù)據(jù)成員和成員函數(shù))和公有成員函數(shù)的實現(xiàn)細(xì)節(jié)均被封裝在此邊界內(nèi),使得這些類成員和實現(xiàn)細(xì)節(jié)無法從類對象外被訪問,從而受到保護(hù)。同時對類公有成員(數(shù)據(jù)成員和成員函數(shù))的訪問和調(diào)用又為類對象之間的通訊提供了接口,使得類對象成為一個既訪問安全又操作方便的抽象數(shù)據(jù)實體。下面以一個簡單機(jī)器人類為例說明類類型的構(gòu)造:第18頁,共143頁,2023年,2月20日,星期四①確定機(jī)器人狀態(tài)的屬性:是位置和面對的方向②改變、訪問和顯示機(jī)器人狀態(tài)的操作:有定位、
轉(zhuǎn)向、前進(jìn)、顯示狀態(tài)等。
classHominoid { intdirction; //機(jī)器人的方向
pointlocation; //機(jī)器人的位置
public: voidturnLeft(); //向左轉(zhuǎn)90度
voidturnRight(); //向右轉(zhuǎn)90度
booladvance(); //前進(jìn)一步
pointlocation(pointloc); //定位
boolfacingWall(); //判斷是否面對墻壁
voiddisplay(); //顯示當(dāng)前位置
… };第19頁,共143頁,2023年,2月20日,星期四
booladvance(); //前進(jìn)一步
pointlocation(pointloc); //定位
boolfacingWall(); //判斷是否面對墻壁
voiddisplay(); //顯示當(dāng)前位置
… };
該定義所建立的機(jī)器人類的構(gòu)造可以形象地用下圖 表示,它很象一個封裝好的器件。第20頁,共143頁,2023年,2月20日,星期四directionlocation...turnLeftdislayturnRightfacingWalladvancelocation屬性操作邊界第21頁,共143頁,2023年,2月20日,星期四
Java
的類定義與C++
的類定義在格式上基本相同,但有兩點是不一樣的:⑴成員的訪問權(quán)限必須逐個顯式說明;⑵類定義結(jié)束不要分號“;”。例如:
classUser
{
privateStringname;
privateintage;
publicUser(Stringstrintyy){name=str;age=yy;} } 返回第22頁,共143頁,2023年,2月20日,星期四3.2類成員函數(shù)的定義1成員函數(shù)的性質(zhì)類的成員函數(shù)在聲明和定義的格式上以及用法和作用上與一般函數(shù)基本一致。但由于成員函數(shù)是屬于某一個類的成員,因此它與一般函數(shù)的區(qū)別表現(xiàn):⑴作用域在類定義體所確定的邊界內(nèi),即可以訪問本類的任何成員(私有和公有的數(shù)據(jù)和函數(shù))。⑵需要根據(jù)功能和作用指定成員函數(shù)的訪問權(quán)限,一般情況下,將向類外提供操作功能的成員函數(shù)指定為public,將只為類內(nèi)提供服務(wù)功能的成員函數(shù)指定為private,將只為類內(nèi)和派生類提供服務(wù)功能的成員函數(shù)指定為protected。第23頁,共143頁,2023年,2月20日,星期四2成員函數(shù)的聲明和定義形式
⑴在類定義體內(nèi)定義成員函數(shù)的實現(xiàn)代碼。這種形 式下,函數(shù)定義的首部將起到函數(shù)原型的作用, 因此無須成員函數(shù)定義之前的原型聲明。例如: classpoint
{ intx,y; public: voidsetpoint(intvx,intvy) {x=vx;y=vy;} };第24頁,共143頁,2023年,2月20日,星期四
⑵在類定義體內(nèi)聲明成員函數(shù),而在類定義體外定 義成員函數(shù)的實現(xiàn)代碼。采用這種定義形式的 時,類定義體外的定義代碼必須滿足:
①在成員函數(shù)名之前應(yīng)綴上所屬的類名::,“::”是 作用域運(yùn)算符,以便說明函數(shù)的作用域。②成員函數(shù)定義的首部(函數(shù)的返回類型、函數(shù) 名和參數(shù)表列)必須與在類定義體中聲明的該 函數(shù)的原型一致。例如:第25頁,共143頁,2023年,2月20日,星期四 classpoint{ intx,y; public: voidsetpoint(int,int); }; voidpoint::setpoint(intvx,intvy) { x=vx; y=vy; }由于第⑵種形式不僅可以減少類定義體的代碼長度,使類定義體清晰、可讀性好;更重要的是有助 于類的操作接口與操作實現(xiàn)細(xì)節(jié)相分離,并隱藏細(xì) 節(jié)。因此,提倡采用該形式定義類成員函數(shù)。第26頁,共143頁,2023年,2月20日,星期四3類的內(nèi)置(內(nèi)聯(lián))成員函數(shù)定義方式:
⑴隱式定義——函數(shù)定義在類定義體中,此時只要函數(shù)的實現(xiàn)代碼符合內(nèi)置函數(shù)的定義要求,該成員函數(shù)就會自動被定義內(nèi)置函數(shù),而說明內(nèi)置函數(shù)的關(guān)鍵字inline可以忽略。例如: classpoint
{ intx,y; public: voidsetpoint(intvx,intvy) //內(nèi)置函數(shù) {x=vx;y=vy;} };其中成員函數(shù)setpoint定義的首部與加綴inline的首部“inlinevoidsetpoint(intvx,intvy)”等效。第27頁,共143頁,2023年,2月20日,星期四
⑵顯式定義——函數(shù)聲明在類定義體中,而函數(shù)定義在類定義體外,此時函數(shù)定義的首部必須冠以關(guān)鍵字inline說明此函數(shù)是內(nèi)置的。例如: classpoint { intx,y; public:
inlinevoidsetpoint(int,int); //內(nèi)置函數(shù)聲明 };
inlinevoidpoint::setpoint(intvx,intvy)//內(nèi)置函數(shù)定義 { x=vx; y=vy; }第28頁,共143頁,2023年,2月20日,星期四需要特別注意的是:由于調(diào)用內(nèi)置函數(shù)需要將內(nèi)置函數(shù)的目標(biāo)代碼復(fù)制到它被調(diào)用的位置,因此編譯器在進(jìn)行內(nèi)置函數(shù)的調(diào)用編譯時,必須能獲得被調(diào)內(nèi)置函數(shù)的目標(biāo)代碼。這就需要在調(diào)用內(nèi)置函數(shù)的源代碼文件中必須包含被調(diào)用內(nèi)置函數(shù)的定義代碼的源代碼文件。也就是說,如果某個被調(diào)用的內(nèi)置成員函數(shù)定義在類定義體中,該類定義體代碼被保存在一個頭文件中,例如“student.h”,則調(diào)用該內(nèi)置函數(shù)的源文件應(yīng)添加預(yù)編譯命令#include“student.h”。如果該內(nèi)置函數(shù)定義在類定義體外,代碼包含在類實現(xiàn)文件中,例如“student.cpp”,則調(diào)用該內(nèi)置函數(shù)的源文件應(yīng)添加預(yù)編譯命令#include“student.cpp”。第29頁,共143頁,2023年,2月20日,星期四4成員函數(shù)的存儲空間從類類型的定義不難看出,類的數(shù)據(jù)成員(對象屬性)中保存的數(shù)據(jù)值代表了類對象的狀態(tài),決定了該類的不同對象的差別,因此當(dāng)類對象創(chuàng)建時,每個類對象都必須獨占一份(個數(shù)相同、類型相同)
數(shù)據(jù)成員存儲空間,用于保存區(qū)別于其他對象的狀態(tài);而類的成員函數(shù)描述了該類所有對象的統(tǒng)一行為操作,而操作結(jié)果(行為表現(xiàn))的差異取決于不同對象的狀態(tài),因此,成員函數(shù)的運(yùn)行代碼被存儲在與數(shù)據(jù)成員存儲空間不同的代碼空間中,被該類的所有對象共享。第30頁,共143頁,2023年,2月20日,星期四例如:由于語句Studentstud1,stud2,stud3;
執(zhí)行所創(chuàng)建的3
個Student
對象在程序運(yùn)行空間中占用內(nèi)存的大小和位置的狀態(tài)示意如下:返回程序數(shù)據(jù)區(qū)程序代碼區(qū)Student::Student()的運(yùn)行代碼Student::~Student()的運(yùn)行代碼Student::display()的運(yùn)行代碼numnamesexstud1numnamesexstud2numnamesexstud3第31頁,共143頁,2023年,2月20日,星期四3.3類對象的定義和使用3.3.1類與對象的關(guān)系⑴類是一組具有相同屬性和行為的對象的抽象,是創(chuàng)建對象的模板,是用戶使用class
創(chuàng)建的自定義類型。類一旦創(chuàng)建,其作用可與系統(tǒng)預(yù)定義類型(例如,int、double等)類比。⑵
對象是類的實例,創(chuàng)建類的對象可以與創(chuàng)建預(yù)定義類型的變量(例如,int
x;、doubled;等)類比。⑶類只是提供了該類對象的創(chuàng)建和使用的方法和規(guī)則,因此類本身不占用內(nèi)存。創(chuàng)建類對象時將按類定義提供的方法和規(guī)則,在內(nèi)存中為類對象分配空間,因此,封裝是對類對象而言的。第32頁,共143頁,2023年,2月20日,星期四3.3.2類對象的定義方法:1先創(chuàng)建類類型,使用時再定義對象
大多數(shù)情況均采用該方法定義類對象。這樣創(chuàng)建的類對象的生存周期取決于創(chuàng)建的位置。例如:
classStudent{…}; voidmain() {
StudentZhang,Li; //創(chuàng)建Student局部對象
… }
雖然C++
也允許將上述的類對象定義語句寫成:
classStudentZhang,Li;
(C
風(fēng)格)但不能體現(xiàn)C++
面向?qū)ο蟮脑O(shè)計風(fēng)格,且不方便簡潔,所以不提倡。第33頁,共143頁,2023年,2月20日,星期四2在創(chuàng)建類類型的同時定義對象。使用這種方法定義的類對象的生存周期取決于類類型的創(chuàng)建位置。例如: classStudent { intnum; charname[20]; charsex; public: voiddisplay(){ cout<<“num:”<<num<<endl; cout<<“name:”<<name<<endl; cout<<“sex:”<<sex<<endl; } }Zhang,Li;第34頁,共143頁,2023年,2月20日,星期四3不出現(xiàn)類名,直接定義對象。使用這種方法定義類對象的類類型一般在程序中只 出現(xiàn)和使用一次。例如,創(chuàng)建一個描述人員信息的 類類型person
中包含了一個聯(lián)系信息屬性成員
touchInfo,該屬性是一個由人員的通訊地址、郵政編 碼、電話號碼、e-mail等信息和相應(yīng)的處理操作構(gòu) 成的類類型對象,而該類類型只在定義touchInfo
屬 性出現(xiàn)和使用一次。此時就可以采用本方法在類類 型person
中定義touchInfo
屬性:第35頁,共143頁,2023年,2月20日,星期四 classperson
{ … class //無須類名 { stringAddess; stringpostCode; stringponeNum; stringE-mail; public: … }touchInfo; … };第36頁,共143頁,2023年,2月20日,星期四3.3.3類對象的使用
類的使用是通過首先創(chuàng)建類對象,然后引用類對象的公有成員(訪問數(shù)據(jù)成員或調(diào)用成員函數(shù))達(dá)到對該類的使用。依據(jù)類對象的創(chuàng)建方式不同,類的使用形式可以分為三種:1通過對象名和成員運(yùn)算符訪問對象成員 一般形式:對象名.成員名;
其中成員名必須是對象名所指明對象的所屬類的公 有數(shù)據(jù)成員名或公有成員函數(shù)名。例如:第37頁,共143頁,2023年,2月20日,星期四 classpoint{ intx,y; public: setpoint(intvx,intvy){x=vx;y=vy;} }; voidfun(){
pointpt; //創(chuàng)建point類對象pt
pt.setpoint(10,10); //給point對象pt的坐標(biāo)x,y賦值
pt.x=50;
//錯誤,不能訪問私有成員 ... }
代碼pt.setpoint(10,10)實際是pt.point::setpoint(10,10)
的縮寫,表明通過對象訪問對象名所指示的類成員。第38頁,共143頁,2023年,2月20日,星期四2通過指向?qū)ο蟮闹羔樤L問對象成員 一般形式:指針名->成員名; 其中成員名必須是指針?biāo)笇ο蟮乃鶎兕惖墓袛?shù) 據(jù)成員名或公有成員函數(shù)名。例如: classpoint{ intx,y; public: setpoint(intvx,intvy){x=vx;y=vy;} }; voidfun(){
point*p=newpoint; //p指向動態(tài)創(chuàng)建的point對象
p->setpoint(10,10);//給p所指對象的坐標(biāo)x,y賦值 ... }第39頁,共143頁,2023年,2月20日,星期四3通過對象的引用訪問對象成員 一般形式:對象引用名.成員名; 其中成員名必須是對象引用名所引用對象的所屬類
的公有數(shù)據(jù)成員名或公有成員函數(shù)名。例如: classpoint{ intx,y; public: setpoint(intvx,intvy){x=vx;y=vy;} }; voidfun(){
pointpt,&p=pt; //p引用point對象pt
p.setpoint(10,10); //給p引用的對象的坐標(biāo)x,y賦值 ... }第40頁,共143頁,2023年,2月20日,星期四3.3.4成員名解析
由于類成員作用域在該類定義體所限定的邊界內(nèi),因此,不同類中具有同名的成員是不會產(chǎn)生二義性。例如: classrealSet
//定義一個實數(shù)集合類 { … public: voidprint(); }; classintSet
//定義一個整數(shù)集合類 { … public: voidprint(); };第41頁,共143頁,2023年,2月20日,星期四 voidfun() {
intSet
is;
realSet
rs; …
is.print(); //調(diào)用intSet類中的print()函數(shù)
rs.print(); //調(diào)用realSet類中的print()函數(shù) } 顯然不會引起二義性錯誤。返回
第42頁,共143頁,2023年,2月20日,星期四3.4構(gòu)造函數(shù) 使用類定義對象時,需要一種操作,使所定義的對象與類的定義域相關(guān)。實現(xiàn)這一操作的成員函數(shù)稱為構(gòu)造函數(shù),該函數(shù)要完成的操作包括:⑴依據(jù)類數(shù)據(jù)成員的個數(shù)和類型為對象分配內(nèi)存;⑵根據(jù)需要為對象的數(shù)據(jù)成員進(jìn)行必要的初始化。 構(gòu)造函數(shù)是類必須擁有的特殊成員函數(shù),該函數(shù)從定義形式到使用場合和方法上都與一般成員函數(shù)有所區(qū)別,這些差異表現(xiàn)在以下幾個方面:第43頁,共143頁,2023年,2月20日,星期四⑴構(gòu)造函數(shù)名必須與類名相同,否則編譯器將會把它當(dāng)作一般成員函數(shù)對待。例如:
classStudent { public:
Student(); … };
Student::Student() { … }第44頁,共143頁,2023年,2月20日,星期四⑵構(gòu)造函數(shù)沒有返回值,因此,聲明和定義構(gòu)造函數(shù) 時都不能說明它的返回類型;⑶構(gòu)造函數(shù)的功能是將對象初始化,因此構(gòu)造函數(shù)一 般只對數(shù)據(jù)成員進(jìn)行初始化和必要的輔助操作,而 不提倡做與初始化無關(guān)的操作。⑷系統(tǒng)總會為類提供一個隱含的缺省構(gòu)造函數(shù)。該構(gòu) 造函數(shù)實際上是一個空定義體函數(shù),因此只能為對 象分配空間而不能為數(shù)據(jù)成員進(jìn)行初始化。 在大多數(shù)情況下,數(shù)據(jù)成員的初始化操作是十分必 要的,因此通常需要顯式定義構(gòu)造函數(shù)。構(gòu)造函數(shù) 一旦顯式定義,缺省構(gòu)造函數(shù)將被覆蓋。第45頁,共143頁,2023年,2月20日,星期四⑸在程序運(yùn)行過程中,類對象是在進(jìn)入其作用域時才 被創(chuàng)建的。也就是說,此時類對象的構(gòu)造函數(shù)被調(diào) 用。⑹構(gòu)造函數(shù)在類對象創(chuàng)建時由系統(tǒng)自動執(zhí)行,不需要 用戶調(diào)用,也不能由用戶調(diào)用。例如:
Studentstud1; //系統(tǒng)調(diào)用構(gòu)造函數(shù)創(chuàng)建stud1 stud1.Student(); //企圖用一般成員函數(shù)的調(diào)用方法
//調(diào)用構(gòu)造函數(shù),因此是錯誤的。⑺不能為構(gòu)造函數(shù)定義函數(shù)指針,也不能獲取構(gòu)造函 數(shù)的調(diào)用地址。⑻基類的構(gòu)造函數(shù)不能被派生類繼承。⑼構(gòu)造函數(shù)不能聲明為虛函數(shù)。第46頁,共143頁,2023年,2月20日,星期四例3-1
定義一個整數(shù)隊列類,使用由系統(tǒng)隱含提供的缺 省構(gòu)造函數(shù)創(chuàng)建整數(shù)隊列,并測試隊列功能。1問題分析
⑴用例分析
⑵類圖描述向隊列中裝入整數(shù)從隊列中取出整數(shù)用戶qurue-q[2..*]:int-head:int-tail:int+qput(ini:int)+qget():int第47頁,共143頁,2023年,2月20日,星期四
2詳細(xì)設(shè)計
⑴類設(shè)計①qurue類
ⅰ類定義 classqueue { intq[100]; //隊列空間 inthead,tail; //隊列頭、尾指示 public: voidqput(inti); //隊列插入操作 intqget(); //隊列取出操作 };第48頁,共143頁,2023年,2月20日,星期四
ⅱ算法描述 成員函數(shù)qput
的N-S
流程圖: 成員函數(shù)qget
的N-S
流程圖:
tail==100&&tail-head==100?
真假顯示"隊列滿"
trail=trail+1,數(shù)據(jù)插入隊尾
head==tail?真假顯示"隊列空"
head=head+1
從隊頭取數(shù)據(jù)
tail==100?真 假移動隊列數(shù)據(jù),修改隊列指針tail=tail–head;head=0;第49頁,共143頁,2023年,2月20日,星期四
⑵類對象創(chuàng)建和使用
main函數(shù)的N-S流程圖:使用queue類創(chuàng)建實例a,b向隊列實例a,b中分別插入:10,20和20,19從隊列實例a,b中分別順序取出數(shù)據(jù),并顯示第50頁,共143頁,2023年,2月20日,星期四3.4.1參數(shù)化的構(gòu)造函數(shù)
與其他成員函數(shù)一樣,構(gòu)造函數(shù)也可以有參數(shù)。通過這些參數(shù)為類對象的數(shù)據(jù)成員傳遞初值。例如: classpoint { intx,y; public:
point(intvx,intvy); //聲明帶參數(shù)的構(gòu)造函數(shù) voidoffset(intax,intay); };第51頁,共143頁,2023年,2月20日,星期四
point::point(intvx,intvy) { x=vx; //用傳遞來的實參對x,y賦初值
y=vy; } main() {
pointp(10,20); //定義對象,并傳遞初值
//… }注意,不要將使用參數(shù)創(chuàng)建類對象的代碼寫成:
pointp=point(10,20);第52頁,共143頁,2023年,2月20日,星期四3.4.2構(gòu)造函數(shù)的重載 在一個類中允許定義多個參數(shù)不同構(gòu)造函數(shù),即構(gòu)造函數(shù)重載。這樣就為在不同情況下創(chuàng)建對象的特定初始化需要提供了實現(xiàn)手段。也就是說,在類對象定義時,編譯器可以依據(jù)創(chuàng)建對象所需要的參數(shù)差異確定調(diào)用構(gòu)造函數(shù)的哪一個版本來創(chuàng)建類對象。例3-1-1
定義一個有兩個構(gòu)造函數(shù)的類,并使用不同構(gòu) 造函數(shù)定義對象。第53頁,共143頁,2023年,2月20日,星期四3.4.3使用缺省參數(shù)值的構(gòu)造函數(shù)與其他函數(shù)一樣,構(gòu)造函數(shù)的參數(shù)也可以具有缺省值,表示類對象的某些屬性在大多數(shù)情況下是預(yù)先可以確定的缺省狀態(tài),例如計數(shù)器的初值一般為“0”、戰(zhàn)士的性別多數(shù)為“男”、大學(xué)教師的學(xué)位一般為“碩士”等。構(gòu)造函數(shù)的缺省參數(shù)值的定義和使用規(guī)則與其他帶缺省參數(shù)值的函數(shù)相同。例3-1-2
描述了如何聲明,定義和使用帶有缺省參數(shù)值 的類構(gòu)造函數(shù)。第54頁,共143頁,2023年,2月20日,星期四歸納構(gòu)造函數(shù)使用缺省參數(shù)值的編程要點是:⑴指定缺省參數(shù)值只能在構(gòu)造函數(shù)的聲明中,而不能 出現(xiàn)在構(gòu)造函數(shù)定義的首部。構(gòu)造函數(shù)定義在類定 義體中的情況除外。⑵函數(shù)聲明中的參數(shù)可以省略參數(shù)名,此時指定缺省參數(shù)值的格式為:類型名=缺省值。例如:
Box(int=10,int=10,int=10);⑶如果構(gòu)造函數(shù)的全部參數(shù)都指定了缺省值,應(yīng)該避 免再定義一個無參數(shù)的構(gòu)造函數(shù)。因為在定義構(gòu)造函數(shù)時,編譯器會認(rèn)為可能是重復(fù)定義。例如:
Box();
Box(int=10,int=10,int=10);第55頁,共143頁,2023年,2月20日,星期四 更重要的是定義類對象時,遇到如下情況:
Boxbox1;
編譯器無法確定調(diào)用哪一個構(gòu)造函數(shù)版本來創(chuàng)建類 對象。⑷如果構(gòu)造函數(shù)的全部參數(shù)都指定了缺省值,就容易 在重載構(gòu)造函數(shù)時造成二義性。例如:
Box(int=10,int=10,int=10);
Box();
Box(int,int);
因此,應(yīng)避免全部參數(shù)都指定了缺省值。例如:
Box();
Box(int,int=10,int=10);
Box(int,int);第56頁,共143頁,2023年,2月20日,星期四3.4.4用參數(shù)初始化表對數(shù)據(jù)成員初始化所謂參數(shù)初始化是指系統(tǒng)在為類對象的各個數(shù)據(jù)成員分配內(nèi)存空間的同時能按照用戶通過參數(shù)指定的值為數(shù)據(jù)成員賦值,而不是在各個數(shù)據(jù)成員的內(nèi)存空間分配完成后,再對它們進(jìn)行賦值。這就需要通過一種語法格式,即參數(shù)初始化表,使編譯器能按照上述要求實現(xiàn)對類對象的各個數(shù)據(jù)成員的初始化。構(gòu)造函數(shù)參數(shù)初始化表的一般形式為:
:[基類初始化列表],[屬性初始化列表]第57頁,共143頁,2023年,2月20日,星期四其中基類初始化列表只有在派生類的構(gòu)造函數(shù)初始化表才會存在,這一部分將在第五章中介紹。屬性初始化列表由若干個屬性初始化項組成,項間用“,”隔開:
屬性初始化項1,
屬性初始化項2,…
屬性初始化項n每個屬性初始化項的一般格式為:
屬性名(參數(shù)列表)不難看出,屬性初始化項的含義是調(diào)用相應(yīng)的屬性類的具有參數(shù)的構(gòu)造函數(shù)用于屬性對象的創(chuàng)建和賦初值操作。第58頁,共143頁,2023年,2月20日,星期四 這從另一個角度告訴我們,一個構(gòu)造函數(shù)的定義中沒有出現(xiàn)初始化表意味著使用了隱含的初始化表,該表的功能是分別調(diào)用了相應(yīng)類的無參數(shù)(或有缺省參數(shù)值)構(gòu)造函數(shù)完成類對象的基類部分和各個屬性對象創(chuàng)建和賦初值。例3-1-3
是將例3-1-1中的類構(gòu)造函數(shù)改寫為使用參數(shù)初 始化表實現(xiàn)類對象各數(shù)據(jù)成員的初始化。 雖然兩個實例中對類對象的數(shù)據(jù)成員初始化的結(jié)果是完全相同的,但兩種初始化方法對數(shù)據(jù)成員的賦值的時間和方法是完全不同的。第59頁,共143頁,2023年,2月20日,星期四 在構(gòu)造函數(shù)定義中使用初始化表另一個非常重要的原因就是對于類對象的常數(shù)據(jù)成員、引用數(shù)據(jù)成員的初始化就必須在創(chuàng)建的同時進(jìn)行賦值操作,而不能在函數(shù)體中進(jìn)行賦值。例如:
classA{ public:
A(inti);
constint&ref; //常引用數(shù)據(jù)成員
private:
constinta; //常數(shù)據(jù)成員
};
A::A(inti):a(i),ref(a){}第60頁,共143頁,2023年,2月20日,星期四3.4.5拷貝構(gòu)造函數(shù)
拷貝構(gòu)造函數(shù)是一個特定的構(gòu)造函數(shù)。該構(gòu)造函數(shù)與其他構(gòu)造函數(shù)在形式上的差別僅在于函數(shù)的參數(shù)必須是同類型對象的常引用。拷貝構(gòu)造函數(shù)的原型格式如下:
類型名(const
類型名&
引用名);
拷貝構(gòu)造函數(shù)的功能是創(chuàng)建一個新對象,并將參數(shù)所引用對象的各個數(shù)據(jù)成員值復(fù)制到新對象的對應(yīng)的數(shù)據(jù)成員域中。第61頁,共143頁,2023年,2月20日,星期四系統(tǒng)會為每個類缺省定義一個拷貝構(gòu)造函數(shù),也允許用戶定義一個拷貝構(gòu)造函數(shù),用以取代缺省的拷貝構(gòu)造函數(shù)。一般情況下,使用系統(tǒng)定義的缺省拷貝構(gòu)造函數(shù)就可以滿足類對象的復(fù)制操作,但在有些情況下,用戶必須定義自己的拷貝構(gòu)造函數(shù)。例如:
classstring
{ intlength; char*str; //指針數(shù)據(jù)成員
public:
string(intlen); … };第62頁,共143頁,2023年,2月20日,星期四 string::string(intlen) { length=len;
str=newchar[len+1];
//指針數(shù)據(jù)成員指向動態(tài)分配的內(nèi)存空間
} main() { strings1(10); //創(chuàng)建一個string對象s1 strings2(s1); //復(fù)制s1到新string對象s2 … }第63頁,共143頁,2023年,2月20日,星期四在這種情況下,s1
和s2
的指針數(shù)據(jù)成員str
指向了同一內(nèi)存空間,使得通過str對該內(nèi)存空間的任何操作都不能保持應(yīng)有的獨立性。更嚴(yán)重的是當(dāng)s1和s2
之中有一個被撤消時,在堆中分配的內(nèi)存空間被撤消回收,使得另一個string
對象的指針數(shù)據(jù)成員str
成為無效指針,任何通過該指針的操作均為非法操作,會導(dǎo)致嚴(yán)重的運(yùn)行錯誤。造成這一問題的原因是系統(tǒng)提供的缺省拷貝構(gòu)造函數(shù)不能復(fù)制string對象。因此,在這種情況下必須定義自己的拷貝構(gòu)造函數(shù):lengthstrs1lengthstrs2第64頁,共143頁,2023年,2月20日,星期四
string::string(conststring&s) { length=s.length; str=
newchar[length+1]; strcpy(str,s.str); }
Java
沒有C++
那種含義的指針,也沒有拷貝構(gòu)造函數(shù)。同時,對象的撤消是由垃圾收集器完成的,因此也不會產(chǎn)生像C++
中那樣的問題。當(dāng)然在Java
中也可以用賦值運(yùn)算符“=”來進(jìn)行對象賦值,但是,這并不意味著一個簡單賦值操作所具有的直覺含義。例如:第65頁,共143頁,2023年,2月20日,星期四
classUser { publicstringname; … } classTest { publicstaticvoidmain(String[]args){
Useru1=newUser(“ariel”,112); System.out.println();
//ariel
Useru2=u1; =“muriel”; System.out.println();
//muriel } }第66頁,共143頁,2023年,2月20日,星期四顯然,這里的u2=u1
只是對對象引用的復(fù)制。由于u1
和u2
引用同一個User
對象,所以才會導(dǎo)致修改
實際上等價于對
的修改(這與C++
中兩個指針指向同一個對象的情況類似)。如果要完成直覺意義上的復(fù)制,就必須通過實現(xiàn)User
類的克隆接口Cloneable后,調(diào)用逐字節(jié)復(fù)制的克隆函數(shù)
clone()
完成。例如:第67頁,共143頁,2023年,2月20日,星期四
classUserimplementsCloneable
{ publicstringname; … } classTest { publicstaticvoidmain(String[]args){
Useru1=newUser(“ariel”,112); System.out.println();
//ariel
Useru2=(User)u1.clone(); =“muriel”;
System.out.println();
//ariel
} }第68頁,共143頁,2023年,2月20日,星期四3.5析構(gòu)函數(shù)
對象撤消時,也需要一種操作,使被撤消的對象從程序的數(shù)據(jù)區(qū)中合法消失。實現(xiàn)這一操作的成員函數(shù)稱為析構(gòu)函數(shù),該函數(shù)要完成的操作包括:⑴回收被撤消對象數(shù)據(jù)成員所占用的內(nèi)存;⑵根據(jù)需要完成回收被撤消對象數(shù)據(jù)成員所占內(nèi)存之 前的必要操作。 析構(gòu)函數(shù)也是類必須擁有的特殊成員函數(shù),該函數(shù)從定義形式到使用場合和方法上都與構(gòu)造函數(shù)相似,主要特點表現(xiàn)在以下幾個方面:第69頁,共143頁,2023年,2月20日,星期四⑴析構(gòu)函數(shù)名必須是類名加字符“~”前綴,否則編譯器將會把它當(dāng)作一般成員函數(shù)對待。例如:
classStudent { public:
~Student(); … };
Student::~Student() { … }第70頁,共143頁,2023年,2月20日,星期四⑵析構(gòu)函數(shù)沒有返回值,因此,聲明和定義析構(gòu)函數(shù) 時都不能說明它的返回類型;⑶系統(tǒng)總會為類提供一個隱含的缺省析構(gòu)函數(shù)。該析 構(gòu)函數(shù)實際上是一個空定義體函數(shù),因此只能撤消 回收對象所占用的空間。 如果在對象被撤消之前無須做必要的預(yù)處理操作, 則可以放心使用缺省析構(gòu)函數(shù)。但在有些情況下, 則必須定義自己析構(gòu)函數(shù)。析構(gòu)函數(shù)一旦顯式定義,缺省析構(gòu)函數(shù)將被覆蓋。例如:第71頁,共143頁,2023年,2月20日,星期四classstring
{ intlength;
char*contents;public: string(char*s); //聲明構(gòu)造函數(shù) ~string(); //聲明析構(gòu)函數(shù)};類string的對象在撤消之前需要先檢查指針類型屬性contents
是否指向有效的內(nèi)存空間,如果是,則應(yīng)回收所占用的內(nèi)存空間。因此,必須重新定義析構(gòu)函數(shù),取代系統(tǒng)隱含定義的缺省析構(gòu)函數(shù)。string類的構(gòu)造函數(shù)和析構(gòu)函數(shù)的操作可以按如下定義:第72頁,共143頁,2023年,2月20日,星期四
string::string(char*s) //定義構(gòu)造函數(shù)
{ if(s) {length=strlen(s);
contents=newchar[length+1]; //分配存儲
strcpy(contents,s); //字串賦值
} else {length=0;
contents=0; //設(shè)置指針數(shù)據(jù)成員為空
}}第73頁,共143頁,2023年,2月20日,星期四
string::~string() //定義析構(gòu)函數(shù)
{ if(contents)delete[]contents; //釋放contents指向的內(nèi)存空間
}⑷在程序運(yùn)行過程中,類對象是在退出其作用域時才 被析構(gòu)的。也就是說,此時類對象的析構(gòu)函數(shù)被調(diào) 用。⑸析構(gòu)函數(shù)在類對象撤消時由系統(tǒng)自動執(zhí)行,不需要 用戶調(diào)用,也不能由用戶直接調(diào)用。第74頁,共143頁,2023年,2月20日,星期四⑹不能為析構(gòu)函數(shù)定義函數(shù)指針,也不能獲取析構(gòu)函 數(shù)的調(diào)用地址。⑺基類的析構(gòu)函數(shù)不能被派生類繼承。⑻析構(gòu)函數(shù)可以聲明為虛函數(shù),并且提倡聲明為虛函 數(shù)(詳細(xì)原因在第六章運(yùn)行多態(tài)性中講述)。返回第75頁,共143頁,2023年,2月20日,星期四3.6對象的動態(tài)創(chuàng)建和釋放與預(yù)定義類型一樣,自定義類型也可以使用運(yùn)算符new
動態(tài)創(chuàng)建對象和使用運(yùn)算符delete
撤消類對象。例3-2
描述了動態(tài)創(chuàng)建、撤消和使用point
類對象。注意:1使用無參數(shù)或具有缺省參數(shù)值的構(gòu)造函數(shù)動態(tài)創(chuàng)建 類對象(即創(chuàng)建對象時不傳遞初始值)的格式為:
new
類型名;
例如,point*p=newpoint;
而不應(yīng)寫成:
new
類型名();
例如,point*p=newpoint();
上述格式與動態(tài)創(chuàng)建系統(tǒng)預(yù)定義類型變量的格式完 全一致(預(yù)定義類型無缺省初始值)。例如:
int*p=newint;第76頁,共143頁,2023年,2月20日,星期四2使用有參數(shù)的構(gòu)造函數(shù)動態(tài)創(chuàng)建類對象(即創(chuàng)建對 象時傳遞初始值)的格式為:
new
類型名(初始值…);
例如,point*p=newpoint(10,20);
上述格式與動態(tài)創(chuàng)建系統(tǒng)預(yù)定義類型變量并傳遞初 始值的格式完全一致。例如:
int*p=newint(10);第77頁,共143頁,2023年,2月20日,星期四例3-3
通過構(gòu)造函數(shù)對對象數(shù)組進(jìn)行初始化。對象數(shù)組初始化的方法一般有兩種:1使用缺省構(gòu)造函數(shù)創(chuàng)建對象數(shù)組后,調(diào)用一個專門 用于初始化的成員函數(shù)對數(shù)組中的每個對象分別進(jìn) 行初始化。該方法雖然必須分兩步完成對象數(shù)組的 創(chuàng)建和初始化,但可以將對象數(shù)組中的元素初始化 為任意值。2定義一個帶缺省值參數(shù)的構(gòu)造函數(shù)。使得在創(chuàng)建對 象數(shù)組的同時,由構(gòu)造函數(shù)的缺省參數(shù)值完成數(shù)組 中的每個對象對象的初始化。使用該方法創(chuàng)建和初 始化對象數(shù)組簡單、方便,但只能將對象數(shù)組中的 每個元素初始化為固定的缺省值。 返回第78頁,共143頁,2023年,2月20日,星期四3.7對象的賦值與復(fù)制1對象的賦值 對象的賦值只能發(fā)生在同類型對象之間的,這與系統(tǒng)預(yù)定義類型變量的賦值是一致的。對象賦值的一般格式為:
對象名1
=
對象名2;
賦值操作是由賦值運(yùn)算符“=”完成的,該運(yùn)算符(函 數(shù))的功能是將對象名2
所指示對象的各個數(shù)據(jù)成員 值依次傳遞給對象名1
所指示對象的各個數(shù)據(jù)成員, 使對象1
與對象2
完全相同。第79頁,共143頁,2023年,2月20日,星期四 系統(tǒng)會為每一個自定義類型自動添加一個隱含的缺 省賦值運(yùn)算符,因此一般的自定義類型不必顯式定 義賦值運(yùn)算符。但如果類定義中包含有指針類屬性 (3.4.5
拷貝構(gòu)造函數(shù)中已經(jīng)討論這種情況),則必 須顯式定義賦值運(yùn)算符用于取代隱含的缺省賦值運(yùn) 算符。如何定義賦值運(yùn)算符將在第五章中討論。
例3-2-1
描述了使用缺省賦值運(yùn)算符完成對象的賦值 操作。第80頁,共143頁,2023年,2月20日,星期四2對象的復(fù)制 對象的復(fù)制是指按照一個已經(jīng)存在的對象創(chuàng)建一個與該對象完全相同的新對象。顯然對象的復(fù)制操作是由類的拷貝構(gòu)造函數(shù)完成的,復(fù)制一個已有對象的一般形式為:
類型名
對象2(對象1);
例如,Boxbox2(box1);
在C++中上述復(fù)制也適用于預(yù)定義類型,即每個預(yù) 定義類型都有一個隱含的拷貝構(gòu)造函數(shù)。 例如,int
a(10),b(a);第81頁,共143頁,2023年,2月20日,星期四
C++
還提供了另外一種方便用戶的復(fù)制表達(dá)式,即用賦值運(yùn)算符代替括號調(diào)用類的拷貝構(gòu)造函數(shù):
類型名
對象2=對象1;
例如,Box
box2=box1;
顯然,這種復(fù)制形式與預(yù)定義類型變量賦值定義形式是一致的。 例如,inta=10,b=a;
程序中需要對象復(fù)制操作的情況有三種: ⑴用戶需要定義與已有對象完全一致的新對象。 ⑵函數(shù)調(diào)用時,系統(tǒng)需要復(fù)制被傳遞的實參對象。 ⑶函數(shù)返回時,系統(tǒng)會復(fù)制被返回的操作結(jié)果。返回第82頁,共143頁,2023年,2月20日,星期四3.8與對象有關(guān)的指針 指針變量可以用于指向任何預(yù)定義類型變量,當(dāng)然也可以指向自定義類型對象。不僅如此,指針變量還可以指向?qū)ο蟮念惓蓡T。1指向?qū)ο蟮闹羔?定義指向?qū)ο蟮闹羔樧兞康囊话阈问剑?/p>
類型名
*對象指針名; 例如,Box*pt;
顯然,這與定義預(yù)定義類型指針的形式是完全一致的。同樣,指針的使用形式也是相同的。例如:
pt=newBox;
pt->volume(); //與(*pt).volume();等價第83頁,共143頁,2023年,2月20日,星期四2指向?qū)ο蟪蓡T的指針 對象的成員有數(shù)據(jù)成員和成員函數(shù)兩種,因此指向 對象成員的指針也有兩種。 ⑴指向數(shù)據(jù)成員的指針 定義指向數(shù)據(jù)成員的指針變量的一般形式:
類型名
*指針變量名;
顯然,這與定義預(yù)定義類型指針是完全一致的。 同樣,指針的使用形式也是相同的。例如:
classTime{ public: inthour,minute,sec; voidshow(); };第84頁,共143頁,2023年,2月20日,星期四
voidTime::show() { cout<<hour<<“:”<<minute<<“:”<<sec<<endl; }
Timetime;
int*p;
p=&time.hour;
而不能按如下表達(dá)式為指針賦值:
p=&Time::hour;
注意,指針只能指向公有數(shù)據(jù)成員。第85頁,共143頁,2023年,2月20日,星期四 ⑵指向?qū)ο蟪蓡T函數(shù)的指針 定義指向成員函數(shù)的指針變量的一般表達(dá)式:
類型名
(類名::*函數(shù)指針名)(參數(shù)列表);
例如,void(Time::*pf)();
為函數(shù)指針賦值,指向成員函數(shù)的一般表達(dá)式:
函數(shù)指針名
=&類名::成員函數(shù)名;
例如,pf=&Time::Show;
通過成員函數(shù)指針調(diào)用成員函數(shù)的一般表達(dá)式:
(對象名.*成員函數(shù)指針)(實參列表);
例如,Timetime; (time.*pf)();
或者(對象指針->*成員函數(shù)指針)(實參列表);
例如,Time*pt=&time; (pt->*pf)();第86頁,共143頁,2023年,2月20日,星期四3this
指針
this指針是類成員函數(shù)擁有的一個隱含指針,它指向該成員函數(shù)被調(diào)用時,操作所施加的類對象地址,實現(xiàn)不同對象的相同行為的表現(xiàn)差異。如圖所示:成員函數(shù){………}this數(shù)據(jù)成員…類對象1數(shù)據(jù)成員…類對象2數(shù)據(jù)成員…類對象n第87頁,共143頁,2023年,2月20日,星期四 成員函數(shù)通過this
指針可以訪問所指對象中的類成 員,數(shù)據(jù)成員的訪問格式可寫為:
this->數(shù)據(jù)成員名 成員函數(shù)的訪問格式可寫成:
this->成員函數(shù)名(參數(shù)列表)
例如:第88頁,共143頁,2023年,2月20日,星期四
classexth{ inti; public: voidload(intval) {this->i=val;} //與i=val;等價
intget() {returnthis->i;} //與returni;等價
}; voidmain() {
exthobj; obj.load(100); cout<<obj.get(); }第89頁,共143頁,2023年,2月20日,星期四 顯然,一般情況下,上述書寫格式是繁瑣的,并且 是不必要的。但在成員函數(shù)的形參名與某個數(shù)據(jù)成 員名相同時,使用this
指針是區(qū)分這兩個數(shù)據(jù)變量 的好方法。例如:
point::point(intx,inty) {
this->x=x; this->y=y; }
當(dāng)然,這種問題也可以用如下方法解決:
point::point(intx,inty) {
point::x=x; point::y=y; }第90頁,共143頁,2023年,2月20日,星期四
為了進(jìn)一步了解成員函數(shù)被調(diào)用時this
指針是如何在“幕后”工作的,我們再來分析一段程序:
classabc{private: chara; intb;public: voidinit(charma,intmb){a=ma;b=mb;}};intmain(){
abcob; ob.init(‘x’,12);
return0;}第91頁,共143頁,2023年,2月20日,星期四分析:在main()
中,abc
類成員函數(shù)init通過類對象
ob
被調(diào)用的:
ob.init(‘x’,12);
C++
是如何使this
指針指向?qū)ο髈b
的呢?編譯器在編譯過程中對上述語句經(jīng)過如下的轉(zhuǎn)換:①
首先將成員運(yùn)算符“.”之前的對象ob
的地址作為 實參傳給函數(shù)init,則語句變?yōu)椋?/p>
init(&ob,‘x’,12);
第92頁,共143頁,2023年,2月20日,星期四 ②
雖然成員函數(shù)init
的原型中沒有相應(yīng)的形參,但 程序編譯時,編譯器將該函數(shù)的原型和定義隱含 轉(zhuǎn)換成以下形式:
init(abc*this,charma,intmb)
{this->a=ma;this->b=mb;}
顯然,該成員函數(shù)被調(diào)用時,第一個形參應(yīng)是調(diào) 用該成員函數(shù)的對象地址,因此當(dāng)對象
ob
調(diào)用 成員函數(shù)init
時,形參this
=
&ob。確保成員函數(shù)
init
的操作施加于對象ob。 返回第93頁,共143頁,2023年,2月20日,星期四3.9靜態(tài)成員 前面我們所涉及到的類成員都是由每個對象獨享,即每個對象擁有獨立的數(shù)據(jù)成員空間,成員函數(shù)的調(diào)用都依賴某個特定對象,成員函數(shù)的操作結(jié)果取決于該對象的狀態(tài),并僅服務(wù)于該對象。這些由對象獨享的成員被稱為非靜態(tài)成員,是類的主要組成成員。此外,類中還需要包括一些由類的所有對象共享的成員,即類的所有對象共同擁有的數(shù)據(jù)成員和調(diào)用不依賴于某個特定對象,操作結(jié)果為類的所有對象服務(wù)的成員函數(shù)。這些由類的所有對象共享的成員被稱為靜態(tài)成員。在類中聲明靜態(tài)成員的方法就是用關(guān)鍵字
static聲明數(shù)據(jù)成員和成員函數(shù);第94頁,共143頁,2023年,2月20日,星期四3.9.1靜態(tài)數(shù)據(jù)成員 與非靜態(tài)數(shù)據(jù)成員不同,靜態(tài)數(shù)據(jù)成員屬于類的所有對象,因此無論創(chuàng)建多少個該類的對象,靜態(tài)數(shù)據(jù)成員在內(nèi)存中都只有一個。也就是說,靜態(tài)數(shù)據(jù)成員所占的內(nèi)存空間不在任何一個類對象空間中,因此類的構(gòu)造函數(shù)不會為靜態(tài)數(shù)據(jù)成員分配空間的初始化。第95頁,共143頁,2023年,2月20日,星期四例3-4
中Student
類中的數(shù)據(jù)成員count
用于存放學(xué)生總 數(shù)。顯然,不管有多少學(xué)生,學(xué)生總數(shù)只有一 個。也就是說,學(xué)生總數(shù)
count
屬于
Student
類的 所有對象,因此應(yīng)聲明為靜態(tài)數(shù)據(jù)成員。而數(shù)據(jù) 成員StudentNo
用于存放每個學(xué)生的學(xué)號,因此 該數(shù)據(jù)成員必須是非靜態(tài)數(shù)據(jù)成員。下圖描述了 這兩種數(shù)據(jù)成員與Student
類對象的關(guān)系:Student1:StudentNoStudent2:StudentNoStudent4:StudentNocount第96頁,共143頁,2023年,2月20日,星期四使用靜態(tài)數(shù)據(jù)成員的四點說明:⑴靜態(tài)數(shù)據(jù)成員屬于類的所有對象,而不像非靜態(tài)數(shù)據(jù)成員那樣屬于某一個對象,是整個類的屬性。因此訪問靜態(tài)數(shù)據(jù)成員的形式通常采用:
類名::數(shù)據(jù)成員名。
例如,Student::count
C++
編譯器也允許采用通過對象名訪問靜態(tài)數(shù)據(jù)成員的形式:
對象名.數(shù)據(jù)成員名。
例如,Student1.count
后一種形式等價與Student1.Student::count,顯然對象名是多余的。第97頁,共143頁,2023年,2月20日,星期四⑵類中聲明的靜態(tài)數(shù)據(jù)成員既不能隨著對象創(chuàng)建被定義和初始化,也不能在類定義中進(jìn)行定義和初始化,而必須在類定義外,并且在該類的任何對象創(chuàng)建之前,提供初始化定義。一般形式為:
數(shù)據(jù)類型
類名::靜態(tài)數(shù)據(jù)成員名
=初值;
例如本例中為Student::count
提供初始化定義語句:
intStudent::count=0;
如果只定義不賦初值,則被缺省設(shè)置為零。注意,對于類的常靜態(tài)(staticconst)數(shù)據(jù)成員,也需要和一般靜態(tài)數(shù)據(jù)成員一樣在類外初始化。第98頁,共143頁,2023年,2月20日,星期四⑶靜態(tài)數(shù)據(jù)成員與其他靜態(tài)變量一樣,是在編譯時創(chuàng)建并被初始化的。它在該類的任何對象被
溫馨提示
- 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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 專題5.3 平面向量的數(shù)量積(原卷版)-2024年高考數(shù)學(xué)一輪復(fù)習(xí)精講精練寶典(新高考專用)
- 幼兒游戲教學(xué)案例
- 人教版(2024)七年級英語下冊Unit 6 學(xué)情調(diào)研測試卷(含答案)
- 路基拼寬施工方案
- 隧道風(fēng)機(jī)房施工方案
- 2025年新高考地理全真模擬試卷4(含答案解析)
- 2025年高考地理二輪復(fù)習(xí):綜合題答題技巧(含練習(xí)題及答案)
- 幕墻防火防雷施工方案
- Unit 6 reading2 教學(xué)設(shè)計 2024-2025學(xué)年譯林版(2024)七年級英語上冊
- 小學(xué)課本劇一年級《小白兔和小灰兔》-劇本
- 化工設(shè)備巡檢培訓(xùn)
- 2024兒童腎病綜合征指南診斷與治療(附表)
- 卵巢癌的健康宣教
- DB45T 2758-2023 小型水利工程施工質(zhì)量管理與評定規(guī)范
- 中建測評二測題庫
- 店長管理員工培訓(xùn)
- DB11∕T 3010-2018 冷鏈物流冷庫技術(shù)規(guī)范
- 愛普生L4168說明書
- 現(xiàn)代家政導(dǎo)論-課件 2.2家庭制度認(rèn)知
- 題型專訓(xùn):平方差公式和完全平方公式
- 內(nèi)容審核機(jī)制
評論
0/150
提交評論