




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1
第二部分面向?qū)ο蟮某绦蛟O(shè)計(jì)
本章導(dǎo)讀
掌握類與對(duì)象的概念,類與對(duì)象的定義方法及二者間的區(qū)別。掌握類的成員函數(shù)的定義方法、保存方法及調(diào)用方法。掌握類中成員的訪問機(jī)制和方法。了解對(duì)象的作用域和生存期。理解并掌握構(gòu)造函數(shù)、析構(gòu)函數(shù)、拷貝構(gòu)造函數(shù)、默認(rèn)構(gòu)造函數(shù)和缺省參數(shù)的構(gòu)造函數(shù)的含義、定義方法以及在對(duì)象的構(gòu)造和撤消中的作用。理解并掌握當(dāng)一個(gè)類的對(duì)象作為另一個(gè)類的數(shù)據(jù)成員時(shí),利用初始化表調(diào)用構(gòu)造函數(shù)的方法、構(gòu)造函數(shù)的執(zhí)行順序。2本章導(dǎo)讀
理解繼承的概念和意義,理解單一繼承、多重繼承。理解并掌握派生類構(gòu)造函數(shù)的編寫要求,以及派生類對(duì)象的構(gòu)造過程和機(jī)理。掌握虛函數(shù)和多態(tài)性的概念,掌握虛函數(shù)的定義方法、調(diào)用方法及其在實(shí)現(xiàn)多態(tài)性方面所起到的作用。了解純虛函數(shù)與抽象基類的概念。了解類的靜態(tài)成員(靜態(tài)數(shù)據(jù)成員和靜態(tài)成員函數(shù))的概念、定義方法及其作用。了解友元函數(shù)與友元類的概念、定義方法及其作用。了解運(yùn)算符重載及在程序中實(shí)現(xiàn)運(yùn)算符重載的方法。了解模板的概念,在程序中如何定義類模板和函數(shù)模板。33.1類與對(duì)象的定義
類和對(duì)象是面向?qū)ο蟪绦蛟O(shè)計(jì)(OOP)的兩個(gè)最基本概念。所謂對(duì)象就是客觀事物在計(jì)算機(jī)中的抽象描述;類是對(duì)具有相似屬性和行為的一組對(duì)象的統(tǒng)一描述。3.1.1類的定義
C++的類是在結(jié)構(gòu)體的基礎(chǔ)上擴(kuò)充而來的。類是把各種不同類型的數(shù)據(jù)(稱為數(shù)據(jù)成員)和對(duì)數(shù)據(jù)的操作(成員函數(shù))組織在一起而形成的用戶自定義的數(shù)據(jù)類型。
C++中,類定義包括類說明和類實(shí)現(xiàn)兩大部分。說明部分提供了對(duì)該類所有數(shù)據(jù)成員和成員函數(shù)的描述,而實(shí)現(xiàn)部分提供了所有成員函數(shù)的實(shí)現(xiàn)代碼。//結(jié)構(gòu)體中的函數(shù)成員.cpp43.1類與對(duì)象的定義
類定義的一般形式為:class類名{private:
數(shù)據(jù)成員或成員函數(shù)
protected:
數(shù)據(jù)成員或成員函數(shù)
public:
數(shù)據(jù)成員或成員函數(shù)};
<各成員函數(shù)的實(shí)現(xiàn)代碼>53.1類與對(duì)象的定義說明:1.class是定義類的關(guān)鍵字,類名由用戶自己定名,必須是C++的有效標(biāo)識(shí)符,但一般首字母大寫。2.大括號(hào)的部分是類的成員(數(shù)據(jù)成員和函數(shù)成員),它們分成三部分,分別由private、public、proctected三個(gè)關(guān)鍵字后跟冒號(hào)來指定。這三部分可以任何順序出現(xiàn),且在一個(gè)類的定義中,這三部分并非必須同時(shí)出現(xiàn)。(1)如果數(shù)據(jù)成員或成員函數(shù)在類的private部分,那么在類之外是不能存取的,只有類中的成員函數(shù)才能存取private的數(shù)據(jù)成員和成員函數(shù)。(2)在一個(gè)類的public部分說明的數(shù)據(jù)成員或成員函數(shù)可被程序中的任何函數(shù)或語(yǔ)句存取,public成員多為成員函數(shù),用來提供一個(gè)與外界的接口,外界只有通過這個(gè)接口才可以實(shí)現(xiàn)對(duì)private成員的存取。63.1類與對(duì)象的定義(3)在類的protected部分說明的數(shù)據(jù)成員和成員函數(shù)是不能在類之外存取的,只有類的成員函數(shù)及其子類(派生類)可以存取protected的成員。(4)當(dāng)定義類時(shí),當(dāng)未指明成員是哪部分時(shí),默認(rèn)是屬于private成員,但一般不要采用默認(rèn)形式。如:下例中定義描述圖書的類定義
classRecord//圖書類
{private://private成員
charbookname[20];//數(shù)據(jù)成員bookname,
//用于表示圖書的名稱
intnumber;//數(shù)據(jù)成員number,表示圖書編號(hào)73.1類與對(duì)象的定義public: //public成員
voidregist(char*a,intb);//成員函數(shù)regist,用于給//各數(shù)據(jù)成員賦值
voidshow();//成員函數(shù)show,顯示各數(shù)據(jù)成員的值
};要特別注意,在類的定義中,類的說明部分的右邊大括號(hào)后面必須有一“;”.
根據(jù)類的定義,可看出:類是實(shí)現(xiàn)封裝的工具,所謂封裝就是將類的成員按使用或存取的方式分類,有條件地限制對(duì)類成員的使用,而封裝是通過public和private與成員函數(shù)實(shí)現(xiàn)的。private的成員構(gòu)成類的內(nèi)部狀態(tài),public的成員則構(gòu)成與外界通信的接口,通過public的成員函數(shù)來使用private的數(shù)據(jù)成員,從而在C++中實(shí)現(xiàn)了封裝。在類中不允許對(duì)所定義的數(shù)據(jù)成員進(jìn)行初始化;另外,類中的數(shù)據(jù)成員可以是另一個(gè)類的對(duì)象(稱之為內(nèi)部對(duì)象成員或子對(duì)象),這時(shí),如果另一個(gè)類的定義在后,需要提前說明。83.1類與對(duì)象的定義3.1.2成員函數(shù)的定義類中的成員函數(shù)可以在以下兩處定義:(1)將成員函數(shù)的定義直接寫在類中:如:對(duì)于前面定義的圖書類Record來說,其成員函數(shù)regist和show的定義可直接寫在類的定義體中。classRecord{private:charbookname[20];intnumber;93.1類與對(duì)象的定義public:voidregist(char*a,intb)//成員函數(shù)regist()的定義
{strcpy(bookname,a);//給數(shù)據(jù)成員bookname賦值
number=b;//給數(shù)據(jù)成員number賦值
}voidshow() //成員函數(shù)show()的定義
{cout<<”名稱:”<<bookname<<endl;cout<<”號(hào)碼:”<<number<<endl;}};103.1類與對(duì)象的定義
在類中直接定義成員函數(shù)的情況一般適合于成員函數(shù)規(guī)模較小的情況,也就是說它們一般為內(nèi)聯(lián)函數(shù),即使沒有明確用inline關(guān)鍵字。(2)在類的定義體中只寫出成員函數(shù)的原型說明,而成員函數(shù)的定義寫在類的定義之后,這種情況比較適合于成員函數(shù)體較大的情況,但這時(shí)要求在定義成員函數(shù)時(shí),在函數(shù)的名稱之前加上其所屬性類名及作用域運(yùn)算符“::”
。定義成員函數(shù)的一般類型為:返回值類型類名::成員函數(shù)名(參數(shù)說明)
{類體}113.1類與對(duì)象的定義
此處的::符號(hào)叫作用域運(yùn)算符,用它來指明哪個(gè)函數(shù)屬于哪個(gè)類或哪個(gè)數(shù)據(jù)屬于哪個(gè)類,所以使用類中成員的全名是:類名::成員名。而如果沒有類名,則為全局?jǐn)?shù)據(jù)或全局函數(shù)(非成員函數(shù)),也就是說類名是其成員名的一部分。如
classRecord{private:charbookname[20];intnumber;public:voidregist(char*a,intb);//成員函數(shù)regist的原型
voidshow();//成員函數(shù)show的原型
};//定義圖書類Record123.1類與對(duì)象的定義voidRecord::regist(char*a,intb)//regist()是類Record的
//成員函數(shù)
{strcpy(bookname,a);number=b;}voidRecord::show()//show()是類Record的成員函數(shù)
{cout<<”名稱:”<<bookname<<endl;cout<<”號(hào)碼:”<<number<<endl;}
此外,目前開發(fā)程序的通常將類的定義寫在一個(gè)頭文件(.h文件)中,成員函數(shù)的定義寫在一個(gè)程序文件(.cpp文件)中,這樣,就相當(dāng)于把類的定義(頭文件)看成是類的外部接口,類的成員函數(shù)的定義看成類的內(nèi)133.1類與對(duì)象的定義部實(shí)現(xiàn)。如:對(duì)上例可改成將類的定義體寫在myapp.h文件中,而成員函數(shù)的定義體寫在另外一個(gè)文件myapp.cpp中://myapp.h文件
classRecord{private:charbookname[20];intnumber;public:voidregist(char*a,intb);
voidshow();
};143.1類與對(duì)象的定義//myapp.cpp文件#include“iostream”usingnamespacestd;#include“myapp.h” //一定不要忘記嵌入該頭文件
voidrecord::regist(char*a,intb){strcpy(bookname,a);number=b;}voidrecord::show(){cout<<”名稱:”<<bookname<<endl;cout<<”號(hào)碼:”<<number<<endl;}153.1類與對(duì)象的定義3.1.3對(duì)象的定義對(duì)象是類的實(shí)例,定義對(duì)象的一般格式為:
類名變量名表;或類名對(duì)象名;如:上例中已定義了類Record,則:
Recordbook1,book2;//此處的book1,book2就是Record//類型,也就是類的兩個(gè)對(duì)象類是抽象的概念,而對(duì)象是具體的,類只是一種數(shù)據(jù)類型,而對(duì)象是屬于該類(數(shù)據(jù)類型)的一個(gè)變量,占用了各自的存儲(chǔ)單元,每個(gè)對(duì)象各自具有了該類的一套數(shù)據(jù)成員(靜態(tài)成員除外),而所有成員函數(shù)是所有對(duì)象共有的。每個(gè)對(duì)象的函數(shù)成員都通過指針指向同一個(gè)代碼空間。163.1類與對(duì)象的定義3.1.4訪問對(duì)象的成員訪問對(duì)象的成員包括讀寫對(duì)象的數(shù)據(jù)成員和調(diào)用它的成員函數(shù),其訪問格式是:對(duì)象名.成員名如上例中,對(duì)象的主函數(shù)如下:voidmain(void){Recordbook1,book2;//定義對(duì)象book1和book2//調(diào)用成員函數(shù)regist,給book1的兩個(gè)數(shù)據(jù)成員
//bookname和number賦值
book1.regist(“C++編程教程”,1001);//調(diào)用成員函數(shù)regist,給book2的兩個(gè)數(shù)據(jù)成員賦值
book2.regist(“C++語(yǔ)言參考”,1002);173.1類與對(duì)象的定義//調(diào)用成員函數(shù)show,顯示book1對(duì)象的數(shù)據(jù)成員
//bookname和number的值book1.show();//調(diào)用成員函數(shù)show,顯示book2對(duì)象的數(shù)據(jù)成員
//bookname和number的值book2.show();}如改為下面的代碼,則錯(cuò)誤:voidmain(void){Recordbook1,book2;//由于bookname和number是類Record的私有成員,在類外//不能直接使用183.1類與對(duì)象的定義strcpy(book1.bookname,“C++編程教程”);book1.number=1001;strcpy(book2.bookname,“C++語(yǔ)言參考”);book2.number=1002;book1.show();book2.show();}注意:1.對(duì)于類的私有成員,只能通過其成員函數(shù)來訪問,不能在類外對(duì)私有成員訪問。193.1類與對(duì)象的定義2.調(diào)用成員函數(shù)時(shí)要在函數(shù)名之前加上對(duì)象名和"."即可,即先指明對(duì)象,再指明成員。也可以采用指向?qū)ο蟮闹羔榿碓L問,但要在函數(shù)名前加上指針變量名和“->”。3.任何對(duì)對(duì)象私有數(shù)據(jù)的訪問都必須通過向?qū)ο蟀l(fā)送消息來實(shí)現(xiàn),而且所發(fā)送的消息還必須是該對(duì)象能夠識(shí)別和接受的。在C++中,消息發(fā)送正是通過公有成員函數(shù)的調(diào)用來實(shí)現(xiàn)的。由于類接口隱藏了對(duì)象的內(nèi)部細(xì)節(jié),用戶只能通過類接口訪問對(duì)象,因此,在類設(shè)計(jì)中必須提供足夠的公有接口以捕獲對(duì)象的全部行為,這正是類設(shè)計(jì)中的一個(gè)最基本的要求。4.
上例中,在對(duì)象調(diào)用book1.regist(“C++編程教程”,1001);時(shí),成員函數(shù)regist除了接受兩個(gè)實(shí)參外,還接203.1類與對(duì)象的定義受了一個(gè)對(duì)象book1的地址,這個(gè)地址被一個(gè)隱含的形參this指針?biāo)@取,它等同于執(zhí)行this=&book1,所以所有對(duì)數(shù)據(jù)成員的訪問都隱含地被加上前綴:this->,因此,在成員函數(shù)體regist中,執(zhí)行strcpy(bookname,a);number=b;就等價(jià)于strcpy(this->bookname,a);this->number=b;這樣,上例中的成員函數(shù)regist也可這樣定義:voidrecord::regist(char*a,intb){strcpy(this->bookname,a);this->number=b;}
通過以上手段就確保了不同對(duì)象調(diào)用成員函數(shù)時(shí)訪問的是不同對(duì)象的數(shù)據(jù),而它們之間沒有干擾。213.1類與對(duì)象的定義3.1.5對(duì)象賦值語(yǔ)句對(duì)于同一個(gè)類生成的兩個(gè)對(duì)象,可以進(jìn)行賦值,其功能是將一個(gè)對(duì)象的數(shù)據(jù)成員賦值到另一個(gè)對(duì)象中去,賦值語(yǔ)句的左右兩邊各是一個(gè)對(duì)象名:【例3-1】
對(duì)于類example的兩個(gè)對(duì)象obj1和obj2,讓obj2的成員數(shù)據(jù)的值等于obj1的成員數(shù)據(jù)的值(假定obj1的成員數(shù)據(jù)num已經(jīng)存有數(shù)據(jù)215)。
223.1類與對(duì)象的定義#include<iostream>usingnamespacestd;//定義類classexample{private://數(shù)據(jù)成員
intnum;public://函數(shù)成員說明
voidset(inti){num=i;}voiddisp(){cout<<"\nnum="<<num;}};233.1類與對(duì)象的定義//主程序voidmain(void){exampleobj1,obj2;obj1.set(215);obj1.disp();obj2=obj1;//對(duì)象賦值語(yǔ)句
obj2.disp();cout<<endl<<endl;}3.1.6對(duì)象的作用域與生存期對(duì)象是類的實(shí)例,它實(shí)質(zhì)就是某種數(shù)據(jù)類型的變量,在不同的位置以不同的方式定義對(duì)象時(shí),其作用域和生存期是不同的。243.1類與對(duì)象的定義如:classDesk//定義Desk類{public:intweight;inthigh;intwidth;intlength;};classStool//定義Stool類{public:intweight;inthigh;intwidth;intlength;};253.1類與對(duì)象的定義deskda; //定義全局對(duì)象Stoolsa;voidfn(){staticStoolss; //靜態(tài)局部對(duì)象
deskda; //定義局部對(duì)象
//……}1.局部對(duì)象(不包括局部靜態(tài)對(duì)象)其作用域是定義它的函數(shù)體,生存期從函數(shù)調(diào)用開始到函數(shù)調(diào)用結(jié)束,下一次再重新調(diào)用函數(shù)時(shí),再重新構(gòu)造對(duì)象。
構(gòu)造局部對(duì)象的次序(即分配存儲(chǔ)單元)是按它們?cè)诤瘮?shù)體中聲明的順序。263.1類與對(duì)象的定義2.靜態(tài)對(duì)象(局部靜態(tài)和全局靜態(tài))其作用域是定義它的函數(shù)體或程序文件,其生存期是整個(gè)程序。構(gòu)造靜態(tài)對(duì)象的次序是按它們?cè)诔绦蛑谐霈F(xiàn)的次序先后,并在整個(gè)程序運(yùn)行開始時(shí)(即在主函數(shù)運(yùn)行前)只構(gòu)造一次。3.全局對(duì)象全局對(duì)象的作用域是整個(gè)程序,生存期是整個(gè)程序的運(yùn)行時(shí)間。它也是在程序運(yùn)行前(即在主函數(shù)運(yùn)行前)只構(gòu)造一次。4.類中成員的構(gòu)造次序是以類中聲明成員的次序進(jìn)行。構(gòu)造函數(shù)和析構(gòu)函數(shù)是類的兩種特殊的成員函數(shù)。273.2構(gòu)造函數(shù)與析構(gòu)函數(shù)3.2.1構(gòu)造函數(shù)構(gòu)造函數(shù)(constructor)是與類名同名的特殊的成員函數(shù),當(dāng)定義該類的對(duì)象時(shí),構(gòu)造函數(shù)將被自動(dòng)調(diào)用以實(shí)現(xiàn)對(duì)該對(duì)象的初始化。構(gòu)造函數(shù)不能有返回值,因而不能指定包括void在內(nèi)的任何返回值類型。構(gòu)造函數(shù)的定義體可與其它成員函數(shù)成員一樣,放在類內(nèi)或類外都可。構(gòu)造函數(shù)的定義格式為:類名(形參說明){函數(shù)體}
構(gòu)造函數(shù)既可定義成有參函數(shù),也可義成無參函數(shù),要根據(jù)問題的需要來定。全局變量和靜態(tài)變量在定義時(shí),將自動(dòng)賦初值為0;局部變量在定義時(shí),其初始值不固定的。而當(dāng)對(duì)象被定義時(shí),由于對(duì)象的意義表達(dá)了現(xiàn)實(shí)世界的實(shí)283.2構(gòu)造函數(shù)與析構(gòu)函數(shù)體,所以一旦定義對(duì)象,就必須有一個(gè)有意義的初始值,在C++中,在定義對(duì)象的同時(shí),給該對(duì)象初始化的方法就是利用構(gòu)造函數(shù)。如:【例3-2】
類person包括4個(gè)數(shù)據(jù)成員,用來記錄人員信息。生成對(duì)象obj,并使用構(gòu)造函數(shù)為obj賦予初始值。#include<iostream>usingnamespacestd;classPerson //定義類{private: //類Person的數(shù)據(jù)成員
charname[10]; //姓名
intage; //年齡
intsalary; //薪金
chartel[8]; //電話293.2構(gòu)造函數(shù)與析構(gòu)函數(shù)public: //構(gòu)造函數(shù)PersonPerson(char*xname,intxage,intxsalary,char*xtel);voiddisp();};//函數(shù)Person的定義Person::Person(char*xname,intxage,intxsalary,char*xtel){strcpy(name,xname);//給各數(shù)據(jù)成員提供初值
age=xage;salary=xsalary;strcpy(tel,xtel);}303.2構(gòu)造函數(shù)與析構(gòu)函數(shù)//函數(shù)disp的定義voidPerson::disp(){cout<<endl;cout<<"姓名:"<<name<<endl;cout<<"年齡:"<<age<<endl;cout<<"工資:"<<salary<<endl;cout<<"電話:"<<tel<<endl<<endl;}//主函數(shù)voidmain(void){//生成對(duì)象obj并初始化
Personobj("張立三",25,850,"45672314");//顯示objobj.disp();}313.2構(gòu)造函數(shù)與析構(gòu)函數(shù)程序的執(zhí)行結(jié)果是:姓名:張立三年齡:25
工資:850
電話:45672314在主函數(shù)中的Personobj("張立三",25,850,"45672314");中完成了以下幾個(gè)功能:1.定義并生成了對(duì)象obj。2.在生成對(duì)象obj的同時(shí),自動(dòng)調(diào)用相應(yīng)類的構(gòu)造函數(shù)Person3.將初始值"張立三",25,850,"45672314"傳遞給構(gòu)造函數(shù)Person相應(yīng)的形參xname,xage,xsalary,xtel。4.執(zhí)行構(gòu)造函數(shù)體,將相應(yīng)的值賦給相應(yīng)的數(shù)據(jù)成員。323.2構(gòu)造函數(shù)與析構(gòu)函數(shù)3.2.2構(gòu)造函數(shù)的重載如果一個(gè)類中出現(xiàn)了兩個(gè)以上的同名的成員函數(shù)時(shí),稱為類的成員函數(shù)的重載。【例3-3】
類Rec定義兩個(gè)重載函數(shù),其中一個(gè)是無參函數(shù),另一個(gè)是有參函數(shù)。它們都是構(gòu)造函數(shù)。#include<iostream>usingnamespacestd;//定義類classRec{private:charbookname[30];intnumber;333.2構(gòu)造函數(shù)與析構(gòu)函數(shù)public:Rec();//第1個(gè)構(gòu)造函數(shù)說明
Rec(char*a,intb);//第2個(gè)構(gòu)造函數(shù)說明
voidshow();};Rec::Rec()//第1個(gè)構(gòu)造函數(shù)定義
{strcpy(bookname,“noname”);number=0;}Rec::Rec(char*a,intb)//第2個(gè)構(gòu)造函數(shù)定義
{strcpy(bookname,a);number=b;}343.2構(gòu)造函數(shù)與析構(gòu)函數(shù)voidRec::show()//show的函數(shù)定義{cout<<"booknameis:"<<bookname<<endl;cout<<"booknumberis:"<<number<<endl;}voidmain(void)//主程序
{Recmybook(“VisualC++6.0”,10020);//自動(dòng)調(diào)用構(gòu)造
//函數(shù)Rec(char*a,intb)mybook.show();Recyourbook;//自動(dòng)調(diào)用構(gòu)造函數(shù)Rec()yourbook.show();}353.2構(gòu)造函數(shù)與析構(gòu)函數(shù)程序的執(zhí)行結(jié)果是:booknameis:VisualC++6.0booknumberis:10020booknameis:nonamebooknumberis:0可見,當(dāng)出現(xiàn)構(gòu)造函數(shù)重載時(shí),其匹配方式同普通函數(shù)重載時(shí)的匹配方式。(例復(fù)數(shù)類的多構(gòu)造函數(shù).cpp)363.2構(gòu)造函數(shù)與析構(gòu)函數(shù)3.2.3默認(rèn)構(gòu)造函數(shù)與缺省構(gòu)造函數(shù)
C++規(guī)定,每個(gè)類必須有一個(gè)構(gòu)造函數(shù)。如果在類中沒有顯式定義構(gòu)造函數(shù)時(shí),則C++編譯系統(tǒng)在編譯時(shí)為該類提供一個(gè)默認(rèn)的構(gòu)造函數(shù),該默認(rèn)構(gòu)造函數(shù)是個(gè)無參函數(shù),它僅負(fù)責(zé)創(chuàng)建對(duì)象,而不做任何初始化工作。只要一個(gè)類定義了一個(gè)構(gòu)造函數(shù)(不一定是無參構(gòu)造函數(shù)),C++編譯系統(tǒng)就不再提供默認(rèn)的構(gòu)造函數(shù)。與變量定義相似,在用默認(rèn)構(gòu)造函數(shù)創(chuàng)建對(duì)象時(shí),如果創(chuàng)建的是全局對(duì)象或靜態(tài)對(duì)象,則對(duì)象的默認(rèn)值為0,否則對(duì)象的初始值是不定的。當(dāng)構(gòu)造函數(shù)有缺省參數(shù)時(shí),稱為具有缺省參數(shù)的構(gòu)造函數(shù),在使用時(shí)要防止二義性。373.2構(gòu)造函數(shù)與析構(gòu)函數(shù)如:classMyclass//定義類Myclass{private:intmember;public:Myclass();Myclass(inti);
……
}Myclass:Myclass()//構(gòu)造函數(shù)Myclass{member=10;}383.2構(gòu)造函數(shù)與析構(gòu)函數(shù)Myclass:Myclass(inti=10)//構(gòu)造函數(shù)Myclass(inti),該函數(shù)
//的形參i為缺省參數(shù){member=i;}voidmain(){Myclassx(20);Myclassy; //產(chǎn)生二義性,無法確定自動(dòng)調(diào)用哪個(gè)構(gòu)造
//函數(shù)完成對(duì)象的構(gòu)造
……}//學(xué)生類的構(gòu)造函數(shù)例子.cpp(學(xué)生類的構(gòu)造函數(shù)例子—解決方法.cpp)393.2構(gòu)造函數(shù)與析構(gòu)函數(shù)3.2.4析構(gòu)函數(shù)
當(dāng)一個(gè)對(duì)象被定義時(shí),系統(tǒng)自動(dòng)調(diào)用構(gòu)造函數(shù)為該對(duì)象分配相應(yīng)的資源,當(dāng)對(duì)象使用完畢后,這些系統(tǒng)資源需要在對(duì)象消失前被釋放。析構(gòu)函數(shù)是類的一個(gè)特殊成員函數(shù),其函數(shù)名稱是在類名的前面加上~,它沒有返回值,沒有參數(shù),不能隨意調(diào)用,也沒有重載,只是在類對(duì)象生命期結(jié)束時(shí),系統(tǒng)自動(dòng)調(diào)用。析構(gòu)函數(shù)的定義方式為:
~類名(){
函數(shù)體
}403.2構(gòu)造函數(shù)與析構(gòu)函數(shù)注:(1)一個(gè)類中只能擁有一個(gè)析構(gòu)函數(shù)。
(2)如果程序員在定義類時(shí),沒有為類提供析構(gòu)函數(shù),則系統(tǒng)會(huì)自動(dòng)創(chuàng)建一個(gè)默認(rèn)的析構(gòu)函數(shù),其形式為:
~類名(){}(3)對(duì)于一個(gè)簡(jiǎn)單的類來說,大多可以直接使用系統(tǒng)提供的默認(rèn)析構(gòu)函數(shù)。但是,如果在類的對(duì)象中分配有動(dòng)態(tài)內(nèi)存(如:用new申請(qǐng)分配的內(nèi)容)時(shí),就必須為該類提供適當(dāng)?shù)奈鰳?gòu)函數(shù),完成清理工作。Demo:需要自定義的析構(gòu)函數(shù).cpp413.2構(gòu)造函數(shù)與析構(gòu)函數(shù)(4)對(duì)象被析構(gòu)的順序與對(duì)象建立時(shí)的順序正好相反。即最后構(gòu)造的對(duì)象先被析構(gòu)。
Demo:多個(gè)對(duì)象的析構(gòu)順序1.cpp
Demo:多個(gè)對(duì)象的析構(gòu)順序2.cpp(當(dāng)對(duì)象數(shù)組的生命期結(jié)束之后,系統(tǒng)為對(duì)象數(shù)組的每個(gè)元素調(diào)用一次析構(gòu)函數(shù),全局對(duì)象數(shù)組和靜態(tài)對(duì)象數(shù)組的析構(gòu)函數(shù)在程序結(jié)束之前被調(diào)用)//對(duì)象數(shù)組的構(gòu)造函數(shù)與析構(gòu)函數(shù).cpp//對(duì)象數(shù)組例.cpp//對(duì)象數(shù)組及其排序.cpp423.2構(gòu)造函數(shù)與析構(gòu)函數(shù)析構(gòu)函數(shù)也是成員函數(shù),當(dāng)某個(gè)對(duì)象被取消時(shí),它起作用。C++對(duì)象在下列情況下被取消:(1)當(dāng)自動(dòng)對(duì)象(定義于程序塊內(nèi))離開了作用域時(shí)。
//析構(gòu)函數(shù)被觸發(fā)-自動(dòng)對(duì)象離開作用域時(shí).cpp(2)當(dāng)動(dòng)態(tài)分配的對(duì)象被刪除時(shí)。(new/delete)//析構(gòu)函數(shù)被觸發(fā)-動(dòng)態(tài)分配的對(duì)象被刪除時(shí).cpp(3)對(duì)于全局對(duì)象和靜態(tài)對(duì)象,在程序運(yùn)行結(jié)束之前調(diào)用析構(gòu)函數(shù)。
//析構(gòu)函數(shù)被觸發(fā)-全局對(duì)象和靜態(tài)對(duì)象在程序結(jié)束前.cpp
433.2構(gòu)造函數(shù)與析構(gòu)函數(shù)
當(dāng)以上三種情況之一發(fā)生時(shí),析構(gòu)函數(shù)(如果存在)都將起作用。但是如果某個(gè)對(duì)象是動(dòng)態(tài)分配的,卻又從未被取消過,則析構(gòu)函數(shù)就永不會(huì)起作用。//動(dòng)態(tài)對(duì)象及其刪除.cpp
構(gòu)造函數(shù)可以重載,但每個(gè)類只有一個(gè)單獨(dú)的析構(gòu)函數(shù),這并不意味著每次一個(gè)對(duì)象被取消時(shí)只有一個(gè)析構(gòu)函數(shù)起作用。下面列舉了每當(dāng)一個(gè)對(duì)象被取消時(shí)會(huì)自動(dòng)起作用的三種析構(gòu)函數(shù):(1)對(duì)象的析構(gòu)函數(shù),即在對(duì)象自身的類中定義的析構(gòu)(2)為成員對(duì)象所設(shè)的析構(gòu)函數(shù)。
//對(duì)象消亡時(shí)成員對(duì)象的析構(gòu)函數(shù)也被調(diào)用.cpp//對(duì)象消亡時(shí)其成員對(duì)象的析構(gòu)函數(shù)被調(diào)用.cpp(3)為對(duì)象的基類所設(shè)的析構(gòu)函數(shù)。
//對(duì)象消亡時(shí)基類的析構(gòu)函數(shù)也被調(diào)用.cppch12-3.cpp443.2構(gòu)造函數(shù)與析構(gòu)函數(shù)3.2.5拷貝構(gòu)造函數(shù)拷貝構(gòu)造函數(shù)是C++中引入的一種新的構(gòu)造函數(shù)。定義一個(gè)拷貝構(gòu)造函數(shù)的方式是:類名(const類名&形式參數(shù))
{函數(shù)體}
由此可看出:(1)拷貝構(gòu)造函數(shù)的名稱與類的名稱相同,且它只有一個(gè)參數(shù),該參數(shù)就是對(duì)該類對(duì)象的引用。(2)拷貝構(gòu)造函數(shù)的功能是用于實(shí)現(xiàn)對(duì)象值的拷貝,通過將一個(gè)同類對(duì)象的值拷貝給一個(gè)新對(duì)象,來完成對(duì)新對(duì)象的初始化,即用一個(gè)對(duì)象去構(gòu)造另外一個(gè)對(duì)象。在c++中,在一個(gè)類中定義的成員函數(shù)可以訪問該類任何對(duì)象的私有成員。453.2構(gòu)造函數(shù)與析構(gòu)函數(shù)【例3-5】Example是一個(gè)人員信息類。用普通構(gòu)造函數(shù)生成obj1,用拷貝構(gòu)造函數(shù)生成obj2。include<iostream>usingnamespacestd;classExample{private:char*name;intnum;public:Example(inti,char*str){//構(gòu)造函數(shù)定義
name=newchar[strlen(str)+1];strcpy(name,str);num=i;}
~Example(){delete[]name);}
463.2構(gòu)造函數(shù)與析構(gòu)函數(shù)Example(constExample&x)//
拷貝構(gòu)造函數(shù)定義{num=x.num;name=newchar[strlen()+1];strcpy(name,);}//如果去掉該拷貝構(gòu)造函數(shù),則有問
//題,因?yàn)槲鰳?gòu)時(shí),delete對(duì)同一空間name釋放了兩次voidlist(void){//定義顯示函數(shù)listcout<<"\n數(shù)據(jù)成員num的值="<<num<<endl<<endl;cout<<"name:"<<name<<endl;}};voidmain(void){Exampleobj1(215,"張立三");
Exampleobj2(obj1);
//使用拷貝構(gòu)造函數(shù)構(gòu)造obj2obj2.list();//顯示obj2的值}473.2構(gòu)造函數(shù)與析構(gòu)函數(shù)程序的執(zhí)行結(jié)果是:數(shù)據(jù)成員num的值=215name:張立三說明:(1)上例中在main函數(shù)中的語(yǔ)句Exampleobj2(obj1);在執(zhí)行時(shí),系統(tǒng)會(huì)自動(dòng)調(diào)用類Example的拷貝構(gòu)造函數(shù)完成對(duì)obj2對(duì)象的構(gòu)造。(2)如果程序員沒有為所設(shè)計(jì)的類提供顯式的拷貝構(gòu)造函數(shù),則系統(tǒng)會(huì)自動(dòng)提供一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù),其功能是:把作為參數(shù)的對(duì)象的數(shù)據(jù)成員逐個(gè)拷貝到目標(biāo)變量中,這稱為成員級(jí)復(fù)制(或淺拷貝)。//由淺拷貝引起的一個(gè)典型錯(cuò)誤.cpp483.2構(gòu)造函數(shù)與析構(gòu)函數(shù)拷貝構(gòu)造函數(shù)具有以下特點(diǎn):(1)也是一種構(gòu)造函數(shù),因此函數(shù)名與類名一樣,不指定函數(shù)返回值。(2)只有一個(gè)參數(shù),是對(duì)同類的某個(gè)對(duì)象的引用;當(dāng)用const修飾時(shí),說明該對(duì)象是一個(gè)不能被更新的常對(duì)象。(3)每一個(gè)類都必須有一個(gè)拷貝構(gòu)造函數(shù)。如果類中沒有聲明拷貝構(gòu)造函數(shù),編譯器會(huì)自動(dòng)生成一個(gè)具有上述形式的公有的拷貝構(gòu)造函數(shù)。493.2構(gòu)造函數(shù)與析構(gòu)函數(shù)通常情況下,拷貝構(gòu)造函數(shù)在以下3種情況下會(huì)被調(diào)用:(1)當(dāng)用類的一個(gè)已知的對(duì)象去初始化該類的另一個(gè)正在創(chuàng)建的對(duì)象時(shí)。(2)當(dāng)采用傳值調(diào)用方式時(shí),對(duì)象作為函數(shù)實(shí)參轉(zhuǎn)遞給函數(shù)形參。(3)當(dāng)對(duì)象作為函數(shù)返回值時(shí)。
//簡(jiǎn)單類的構(gòu)造函數(shù)析構(gòu)函數(shù)和拷貝構(gòu)造構(gòu)造函數(shù)的例子//一個(gè)綜合的例子construanddestru.cpp//一個(gè)綜合的例子construanddestru的補(bǔ)充.cpp//拷貝構(gòu)造函數(shù)及賦值操作的調(diào)用時(shí)機(jī).cpp//排序二叉樹及遍厲.cpp//帶頭結(jié)點(diǎn)的單鏈表類的定義和實(shí)現(xiàn)1.cpp//帶頭結(jié)點(diǎn)的單鏈表類的定義和實(shí)現(xiàn)2.cpp503.2構(gòu)造函數(shù)與析構(gòu)函數(shù)3.2.6一個(gè)類的對(duì)象作為另一個(gè)類的數(shù)據(jù)成員一個(gè)類中的數(shù)據(jù)成員除了可以是int,char,float等這些基本的數(shù)據(jù)類型外,還可以是某一個(gè)類的一個(gè)對(duì)象。用子對(duì)象創(chuàng)建新類。在C++中,當(dāng)把一個(gè)類的對(duì)象作為新類的數(shù)據(jù)員時(shí),則新類的定義格式可表示為:classX{類名1成員名1;
類名2成員名2;
……
類名n成員名n;
……//其它成員};513.2構(gòu)造函數(shù)與析構(gòu)函數(shù)(3)如果一個(gè)類A的對(duì)象作為另一個(gè)類B的數(shù)據(jù)成員,則在類B的對(duì)象創(chuàng)建過程中,調(diào)用其構(gòu)造函數(shù)的過程中,數(shù)據(jù)成員(類A的對(duì)象)會(huì)自動(dòng)調(diào)用類A的構(gòu)造函數(shù)。但應(yīng)注意:如果類A的構(gòu)造函數(shù)為有參函數(shù)時(shí),則在程序中必須在類B的構(gòu)造函數(shù)的括號(hào)后面加一“:”和被調(diào)用的類A的構(gòu)造函數(shù),且調(diào)用類A的構(gòu)造函數(shù)時(shí)的實(shí)參值必須來自類B的形參表中的形參。這種方法稱為初始化表的方式調(diào)用構(gòu)造函數(shù)。如:以上面定義的類X為例,在對(duì)類X的對(duì)象進(jìn)行初始化時(shí),必須首先初始化其中的子對(duì)象,即必須首先調(diào)用這些子對(duì)象的構(gòu)造函數(shù)。因此,類X的構(gòu)造函數(shù)的定義格式應(yīng)為:X::X(參數(shù)表0):成員1(參數(shù)表1),成員2(參數(shù)表2),…,成員n(參數(shù)表n){……}523.2構(gòu)造函數(shù)與析構(gòu)函數(shù)其中,參數(shù)表1提供初始化成員1所需的參數(shù),參數(shù)表2提供初始化成員2所需的參數(shù),依此類推。并且這幾個(gè)參數(shù)表的中的參數(shù)均來自參數(shù)表0,另外,初始化X的非對(duì)象成員所需的參數(shù),也由參數(shù)表0提供。在構(gòu)造新類的對(duì)象過程中,系統(tǒng)首先調(diào)用其子對(duì)象的構(gòu)造函數(shù),初始化子對(duì)象;然后才執(zhí)行類X自己的構(gòu)造函數(shù),初始化類中的非對(duì)象成員。對(duì)于同一類中的不同子對(duì)象,系統(tǒng)按照它們?cè)陬愔械恼f明順序調(diào)用相應(yīng)的構(gòu)造函數(shù)進(jìn)行初始化,而不是按照初始化表的順序。
533.2構(gòu)造函數(shù)與析構(gòu)函數(shù)
在構(gòu)造函數(shù)中給數(shù)據(jù)成員賦值時(shí),可以采用兩種形式:一是在函數(shù)體中使用賦值語(yǔ)句把表達(dá)式的值賦給成員變量;二是在形參后和函數(shù)體之前使用成員初始化列表賦值,并且成員初始化列表同形參表之間必須用冒號(hào)分開,成員初始化列表中用逗號(hào)分開的每一項(xiàng)用于給一個(gè)成員變量賦值,每一項(xiàng)的格式為“成員變量名(初值表達(dá)式)”。這兩種形式可以混合使用。forexamples://含有對(duì)象成員的外部類的構(gòu)造函數(shù)與析構(gòu)函數(shù).cpp//(useclassmember變化.cpp)//類中成員函數(shù)的遞歸實(shí)現(xiàn).cpp543.2構(gòu)造函數(shù)與析構(gòu)函數(shù)【例3-6】以下定義了三個(gè)Student、Teacher和Tourpair,其中Student類的對(duì)象和Teacher類的對(duì)象作為了Tourpair的數(shù)據(jù)成員,觀察對(duì)象的構(gòu)造過程和構(gòu)造函數(shù)被執(zhí)行的順序。#include<iostream>usingnamespacestd;classStudent{public:Student(){cout<<”constructstudent.\n”;semeshours=100;gpa=3.5;}553.2構(gòu)造函數(shù)與析構(gòu)函數(shù)protected:intsemeshours;floatgpa;};classTeacher{public:Teacher(){cout<<”constructTeacher.\n”;}};563.2構(gòu)造函數(shù)與析構(gòu)函數(shù)classTourpair{public:Tourpair(){cout<<”constructtourpair.\n”;nomeeting=0;}protected:Studentstudent;Teacherteacher;intnomeeting;};573.2構(gòu)造函數(shù)與析構(gòu)函數(shù)voidmain(void){Tourpairtp;cout<<”backinmain.\n”;}其執(zhí)行結(jié)果是:
constructstudent.constructteacher.constructtourpair.backinmain.
由此可見:主函數(shù)main()運(yùn)行開始時(shí),遇到要?jiǎng)?chuàng)建Tourpair類的對(duì)象,于是調(diào)用其構(gòu)造函數(shù)Tourpair(),該構(gòu)造啟動(dòng)時(shí),首先分配對(duì)象空間(包含一個(gè)Student對(duì)583.2構(gòu)造函數(shù)與析構(gòu)函數(shù)象、一個(gè)Teacher對(duì)象和一個(gè)int型數(shù)據(jù)),然后根據(jù)其在類中聲明的對(duì)象成員的次序依次調(diào)用其構(gòu)造函數(shù)。即先調(diào)用Student()構(gòu)造函數(shù),后調(diào)用Teacher()構(gòu)造函數(shù),最后才執(zhí)行它自己的構(gòu)造函數(shù)的函數(shù)體。由于上例中Tourpair類的數(shù)據(jù)成員student和teacher的構(gòu)造函數(shù)都是無參函數(shù),所以系統(tǒng)在構(gòu)造student和teacher對(duì)象時(shí)會(huì)自動(dòng)調(diào)用各自的構(gòu)造函數(shù)Student()和Teacher(),而不需要用初始化表的方式去調(diào)用?!纠?-7】試分析以下程序的執(zhí)行結(jié)果:#include<iostream>usingnamespacestd;593.2構(gòu)造函數(shù)與析構(gòu)函數(shù)classStudent{public:Student(char*pName="Noname"){cout<<"構(gòu)造新同學(xué):"<<pName<<endl;strncpy(name,pName,sizeof(name));name[sizeof(name)-1]='\0';}Student(Student&s){cout<<"構(gòu)造copyof"<<<<endl;strcpy(name,"copyof");strcat(name,);}603.2構(gòu)造函數(shù)與析構(gòu)函數(shù)~Student(){cout<<"析構(gòu)
"<<name<<endl;}protected:charname[40];};classTutor{public:Tutor(Student&s):student(s)//此為初始化表,調(diào)用
//Student的拷貝構(gòu)造函數(shù)
{cout<<"構(gòu)造指導(dǎo)教師
\n";}protected:Studentstudent;};613.2構(gòu)造函數(shù)與析構(gòu)函數(shù)voidmain(void){Studentst1;//此處調(diào)用Student的構(gòu)造函數(shù)Student(char//*pName="Noname")Studentst2("zhang");//同上
Tutortutor(st2);//此處調(diào)用Tutor的構(gòu)造函數(shù)Tutor(Student&s)//在構(gòu)造tutor對(duì)象的過程中,用初始化表調(diào)用
//Student類的拷貝構(gòu)造函數(shù)Student(Student&s)}執(zhí)行結(jié)果如下:構(gòu)造新同學(xué):Noname構(gòu)造新同學(xué):zhang構(gòu)造copyofzhang623.2構(gòu)造函數(shù)與析構(gòu)函數(shù)構(gòu)造指導(dǎo)教師析構(gòu)copyofzhang析構(gòu)zhang析構(gòu)Noname
當(dāng)釋放一個(gè)包含內(nèi)部對(duì)象類時(shí)(因?yàn)橛形鰳?gòu)函數(shù)),包含內(nèi)部對(duì)象的析構(gòu)函數(shù)代碼體在內(nèi)部對(duì)象的析構(gòu)函數(shù)被執(zhí)行之前執(zhí)行(與構(gòu)造函數(shù)的次序相反)。//例inner_object.cpp633.2構(gòu)造函數(shù)與析構(gòu)函數(shù)3.2.7利用初始化表對(duì)常量數(shù)據(jù)成員或引用成員提供初值
如前所述,構(gòu)造函數(shù)可對(duì)對(duì)象的數(shù)據(jù)成員進(jìn)行初始化,但若數(shù)據(jù)成員為常量成員或引用成員時(shí),就有所不同。如:643.2構(gòu)造函數(shù)與析構(gòu)函數(shù)classSillyclass{public:Sillyclass(){ten=10;ref=i;}//此構(gòu)造函數(shù)對(duì)成員ten和refi的初始化錯(cuò)誤protected:constintten;//常量數(shù)據(jù)成員tenconstint&ref;//引用ref};//Sillyclass::Sillyclass():ten(10),ref(ten){};653.2構(gòu)造函數(shù)與析構(gòu)函數(shù)說明:
1.造成以上錯(cuò)誤的原因是在Sillyclass類的構(gòu)造函數(shù)進(jìn)入之后(開始執(zhí)行其函數(shù)體時(shí)),對(duì)象結(jié)構(gòu)已經(jīng)建立,數(shù)據(jù)成員ten和refi已存在,而其數(shù)據(jù)成員ten為const,而refi為引用,所以在構(gòu)造函數(shù)體內(nèi)不能再對(duì)其指派新的值。2.解決以上問題的方法是利用初始化表:在構(gòu)造函數(shù)的括號(hào)后面加一“:”和初始化表。初始化表的格式是:數(shù)據(jù)成員名(值),如果有多個(gè)時(shí),需要用逗號(hào)隔開。
Sillyclass::Sillyclass():ten(10),ref(ten){};663.2構(gòu)造函數(shù)與析構(gòu)函數(shù)說明:
3.常數(shù)據(jù)成員的定義與一般常量的定義方式相同,只是它的定義必須出現(xiàn)在類體中。常數(shù)據(jù)成員也必須進(jìn)行初始化,并且不能被更新。但常數(shù)據(jù)成員的初始化只能通過構(gòu)造函數(shù)的成員初始化列表進(jìn)行。673.2構(gòu)造函數(shù)與析構(gòu)函數(shù)【例3-8】
類employee中包括私有數(shù)據(jù)成員sal,和2個(gè)公有函數(shù)成員example、show。程序中使用了初始化表。#include<iostream>usingnamespacestd;//定義類employeeclassemployee{private:constintsal;constint&ref_sal;public:employee();voidshow();};683.2構(gòu)造函數(shù)與析構(gòu)函數(shù)#include<iostream>usingnamespacestd;classemployee{
private:
constintsal;
constint&ref_sal;
public:
employee();voidshow();
};employee::employee():sal(950),ref_sal(sal){};//初 //始化表voidemployee::show(){cout<<"\nsal="<<sal<<
endl;}voidmain(void){employeeobj;obj.show();
}693.2.8類作用域3.2.8類作用域
類作用域又可稱為類域,它是指在類定義中用一對(duì)大括號(hào)所括起來的范圍。由于在程序文件中可包含類,而類中又包含函數(shù),因此,類域顯然是一個(gè)小于文件域,而大于函數(shù)域的概念。由于在一個(gè)類中既可定義變量(數(shù)據(jù)成員),又可定義函數(shù)(成員函數(shù)),所以,類域在許多方面與文件域相似。但是,在類域中定義的變量不能使用auto、register和extern等修飾符,而且在類域中定義的函數(shù)也不能使用extern修飾符。同時(shí),在類域中定義的靜態(tài)成員和成員函數(shù)還具有外部的連接屬性。703.2.8類作用域【例3-9】類域及其成員引用舉例,設(shè)以下程序代碼被存放到了一個(gè)程序文件中。#include<iostream>usingnamespacestd;classMyclass{private:intx;inty;public:Myclass(inta,intb){x=a;y=b;}voidprint();voidmyfunc();};713.2.8類作用域voidMyclass::print(){cout<<"x="<<x<<","<<"y="<<y<<endl;}voidMyclass::myfunc(){intx=9,y=10;cout<<"Inmyfunc:x="<<x<<","<<"y="<<y<<endl;//輸出?
cout<<"Myclass::x="<<Myclass::x<<","<<" Myclass::y="<<Myclass::y<<endl;//輸出?}723.2.8類作用域voidmain(void){Myclasstest(100,200),*ptest=&test;test.print();//通過對(duì)象名訪問公有成員
ptest->myfunc();}程序的運(yùn)行結(jié)果為:?x=100,y=200Inmyfunc:x=9,y=10Myclass::x=100,Myclass::y=200733.2.8類作用域說明:
(1)類成員函數(shù)的原型在類的定義體中聲明,具有類作用域,但其實(shí)現(xiàn)部分在類的定義體外。由于不同類的成員函數(shù)可以具有相同的名字,因此,需要用作用域運(yùn)算符“::”(作用域分辨符)來指明該成員函數(shù)所屬的類。743.2.8類作用域說明:
(2)類中的成員擁有類作用域,因此在成員函數(shù)中可以直接引用類的數(shù)據(jù)成員。但是,如果在成員函數(shù)中定義了同名的局部變量時(shí),則必須用作用域運(yùn)算符“::”來指定,以免混亂。如:上例中的myfunc()函數(shù)中定義了與類的數(shù)據(jù)成員同名的局部變量x、y,所以在myfunc()函數(shù)中要訪問類中的數(shù)據(jù)成員x和y的值時(shí),必須加上作用域運(yùn)算符。753.2.8類作用域說明:
(3)類中的成員擁有類的作用域,如果要從類外訪問類的成員時(shí),則必須通過對(duì)象名或指向?qū)ο蟮闹羔?。?dāng)通過對(duì)象名時(shí),應(yīng)使用圓點(diǎn)成員選擇符“.”;當(dāng)通過指針時(shí),應(yīng)使用箭頭成員選擇符“->”。如上例中的test.print();與ptest->myfunc();
下面看看作用域分辨符在類層次結(jié)構(gòu)中是如何唯一標(biāo)識(shí)成員的?763.2.8類作用域
在類的派生層次結(jié)構(gòu)中,基類的成員和派生類新增的成員都具有類作用域,二者的作用范圍不同,是相互包含的兩個(gè)層,派生類在內(nèi)層。這時(shí),如果派生類聲明了一個(gè)和某個(gè)基類成員同名的新成員,派生的新成員就隱藏了外層同名成員,直接使用成員名只能訪問到派生類的成員。//基類與派生類中的同名成員.cpp773.2.8類作用域
如果派生類中聲明了與基類成員函數(shù)同名的新函數(shù),即使函數(shù)的參數(shù)表不同,從基類繼承的同名函數(shù)的所有重載形式也都會(huì)被隱藏。如果要訪問被隱藏的成員,就需要使用作用域分辨符和基類名來限定。
對(duì)于多繼承情況?783.2.8類作用域
對(duì)于多繼承情況,首先考慮各個(gè)基類之間沒有任何繼承關(guān)系,同時(shí)也沒有共同基類的情況。最典型的情況就是所有基類都沒有上級(jí)基類。如果某派生類的多個(gè)基類擁有同名的成員,同時(shí),派生類又新增這樣的同名成員,在這種情況下,派生類成員將隱藏所有基類的同名成員。這時(shí)使用“對(duì)象名.成員名”方式可以唯一標(biāo)識(shí)和訪問派生類新增成員,基類的同名成員也可以使用基類名和作用域分辨符訪問。//多繼承同名隱藏舉例1.cpp
793.2.8類作用域但是,如果派生類沒有聲明同名成員,“對(duì)象名.成員名”方式就無法唯一標(biāo)識(shí)成員,這時(shí),從不同基類繼承過來的成員具有相同的名稱,同時(shí)具有相同的作用域,系統(tǒng)僅僅根據(jù)這些信息根本無法判斷到底是調(diào)用哪個(gè)基類的成員,這時(shí)就必須通過基類名和作用域分辨符來標(biāo)識(shí)成員。//多繼承同名隱藏舉例2.cpp803.2.8類作用域上面討論多繼承中,假定所有基類之間沒有繼承關(guān)系,如果這個(gè)條件得不到滿足會(huì)出現(xiàn)什么情況呢?如果某個(gè)派生類的部分或全部直接基類是從另一個(gè)共同的基類派生而來,在這些直接基類中,從它們的上一級(jí)基類繼承來的成員就擁有相同的名稱,因此派生類中也就會(huì)產(chǎn)生同名現(xiàn)象,對(duì)這種類型的同名成員也要使用作用域分辨符來唯一標(biāo)識(shí),而且必須用直接基類來進(jìn)行限定。//派生類的直接基類擁有共同基類.cpp813.2.8前向引用聲明3.2.8前向引用聲明
C++類應(yīng)當(dāng)先聲明后使用,但是在處理相對(duì)復(fù)雜的問題,考慮類的組合時(shí),很可能遇到兩個(gè)類相互引用的情況。如:classA{public:voidf(Bb);//以B類的對(duì)象b為形參的成員函數(shù)};classB{public:voidg(Aa);//以A類的對(duì)象a為形參的成員函數(shù)};823.2.8前向引用聲明
無論將哪一個(gè)類的聲明放在前面,都會(huì)引起編譯錯(cuò)誤。解決這個(gè)問題的辦法,就是使用前向引用聲明。前向引用聲明,是在引用未聲明的類之前,將該類的名字告訴編譯器,使編譯器知道那是一個(gè)類名。這樣,當(dāng)程序中使用這個(gè)類名時(shí),編譯器就不會(huì)認(rèn)為是錯(cuò)誤,而類的完整聲明可以在程序的其他地方。833.2.8前向引用聲明加上前向引用聲明如下:ClassB;//前向引用聲明classA{public:voidf(Bb);//以B類的對(duì)象b為形參的成員函數(shù)};classB{public:voidg(Aa);//以A類的對(duì)象a為形參的成員函數(shù)};注意:盡管使用了前向引用聲明,但是在提供一個(gè)完整類聲明之前,不能聲明該類的對(duì)象,也不能在內(nèi)聯(lián)成員函數(shù)中使用該類的對(duì)象。843.2.8前向引用聲明如:classFred;//前向引用聲明ClassBarney{Fredx;//error類Fred的聲明尚不完善};classFred{Barneyy;}對(duì)類名Fred的前向引用聲明只能說明Fred是一個(gè)類名,而不能給處該類的完整聲明,因此在類Barney中不能聲明類Fred的數(shù)據(jù)成員。因此使用兩個(gè)類以彼此的對(duì)象為數(shù)據(jù)成員是不合法的。853.2.8前向引用聲明再如:classFred;//前向引用聲明classBarney{public:voidmethod(){x->yabbaDabbaDo();//error:Fred類的對(duì)象在定義之前被使用
}private:Fred*x;//right:經(jīng)過前向引用聲明,可以聲明 //Fred類的對(duì)象指針};classFred{public:voidyabbaDabbaDo();private:Barney*y;};863.2.8前向引用聲明解決這個(gè)問題的方法是,更改這兩個(gè)類的生命次序,或者將函數(shù)method()改為非內(nèi)聯(lián)形式,并且在類Fred的完整聲明之后,再給出函數(shù)的定義。應(yīng)當(dāng)記?。寒?dāng)使用前向引用聲明時(shí),只能使用被聲明的符號(hào),而不能涉及類的任何細(xì)節(jié)。873.3繼承和派生本節(jié)基本內(nèi)容理解繼承的概念和意義,理解單一繼承、多重繼承;理解并掌握派生類構(gòu)造函數(shù)的編寫要求,以及派生類對(duì)象的構(gòu)造過程和機(jī)理;理解并掌握派生類析構(gòu)函數(shù)的定義方法,以及派生類對(duì)象的析構(gòu)過程和機(jī)理。本節(jié)重點(diǎn)和難點(diǎn)基類和派生類中成員的訪問控制關(guān)系;派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)的定義,派生類對(duì)象的構(gòu)造和析構(gòu)過程和機(jī)理。
883.3繼承和派生3.3.1繼承的概念一個(gè)類的數(shù)據(jù)成員和成員函數(shù),有些是類本身自己定義的,有一些是可繼承的或通過模板生成的。所謂繼承(inheritance)就是利用已有的數(shù)據(jù)類型定義出新的數(shù)據(jù)類型。利用類的“繼承”,就可以將原來的程序代碼重復(fù)使用,從而減少了程序代碼的冗余度,符合軟件重用的目標(biāo)。
893.3繼承和派生3.3.1繼承的概念
繼承是面向?qū)ο蟪绦蛟O(shè)計(jì)的一個(gè)重要機(jī)制。另外,在C++中擴(kuò)充派生類成員的方法是非常靈活的。派生類不僅可以繼承原來類的成員,還可以通過以下方式產(chǎn)生新的成員:(1)增加新的數(shù)據(jù)成員;(2)增加新的成員函數(shù);(3)重新定義已有成員函數(shù);(4)改變現(xiàn)有成員的屬性。
903.3繼承和派生
在繼承關(guān)系中,稱被繼承的類為基類(baseclass)(或父類),而把通過繼承關(guān)系定義出來的新類稱為派生類(derivedclass)(子類)。由此可見,派生類既可以對(duì)基類的性質(zhì)進(jìn)行擴(kuò)展,又可以進(jìn)行限制,從而得到更加靈活、更加適用的可重用模塊,大大縮短程序的開發(fā)時(shí)間。913.3繼承和派生3.3.2單繼承1.定義派生類在基類的基礎(chǔ)上定義其派生類的定義形式為:
class派生類名:訪問方式基類名
{派生類中的新成員}其中:(1)派生類名由用戶自己命名;(2)訪問方式即繼承方式,可以為public或private,默認(rèn)為private方式。訪問方式為public方式時(shí),這種繼承稱為公有繼承,而訪問方式為private方式時(shí),稱為私有繼承;(3)基類名必須是程序中一個(gè)已有的類。923.3繼承和派生(4)在冒號(hào)“:”后的部分告訴系統(tǒng),這個(gè)派生類是從哪個(gè)基類派生的,以及在派生時(shí)的繼承方式。(5)大括號(hào)內(nèi)的部分是派生類中新定義的成員。2.基類與派生類之間的關(guān)系(1)派生類不僅擁有屬于自己的數(shù)據(jù)成員與成員函數(shù),還保持了從基類繼承來的數(shù)據(jù)成員與成員函數(shù);同時(shí)派生類可對(duì)一些繼承來的函數(shù)重新定義,以適應(yīng)新的要求。933.3繼承和派生(2)C++關(guān)于類的繼承方式的規(guī)定,如下表3.1所示:①按private方式繼承(即私有繼承)時(shí),基類中的公有成員和保護(hù)成員在派生類中皆變?yōu)樗接谐蓡T。②按public方式繼承(即公有繼承)時(shí),基類中的公有成員和保護(hù)成員在派生類中不變。943.3繼承和派生③無論哪種繼承方式,基類的私有成員在派生類中均不可見(不可訪問,不可繼承?)。這與私有成員的定義是一致的,符合數(shù)據(jù)封裝的思想。④在公有繼承方式下,基類的公有成員和保護(hù)成員被繼承為派生類成員時(shí),其訪問屬性不變?;惞信缮愃接信缮恜ublic成員public成員private成員protected成員protected成員private成員private成員
不可見
不可見953.3繼承和派生注意:
私有成員與不可訪問(不可見)成員是兩個(gè)不同的概念。某個(gè)類的私有成員只能被該類的成員函數(shù)所訪問,而類的不可訪問成員甚至不能被該類自身的成員函數(shù)所訪問。類的不可訪問成員總是從某個(gè)基類派生來的,它要么是基類的私有成員,要么是基類的不可訪問成員。963.3繼承和派生表3.1直接基類和直接派生類的訪問權(quán)限變化表—重點(diǎn)
繼承類型基類中的訪問權(quán)限基類成員在派生類中的訪問權(quán)限public公有繼承publicprivateprotectedpublic不可見(被隱藏)protectedprivate私有繼承publicprivateprotectedprivate不可見(被隱藏)privateprotected保護(hù)繼承publicprivateprotectedprotected不可見(被隱藏)private973.3繼承和派生(3)在C++中,可以根據(jù)需要定義多層的繼承關(guān)系,也可以從一個(gè)基類派生出多個(gè)類,形成類的層次結(jié)構(gòu),在類的層次結(jié)構(gòu)中,處于高層的類,表示最一般的特征,而處于底層的類,表示更具體的特征,在多層繼承關(guān)系中,基類與派生類的關(guān)系是相對(duì)的。例如:由類A派生出類B,再由類B派生出類C,這里類B相對(duì)于類A是派生類,而相對(duì)于類C是基類,并稱類C是類A的間接派生類,稱類A是類C的間接基類;而稱具有直接派生關(guān)系的兩個(gè)類分別為直接派生類和直接基類。983.3繼承和派生【例3-9】
類Build_1是一個(gè)關(guān)于樓房數(shù)據(jù)的類。它的數(shù)據(jù)成員有posi_x、posi_y和area,分別是樓房位置的經(jīng)、緯度和建筑面積。它的函數(shù)成員只有set1,用于設(shè)置數(shù)據(jù)成員posi_x、posi_y和area的值。讓Build_1作為基類,再增加數(shù)據(jù)成員high、函數(shù)成員set2和disp來定義派生類Build_2。993.3繼承和派生#include<iostream>usingnamespacestd;classBuild_1 //定義基類{protected:intposi_x; //有三個(gè)保護(hù)型的數(shù)據(jù)成員
intposi_y;intarea;public:voidset1(intx,inty,inta){posi_x=x;posi_y=y;area=a;}};1003.3繼承和派生//定義派生類Build_2classBuild_2:publicBuild_1{intheight;//新增數(shù)據(jù)成員public:voidset2(inth){height=h;}voiddisp(void){cout<<"\n經(jīng)度:"<<posi_x<<endl;cout<<"緯度:"<<posi_y<<endl;cout<<"高度:"<<height<<endl;cout<<"面積:"<<area<<endl<<endl;}};1013.3繼承和派生voidmain(void){//用Build_2生成對(duì)象objBuild_2obj;obj.set1(100,200,300);obj.set2(400);obj.disp();}1023.
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- TY/T 3501.2-2024高山滑雪板性能測(cè)定第2部分:質(zhì)量和極慣性矩
- NB/T 11524-2024礦用噴水滅火機(jī)器人通用技術(shù)要求
- 課題申報(bào)書全部
- 法治思維課題申報(bào)書
- Unit 3 Keep Fit section B 2a-2c 同步課時(shí)講練(含答案)七年級(jí)英語(yǔ)下冊(cè)(人教版2024)
- 廣州 社科 課題申報(bào)書
- 合同范本模板不能復(fù)制
- 不讓停車協(xié)議合同范本
- 體育和音樂課題申報(bào)書
- 醫(yī)療會(huì)議服務(wù)合同范例
- 2023年全省職業(yè)院校技能大賽高職教師組護(hù)理技能賽項(xiàng)競(jìng)賽規(guī)程
- 小學(xué)利潤(rùn)問題應(yīng)用題100道附答案(完整版)
- 醫(yī)院智能化系統(tǒng)內(nèi)網(wǎng)、外網(wǎng)及設(shè)備網(wǎng)系統(tǒng)拓?fù)鋱D-可編輯課件
- 車庫(kù)租賃合同
- 小學(xué)生心理健康主題家長(zhǎng)會(huì)
- 社交禮儀-儀態(tài)禮儀
- 安徽省2024年中考語(yǔ)文真題試卷【附答案】
- QB/T 4031-2024 阻燃性汽車空氣濾紙(正式版)
- 2024年南京科技職業(yè)學(xué)院?jiǎn)握新殬I(yè)適應(yīng)性測(cè)試題庫(kù)帶答案
- DB52-T 1780-2024 醬香型白酒安全生產(chǎn)規(guī)范
- 2024年皖西衛(wèi)生職業(yè)學(xué)院?jiǎn)握新殬I(yè)適應(yīng)性測(cè)試題庫(kù)及參考答案
評(píng)論
0/150
提交評(píng)論