C++面向对象程序设计课件_第1页
C++面向对象程序设计课件_第2页
C++面向对象程序设计课件_第3页
C++面向对象程序设计课件_第4页
C++面向对象程序设计课件_第5页
已阅读5页,还剩436页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

2023-11-161C++:面向對象程式設計2023-11-162類的誕生類(Class)是面向對象思想中的一個重要組成部分。如何在電腦中將“類”表達出來?在C語言中,當定義結構體(struct):structSAMPLE{ intmember1,member2;}var1;以後,就可以使用結構中的成員:intvar=var1.member1*var1.member2;2023-11-163將結構體成員的使用代碼寫成一個函數,得到:intcaluc(structSAMPLEvar){returnvar.member1*var.member2;}操作結構變數類的誕生2023-11-164然後,將上面的函數放入結構體中structSAMPLE{

intcalcu() { returnmember1*member2; } intmember1,member2;};數據成員函數成員類的誕生2023-11-165這樣的結構體就具有了一定的屬性(member1和member2),也具有一定的行為(函數calcu),它就是“類”的雛形。該結構體的使用方法如下:structSAMPLEvar;var.member1=var.member2=10;inttemp=var.calcu();類的誕生2023-11-1662.1類2023-11-1672.1.1類的定義classCCompanyStaff{//BEGIN

//聲明成員函數voidSetBasicSal(floatsal);//設置基本工資

//聲明(定義)數據成員intm_iStaffNum;//工作編號charm_cName[20];//姓名floatm_fRateOfAttend;//出勤率floatm_fBasicSal;//基本工資};//END多了一個函數(SetBasicSal),其他都象結構體。struct

class2023-11-168CCompanyStaff類的定義以關鍵字class開始。其後是類名。“{}”表示類定義的開始和結束,最終以分號結束。一般在類中只聲明成員函數的原型,而函數的實現(即函數體的定義)則放在類外完成。“函數原型”,即只聲明函數名、參數類型和返回值類型,而不包括函數體代碼。2.1.1類的定義2023-11-169函數的定義則可在函數(SetBasicSal)聲明之後:voidCCompanyStaff::SetBasicSal(floatsal){m_fBasicSal=sal;}函數定義:函數名前多了一個類作用域運算符(CCompanyStaff::),用於標識該函數定義屬於哪一個類。2.1.1類的定義2023-11-1610對於C++語言的編程習慣:1、將類的聲明存放於“*.h”或“*.hpp”的頭檔中,每個類一個聲明檔。2、將類的定義存放於“*.cpp”檔中,與相應的聲明檔一一對應.c

.cpp2.1.1類的定義2023-11-1611封裝成類的好處——可以實現數據隱藏。封裝也確定了類成員的訪問屬性。對象的封裝性2023-11-1612對象的封裝性C++語言引入面向對象思想,引入類(對象)的概念之後,必然會具有一些新的特性。面向對象思想中類(對象)的基本特性之一:封裝性。封裝性:直觀理解,類將屬於它的數據(成員變數)和針對數據的操作(成員函數)包裹在一起就是一種封裝。封裝的目的:保護類(對象)的實現。對封裝屬性進行細分:公有的;私有的;保護的。2023-11-1613C++語言為了實現面向對象的封裝,引入了三個新的“修飾”關鍵字:public

(公有的):公開的,可見的。對象成員(變數與方法)可以在對象外使用。private

(私有的):不可見的。成員只能在對象內部使用。protected

(保護的):受到保護的。成員也只能在內部使用。(以後再討論)C++類的成員(變數和方法)具有了各自不同的屬性。對象的封裝性2023-11-1614classCCompanyStaff{public:voidSetBasicSal(floatsal);//設置基本工資private:intm_iStaffNum;//工作編號charm_cName[20];//姓名floatm_fRateOfAttend;//出勤率floatm_fBasicSal;//基本工資};//END對象的封裝性2023-11-1615CCompanyStaffstaff;staff.SetBasicSal(600);//合法的,因為SetBasicSal是一個公開(public)的方法。staff.m_iStaffNum=100;//非法的,因為m_iStaffNum是一個私有(private)的變數。voidCCompanyStaff::SetBasicSal(floatsal){m_fBasicSal=sal;//SetBasicSal是對象的成員,所以它的定義中可以訪問m_fBasicSal私有變數。}對象的封裝性2023-11-16161、C++類定義中的缺省屬性為私有的(private)。2、每個修飾符的作用範:從一個修飾符開始,直到另外一個修飾符時結束(或類結束了)。3、C++類中的成員(變數和方法)都應該明確地指明它們各自的屬性。對象的封裝性2023-11-16172.1.2類成員的訪問許可權封裝所實現的數據隱藏是面向對象程式設計的一個關鍵特性——隱藏一個類的數據從而使其他類無法訪問。隱藏由封裝實現,所以隱藏所實現的類成員的訪問控制權限也和封裝方法一一對應。對應於封裝,類成員有3種訪問許可權:公有類型(public)、私有類型(private)保護類型(protected)。2023-11-1618類

私有

公有數據或函數數據或函數無法從類外訪問允許從類外訪問2.1.2類成員的訪問許可權2023-11-1619公有類型的成員定義了類的介面,由關鍵字public聲明,在類外只能訪問公有成員。私有類型的訪問許可權為私有的成員由關鍵字private聲明,它們只能被類本身的成員函數訪問,來自類外部的任何訪問都是非法的。保護類型的成員與私有成員類似,區別僅在於繼承過程中,保護類型的成員可以被所在類的派生類成員函數訪問;而這一點對於私有成員來說是非法的。2.1.2類成員的訪問許可權2023-11-1620類的成員函數可以訪問類的所有成員,沒有任何限制;而類對象訪問類的成員就要受到訪問控制符的限制。訪問許可權舉例:sclass2_1_demo.hsmain2_1.cpp類對象和類的成員函數對數據成員的訪問許可權不同。2.1.2類成員的訪問許可權2023-11-1621外部介面是類外訪問類中私有數據的橋樑。聲明時,類中不同訪問許可權的成員可以按任意順序出現。但依然涉及到一個編程習慣問題:“以數據為中心”

“以行為為中心”

國際公約。2023-11-1622建議把一個類的數據成員都聲明為私有的訪問許可權。這樣做有兩個好處:一是資訊隱藏,即實現封裝,把類的內部實現和外部表現分開,讓使用者無需瞭解類的實現細節;二是數據保護,將類的重要資訊保護起來,以免被其他程式不恰當地修改。2.1.2類成員的訪問許可權2023-11-16232.1.3成員函數的實現被隱藏的數據可以有成員函數來訪問,對數據的操作也體現在成員函數中:成員函數決定對象的操作行為。它是程式演算法的實現部分。它也是對封裝的數據進行操作的唯一途徑。它有兩種方式:類外實現和類內實現。注:數據對成員函數無法隱藏。2023-11-16241.一般實現方式類外實現成員函數的形式如下:返回值類型類名::成員函數名(形式參數表){

函數體}例2.2企業員工類的實現。sclass2_2_companyStaff.hs2_2\sclass2_2_companyStaff.cpps2_2\smain2_2.cpp2023-11-16252.內聯函數方式內聯函數是指程式在編譯時用函數的代碼替換每一處函數調用的地方。內聯的優點——以空間換時間。內聯的兩種方式:系統默認:代碼在類聲明中

例:sclass2_2_0_companyStaff.h函數前加關鍵字inline:inline<函數返回值類型><函數名>(<形參表>){<函數體語句>}

參見例:smain2_3.cpp。2023-11-1626內聯和宏替換宏替換是在編譯前由預處理程式進行預處理,它只做簡單的字元替換而不涉及語法檢查。而內聯函數是在編譯時處理的,編譯程序能識別內聯函數,對它進行語法檢查。2023-11-16272.2類與對象類(class):具有相同或相近的性質和行為的事物集合。對象(object):theinstanceofaclass。一個類的實例,即類的性質(變數)具體化之後成為對象。一個對象是類的一種特殊情況。一個類會有很多的對象,一個對象屬於一個類。它們是一對多的關係。2023-11-1628C++中的對象(一)當C++中類的成員變數被賦與特定的值之後,它即成為一個C++的對象。CCompanyStaffstaff;//一個對象變數staff.m_iStafNum=12345;staff.m_cName=“張三”

;staff成為類CCompanyStaff的一個對象,它有了一個工作編號,對應一個人名。當然,還會有很多其他的對象,比如staff1、staff2等等2023-11-1629C++中的對象(二)1、對象針對電腦而言,就是代碼中的一個變數。2、C++中對象變數的使用與C語言中結構體變數的使用方法一樣。CCompanyStaffstaff1,*pstaff1,staff3[10];staff1.m_iStafNum=11111;pstaffm_iStafNum=22222;staff[0].m_iStafNum=33333;2023-11-1630C++類的對象化(一)C++中對象是類的變數,但它不象變數那麼單純。C++提供一套特殊的機制實現類到對象的轉換。classCCompanyStaff{public:

CCompanyStaff();~CCompanyStaff();voidSetBasicSal(floatsal);//設置基本工資private:intm_iStaffNum;//工作編號};//END2023-11-1631對象的創建和銷毀創建對象時,“對象存放在何處??”需要向操作系統申請一定的記憶體空間用於存放新建的對象。為對象分配存儲空間主要有:靜態分配和動態分配兩種方式。2023-11-1632靜態分配:可以將對象或靜態成員存放在棧中或靜態存儲區域中。動態分配:

動態記憶體分配是指在堆(也稱自由記憶體)中分配存儲單元,即為對象動態從堆中分配記憶體。使用操作符new分配記憶體空間;使用操作符delete釋放記憶體空間。對象的創建和銷毀2023-11-16332.1.1構造函數與析構函數C++語言為了保證一個對象被初始化(類的實例化),定義了一組特殊的方法(函數),專門用於對象生成時的初始化。構造函數(constructor)——與類名稱相同,沒有返回值。它在對象生成之時自動執行。析構函數(destructor)——在類名前加~作為函數名的函數,沒有返回值,也沒有參數。它在對象結束時自動執行。2023-11-1634C++要求類設置一個專門的成員函數來負責類中所有對象的初始化,這個成員函數就是構造函數。構造函數的作用就是在對象被創建時利用特定的值構造對象,將對象初始化到一個特定的狀態。聲明一個構造函數的語示格式如下:public:類名(<參數表>);2.1.1構造函數與析構函數2023-11-1635構造函數可以由程式設計人員自己編寫。也可以由系統提供。例2.4構造函數舉例。sclass2_4_companyStaff.hsmain2_4.cpp2.1.1構造函數與析構函數2023-11-1636重載構造函數所謂重載構造函數,是指同一個構造函數名,具有不同的實現。例2.5在員工管理系統中,創建一個對象時,還可以一次性地給對象的姓名、出勤率、基本工資等幾個數據成員都賦初值,這就需要重載構造函數。sclass2_5_companyStaff.hsclass2_5_companyStaff.cppsmain2_5.cpp2023-11-1637需要注意一點,當構造函數帶默認參數時,要謹防出現歧義。例2.6下麵這個程式存在歧義。sclass2_6.hsmain2_6.cpp當創建對象d2時,有二義性。2.1.1構造函數與析構函數2023-11-1638拷貝構造函數拷貝構造函數是用來複製對象的一種特殊的構造函數。聲明拷貝構造函數的語法格式如下:class類名{public:

類名(類名&對象名);};只有一個參數2023-11-1639例2.7通過水準座標和垂直座標來確定螢幕上的一個點。sclass2_7_point.hsmain2_7.cppCPointb(a);CPointc=a;拷貝構造函數2023-11-1640析構函數析構函數與構造函數的作用幾乎正好相反,當一個對象消失時,或用delete刪除用new創建的對象時,系統都會自動調用類的析構函數,做一些清理工作。聲明一個析構函數的語法格式如下:classDemo{public: Demo(<參數表>); ~Demo(void);}2023-11-1641析構函數不能重載。

//析構函數

~CCompanyStaff(void) { cout<<"對象"<<m_cName<<"消亡"<<endl; }析構函數2023-11-1642當某對象消亡時,系統會自動調用該對象的析構函數。而且調用的順序是:最後創建的對象最先消亡,即最先調用其析構函數;相反地,最先創建的對象最後消亡,即最後調用其析構函數。 如果不顯式地定義析構函數,系統也會生成一個默認的析構函數,它是一個空的析構函數,不做任何事情。析構函數2023-11-1643C++類的對象化(附)classCCompanyStaff{public:

CCompanyStaff();CCompanyStaff(intnum,char*pName);~CCompanyStaff();voidSetBasicSal(floatsal);//設置基本工資……………};//ENDCCompanyStaffstaff(12345,“張三”);2023-11-1644C++類的對象化(附)1、構造函數與析構函數被“隱含”調用,即不管願意與否,它們都會被“強制”地執行。2、C++類都有缺省的構造函數,即沒有參數的構造函數。它也是“強制”的,沒有定義每個類也有一個這樣的構造函數。3、編程習慣:不管有用沒用,希望編程人員為每個類定義明確的構造與析構函數2023-11-16452.2.3對象成員的訪問可以通過對象名,也可以通過對象地址來訪問一個對象:<類名><對象名>; <對象名>.<成員名> //訪問公有數據成員<對象名>.<成員名>(<參數表>)

//訪問公有成員函數CCompanyStaffstaff1("LiHua"); staff1.SetBasicSal(4000.0);2023-11-1646<類名>*<對象指針名>;<對象指針>-><成員名> //訪問公有數據成員<對象指針>-><成員名>(<參數表>)

//訪問公有成員函數CCompanyStaff*pstaff;pstaff=newCCompanyStaff("LiuMei",0.95,3000.0);pstaff->GetName()

2.2.3對象成員的訪問2023-11-1647例2.8在員工管理系統中,建立兩個對象分別用兩種方式去訪問類成員。sclass2_8_companyStaff.hsclass2_8_companyStaff.cppsmain2_8.cpp2.2.3對象成員的訪問2023-11-1648對象指針:普通對象指針例2.9普通對象指針舉例。sclass2_9_objPointer.hsmain2_9.cpp

CPointerExam*pointer; pointer=&obj;

pointer->SetNum(2);對象指針在使用之前一定要初始化,為其動態分配存儲空間;使用完畢必須釋放該對象指針所代表的資源。2023-11-1649對象的this指針每個對象都擁有自己獨立的數據成員。而類中的所有對象使用相同的成員函數,成員函數在內存中只有一份。每個對象隱含了一個常量指針,稱為this指針,用於指向當前發送消息的對象,以識別當前調用成員函數的對象究竟是誰。當通過一個對象調用成員函數時,系統先將該對象的地址賦給this指針,成員函數在對對象的數據進行操作時,就隱含地使用了this指針。2023-11-1650CCompanyStaff(charcName[]) {

strcpy(m_cName,cName);}CCompanyStaff(charcName[],CCompanyStaff*constthis) {

strcpy(this->m_cName,cName);

}顯示指明this對象的this指針2023-11-1651CCompanyStaff(charcName[]) {

strcpy(this->m_cName,cName);

}隱含this對象的this指針2023-11-1652一般不需要顯示指出this,只有當函數需要返回當前對象自身的時候,才顯式地使用它。例2.10this指針舉例。sclass2_10_this.hsmain2_10.cpp

CSampleAdd(CSamples1,Csamples2) { this->n=s1.n+s2.n;

return(*this); }對象的this指針2023-11-1653類成員指針(1)類數據成員指針如果指針指向類數據成員的地址,則這個指針稱為類數據成員指針;<類型><類名>::*<指針名>

sclass2_11_dataPointer.hsmain2_11.cpp

intCSample::*p=&CSample::m; CSamples1; s1.*p=20;類數據成員指針只能指向公有數據成員。2023-11-1654(2)類成員函數指針如果指針指向類成員函數的地址,則稱為類成員函數指針。聲明一個類成員函數指針並為其賦值的語法格式如下:<類型>(<類名>::*<指針名>)(<參數表>)

<指針名>=<類名>::<成員函數名>;

使用指向成員函數的指針調用函數的格式如下:(*<指針名>)(<實參表>)

類成員指針2023-11-1655//主文件:smain2_12.cpp#include"sclass2_8_companyStaff.h"

//包含sclass2_8_companyStaff.h的代碼#include<iostream>usingnamespacestd;voidmain(void){ void(CCompanyStaff::*pFunc)(float);

//聲明一個類成員函數指針

CCompanyStaffstaff("LiHua");

//創建一個對象staff pFunc=CCompanyStaff::SetBasicSal;//指針初始化指向SetBasicSal函數

(staff.*pFunc)(3000);//相當於staff調用SetBasicSal() cout<<"員工LiHua的基本工資是"<<staff.GetBasicSal();}類成員指針2023-11-1656對象數組例2.13對象數組應用舉例sclass2_13_fruit.hsmain2_13.cpp

CFruitd[4];

//創建一個對象數組,相應調用4次構造函數如需要建立一個對象數組,必須滿足以下條件:至少有一個構造函數沒有參數或只帶默認參數。2023-11-1657Static靜態成員static(靜態)修改類中的成員(變數和函數),表明該成員只有一個副本,與具體的對象沒有關係。static後的成員變數可以用於對象間的數據共用。static後的成員函數只能使用static成員變數。2023-11-1658static成員的使用當一個成員被static修飾之後,它的使用不再屬於各個對象,而是屬於這個類。classCCompanyStaff{public:staticvoidSetBasicSal(floatsal);

intm_iStaffNum;};//ENDCCompany::m_iStaffNum=1234;CCompany::SetBasicSal(30000);2023-11-1659靜態成員類的靜態成員擁有一塊單獨的存儲區。該類的所有對象都共用這塊靜態存儲空間這就為對象提供了一個相互通信的方法。靜態成員由關鍵字static標識。它屬於類而不屬於對象。它分為靜態數據成員和靜態成員函數。2023-11-1660聲明一個靜態數據成員的語法格式如下:static<數據類型><靜態數據成員名>靜態數據成員在使用前也要初始化,但它的初始化不能在構造函數中進行,在類外進行。<數據類型><類名>::<靜態數據成員名>=〈初始值〉;其訪問語法格式如下:<類名>::<靜態數據成員名>靜態成員2023-11-1661sclass2_14_companyStaff.hsclass2_14_companyStaff.cppsmain2_14.cpp

staticints_iCount;intCCompanyStaff::s_iCount=1000;靜態成員2023-11-1662靜態成員函數是一種特殊的成員函數,它屬於整個類,也為同類中所有對象共同擁有。只要類存在,靜態成員函數就可以使用。可以通過類名和對象名來調用。其定義語法格式如下:<類名>::<靜態成員函數名>(<參數表>)例2.15靜態成員函數舉例。sclass2_15.hsmain2_15.cpp靜態成員2023-11-1663靜態成員函數一般不訪問普通數據成員,它的作用主要是訪問和操作同類中的靜態數據成員。類的普通成員函數都擁有this指針。而靜態成員函數沒有this指針,但可以通過類名或對象名來實現對它的訪問。靜態成員2023-11-1664對象封裝性的局限已經學習過類對於成員的封裝(public,private,protected)封裝性有效地保護了對象的內部細節,使得對象的使用和對象的實現分開,互相不產生影響。同時,封裝性帶來負面影響:1、C++為實現對象的封裝,必然會做一些額外的工作,從而導致程式的效率下降。2、一個對象封裝的太好,也會讓該對象很難使用,也很難實現。2023-11-1665突破對象封裝C++提供了友元(friend)來解決由封裝性帶來的問題。friend關鍵字修改函數或類,因此對於一個類而言,它有友元函數或友元類。friend用於應對編程中一些比較特殊的情況(如提高效率),絕大多數情況下不需要使用。亂使用只會使C++變成C,甚至更糟。2023-11-16665.友元關係一個類可以聲明一個友元關係,一起來共用類中的所有成員。友元如果是一個函數,則稱為友元函數;如果是一個類,則稱為友元類。友元函數是在類中由關鍵字friend修飾的非成員函數。友元函數可以是一個普通的函數,也可以是其他類的成員函數。雖然它不是本類的成員函數,但是在它的函數體中可以通過對象名訪問類的私有和保護成員。2023-11-1667例2.16友元函數與成員函數的比較。sclass2_16.hsmain2_16.cpp(例中的友元關係會發生錯誤)friendvoidFriendFunc(CSample*cp,inta)//增加一個對象指針參數{ cp->i=a;//對象指針參數為i指明當前所屬對象}需要操作對象5.友元關係2023-11-1668友元函數的特點:第一、友元函數可以直接訪問該類的所有成員,但它不是該類的成員函數,可以像普通函數一樣在任何地方調用。第二、友元函數不屬於任何類,因此可以放在類說明的任何位置,既可以在public區,也可以在private區。第三、友元函數不需要通過對象或對象指針來調用,可以直接調用即可。5.友元關係2023-11-1669友元類在類中可把另一個類聲明為友元類,如類B是類A的友元類,則類B中的所有成員函數都是類A的友元函數,都可以訪問類A的私有和保護成員。友元類的聲明語句如下:classB;//前向引用聲明classA{ …… friendclassB;//B為A的友元類

……}5.友元關係2023-11-1670例2.17編寫一個有關棧結構的程式,要求實現入棧和出棧。其中有兩個類,一個是結點類CNode,擁有結點值和指向下一結點的指針;另一個是棧類CStack,它擁有棧的頭指針。由此生成的鏈式結構如圖2.3所示。321頭指針棧頂棧底指向下一結點的指針5.友元關係2023-11-1671sclass2_17_stack.hsclass2_17_stack.cppsmain2_17由於棧類CStack是結點類CNode的友元類,所以棧CStack類的所有成員函數都成為類CNode的友元函數。因此Push()和Pop()可以訪問結點類CNode對象的私有成員並對其進行操作。封裝是對象與外界之間一堵不透明的牆,而友元恰好在這堵牆上開了一個小孔,它以犧牲資訊隱藏、削弱封裝性為代價來實現數據共用。5.友元關係2023-11-16722.3C++輸入輸出流對象標準輸入輸出流:數據從程式中流入到螢幕或磁片檔,即輸出流;數據從鍵盤流入到程式中,即輸入流。所謂流,是從源到矢的數據流的抽象引用,具體地說,就是數據從一個對象流向另一個對象。在進行I/O操作時,首先執行打開操作,使流和文件發生聯繫,建立聯繫後的檔才允許數據流入或流出,輸入輸出結束後,執行關閉操作使檔與流斷開聯繫。2023-11-16732.2.1標準輸入輸出流iosistreamostreamifstreamiostreamofstreamstreambuffilebufstringbuffstreambaseistream_withassignostream_withassigniostream_withassignfstreamC++流類庫C++提供了一個流類庫,它具有兩個平行的根基類:streambuf類和ios類。2023-11-1674streambuf類主要負責緩衝區的處理,提供對緩衝區的低級操作。ios類是流基類,它及其派生類提供用戶使用流類所需的介面,支持對streambuf的緩衝。由它可以派生出輸入流類istream和輸出流類ostream等。兩個根基類以及由它們派生出的所有流類(當然包括istream類和ostream類)都被定義在名為iostream的頭檔中。因此須用“#include”編譯指令將iostream頭檔包含進來。C++流類庫2023-11-16752023-11-1676系統預定義的一些流:進行輸入輸出常用的標準輸入流對象cin和標準輸出流對象cout,還有未被緩衝的標準錯誤輸出cerr和被緩衝的標準錯誤輸出clog。2.預定義輸入/輸出流cin和cout2023-11-1677cin是通用輸入流類istream_withassign的對象,與標準輸入設備連接。它通過重載運算符“>>”執行輸入操作,在流操作中將“>>”稱為提取運算符。cin從輸入流中取出數據,數據從提取運算符“>>”處流進程序。2.預定義輸入/輸出流cin和cout2023-11-1678cout是通用輸出流類ostream的對象,與標準輸出設備連接。通過重載運算符“<<”執行輸出操作,在流操作中,將“<<”稱為插入運算符。插入運算符“<<”向輸出流發送字元。實際上,位於插入運算符右側的字串被存儲在“<<”左側的流中。cout<<m<<""<<n<<endl;cout<<m;cout<<“

“;cout<<n;cout<<endl;2.預定義輸入/輸出流cin和cout2023-11-1679預定義操縱符操縱符是直接插入到流中的格式化指令,一般都定義在ios_base類中和<iomanip>頭檔中,分為帶參數和不帶參數兩類。2023-11-16802.預定義輸入/輸出流cin和cout例如:cout<<setw(5)<<n<<endl;表示設置輸出n時所占的位元組長度為5,輸出n後換行。2023-11-16812.3.2檔輸入輸出流輸入是指從磁片檔流向記憶體。輸出是指從記憶體流向磁片。

C++提供了3個檔流類:ofstream,ifstream,fstream,都定義在頭檔fstream.h中。其中fstream類以ofstream類和ifstream類為基類。ofstream類:輸出流類,用於向檔中寫入內容;ifstream類:輸入流類,用於從檔中讀出內容;fstream類:輸入輸出流類,用於既要讀又要寫的檔操作。2023-11-1682ofstream::ofstream(char*pFileName,intmode=ios::out,intprof=filebuf::openprot);第一個參數用於指業檔路徑及檔案名字串,第二個參數說明文件打開方式,第三個參數說明文件保護方式。例2.18給出程式的執行結果smain2_18.cpp檔流對象構造函數2.3.2檔輸入輸出流2023-11-16832.3.2檔輸入輸出流2023-11-16842.3.2檔輸入輸出流2023-11-1685(1)輸入流成員函數

open()close()get()getline()read()(2)輸出流成員函數put()write()2.常用輸入輸出流成員函數2023-11-16862.常用輸入輸出流成員函數例2.19下麵的程式將用戶輸入顯示到螢幕上,輸入字母y時輸出OK並結束。smain2_19.cpp

例2.20連續讀入一串字元,直到遇到字元q時停止,然後輸出這個字串。要求字元個數最多不超過79個。smain2_20.cpp

例2.21將英文字母及對應的ASCⅡ值輸出到螢幕上。smain2_21.cpp

2023-11-1687C++流的應用1、C++專門為互動式輸入與輸出定義了兩個對象:cin用於鍵盤的輸入;cout用於顯示幕的輸出。2、所有的C++流都有兩個操作符<<

和>><<:輸出(插入)操作符。流對象<<數據;>>:輸入(提取)操作符。流對象>>數據;2023-11-1688輸入輸出流應用舉例例2.22員工管理系統中的輸入輸出sclass2_22_companyStaff.hsclass2_22_companyStaff.cppsmain2_22.cpp2023-11-1689習題課後習題、作業習題1習題2習題42023-11-1690Thanks!TheEnd2023-11-1691派生類的構造函數與析構函數第三章繼承132繼承方式類的繼承與派生4虛基類5聚合類6繼承應用實例本章內容2023-11-1692

3.1類的繼承與派生3.1.1繼承與派生實例繼承是軟體重用的一種形式。繼承是將自然界中存在的普遍和特殊關係用程式設計的方式進行分類描述;在設計新類時,允許重用某個原有類的所有特徵,並在此基礎上添加新類的新特徵。被重用的原有類稱為基類baseclass而新創建的類稱為派生類derivedclass。派生類不會影響到原有類的結構。2023-11-1693實例:某一小型公司的實例企業員工工作編號姓名……顯示企業員工資訊計算實際發放工資……行政人員(與企業員工類相同)計算實際發放工資……經理公司總銷售額提成比例(其餘同企業員工類)計算實際發放工資……銷售人員個人銷售額提成比例(其餘同企業員工類)計算實際發放工資……2023-11-16943.1類的繼承與派生針對該公司的情況,先設計:一個一般員工類CCompanyStaff,代表員工的共性;讓三個新類分別繼承一般員工類CCompanyStaff,然後根據各自的差異性,新類可以對繼承來的內容進行改造。2023-11-16953.1.2派生類的定義從已有類產生新類的過程就是類的派生。定義語法如下:

class<派生類名><繼承方式>:<基類名1>, <繼承方式>:<基類名2>…{ <派生類成員的定義>; }3.1類的繼承與派生2023-11-1696繼承方式分為三種關鍵字:publicprivate

protected;單繼承(只有一個基類名)和多繼承;該公司情況類代碼:企業員工類相關派生類P79經理、銷售人員、行政人員這三個派生類在聲明語句中分別指定了它們的基類CCompanyStaff,以及它們的繼承方式-public;CSaleManager類,同時繼承經理類Cmanager和銷售人員類Csaleman;3.1類的繼承與派生2023-11-1697銷售員經理行政主管行政人員企業員工類圖3.1.3繼承的級別增加行政主管一職,員工關係圖如下:3.1類的繼承與派生2023-11-1698用C++語言描述三個類的前向引用聲明,形成了一個“類家族”:classCCompanyStaff;//基類員工//派生類行政人員繼承員工classCAdminStaff:publicCCompanyStaff;

//派生類行政主管繼承行政人員classCForeAdmin:publicCAdminStaff;基類不受派生類變化的影響;派生類繼承了基類的全部數據成員和除了構造函數析構函數之外的全部成員函數,但是派生類能否訪問這些成員還要受繼承方式的約束;3.1類的繼承與派生2023-11-16993.2繼承方式3.2.1公有繼承類的繼承方式有public、private和protected三種,其中private是缺省的繼承方式;公有繼承:

基類各成員的訪問許可權如果是public或protected則在派生類中保持不變而基類中的private成員對派生類不可見,如圖:2023-11-16100private基類Base圖3.3公有繼承中的訪問控制protectedpublicprivate派生類Derv:publicBaseprotectedpublicDervobjDBaseobjB3.2繼承方式2023-11-16101

例3.1一個公有繼承的例子:sclass3_1.hsclass3_1.cppsmain3_1.cpp

基類A的所有成員在派生類中的訪問控制權限都保持不變。3.2繼承方式2023-11-161023.2.2私有繼承:基類各成員無論是何種訪問許可權在派生類中一律以private的身份出現,連派生類的對象也無法訪問,只有派生類的函數能在類內訪問它們。若要私有繼承來的某些基類的成員函數在派生類中也能對外可見,需在派生類中對其成員公有化。

usingCAnimal::SetWeight;//在派生類中將基類的成員函數SetWeight()公有化

usingCAnimal::GetWeight;//在派生類中將基類的成員函數GetWeight()公有化例3.2私有繼承中的公有化:sclass3_2.hsmain3_2.cpp3.2繼承方式2023-11-16103私有繼承後的基類成員成為了派生類的私有成員,實際上相當於終止了基類功能的繼續派生。class<派生類名>:private<基類名>{public:

using<基類名>::<基類函數名>;//公有化

……};3.2繼承方式2023-11-161043.2.3保護繼承:

protected訪問許可權: 設計基類時,在隱藏成員的同時還要允許派生類的成員能訪問到,這時protected就比private更合適;

例3.3保護許可權舉例:sclass3_3.hsmain3_3.cpp3.2繼承方式2023-11-16105使用protected修飾類成員存在的隱患,它破壞了封裝;

protected繼承;

保護繼承下基類各成員的訪問許可權(除private外)都以protected許可權出現;比較私有繼承和保護繼承,可以看出在直接派生類中兩者的效果實際上都相同,但如果再繼續派生下去就會出現區別;3.2繼承方式2023-11-16106假設有A<-B<-C這樣一個繼承順序如果B私有繼承A後又派生出C那麼C就無法再間接繼承A的成員對A功能的繼承在B那兒就終止了;而如果B保護繼承A後又派生出C那麼A中的公有和保護成員在B中都是保護成員因此A的功能可以被C間接繼承;在實際開發時選擇恰當的繼承方式;3.2繼承方式2023-11-161073.2繼承方式3.2.4多繼承(多個父類):聲明多繼承的語法如下:

class<派生類名>:<繼承方式><基類名1>,<繼承方式><基類名2>,…多繼承舉例:在公司員工管理中有時會存在多繼承關係,如圖:經理銷售部經理銷售人員2023-11-161083.3派生類的構造函數和析構函數3.3.1構造函數:在創建派生類的對象時,系統執行派生類的構造函數而不會自動執行基類的構造函數;在設計派生類的構造函數時,不僅要為初始化派生類新增加的數據提供參數,而且還應為初始化基類數據成員提供參數相關語法:

<派生類名>(<形參表>):<基類名1>(<形參表1>)<基類名2>(<形參表2>)

{ <派生類新增數據成員的初始化> }2023-11-161093.3派生類的構造函數和析構函數派生類構造函數的調用順序為先父輩(基類數據)後自己(派生類新數據);轎車具有交通工具的一般特徵,比如輪胎數目,同時又具有轎車自身的特徵,如載客人數,因此轎車繼承交通工具,成為它的一個派生類。例3.4派生類構造函數舉例:sclass3_4.hsmain3_4.cpp2023-11-161103.3派生類的構造函數和析構函數派生類必須定義構造函數的兩種情況:派生類新增的數據成員需要定義構造函數來為其初始化;基類定義了帶參數的構造函數需要派生類為其提供參數以完成基類數據成員的初始化。2023-11-161113.3派生類的構造函數和析構函數3.3.2析構函數當派生類對象消亡時,系統會自動調用派生類的析構函數做一些必要的清理工作;由於在繼承過程中派生類不能繼承基類的析構函數,所以如果需要就只能在派生類中重新定義;2023-11-16112在執行派生類的析構函數時基類的析構函數也將被自動調用;析構函數調用的順序是先派生類的析構函數,然後是基類的析構函數。例3.5派生類析構函數舉例:

sclass3_5.hsmain3_5.cpp3.3派生類的構造函數和析構函數調用類A的構造函數調用類B的構造函數調用類B的析構函數調用類A的析構函數2023-11-161133.4虛基類3.4.1聲明一個虛基類

多繼承中的多義性

2023-11-161142.作用域分辨符可以通過作用域分辨符明確指定調用哪個類的介面;其語法形式如下:<派生類對象名>.<基類名>::<數據成員名>//訪問數據成員<派生類對象名>.<基類名>::<成員函數名><參數表>//訪問成員函數3.4虛基類2023-11-16115作用域分辨符舉例:Smain3_5_B.cpp基類可以將其設為虛基類,這樣它的數據成員在內存中就只有一個副本,成員函數也只有一個映射從而解決了同名成員的唯一標識問題;3.4虛基類2023-11-161163.虛基類由virtual標識,聲明語法如下:class<派生類名>:virtual<繼承方式><基類名>;例3.6虛基類舉例:sclass3_6.hsmain3_6.cpp考慮員工管理系統中的多繼承關係使用虛基類來定義公司員工管理系統中的多繼承關係:3.4虛基類2023-11-16117//在繼承路徑“銷售經理—>銷售人員—>員工”中將基類CCompanyStaff設為虛基類classCSaleman:virtualpublicCCompanyStaff;//在繼承路徑“銷售經理—>經理—>員工”中將基類CCompanyStaff設為虛基類classCManager:virtualpublicCCompanyStaff;//派生類CSaleManager成為兩條繼承路徑的交匯點classCSaleManager:publicCSaleman,publicCmanager;3.4虛基類2023-11-16118比較使用作用域分辨符和虛基類技術:使用作用域分辨符時基類的成員在內存中存在多個副本,通過指定基類名來唯一標識使用的是哪個副本,這樣可以存放不同的數據進行不同的操作;使用虛基類技術使基類的成員在內存中只存有一個副本;3.4虛基類2023-11-161193.4.2虛基類的初始化:虛基類的初始化在語法上和處理一般基類一樣只是調用順序略有不同它遵循以下順序:1)虛基類的構造函數在非虛基類之前調用;2)若同一層次中包含多個虛基類則按它們的聲明順序調用;3)若虛基類由非虛基類派生而來要先調用更高級別基類的構造函數再遵循上述1和2的順序。

3.4虛基類2023-11-16120虛基類初始化順序的簡單舉例:classA;classB;classC:publicA,virtualB

{};將產生如下的調用次序B()

A()

C()3.4虛基類2023-11-16121較複雜的虛基類初始化舉例:

3.4虛基類2023-11-16122為了避免在不同繼承路徑中繼承來的同一成員發生多個副本的衝突可以將相應的基類設為虛基類,見源代碼:sclass3_7.hsmain3_7.cpp運行結果:classBase1classBase2classLevel2classBase2classLevel1classLeaf3.4虛基類2023-11-161233.5聚合類3.5.1聚合類的概念設計孤立的類是較容易的,難的是正確設計基類及其派生類;一般地如果在邏輯上A是B的一部分而A與B又不屬於同一類範疇,則不允許B繼承A的功能而是要用A和其他東西組合出B;聚合(aggregation)也是實現程式代碼重用的另一有效手段;類的聚合,就是指在一個類中內嵌其他類的對象作為成員的現象;“擁有”(hasa)關係,聚合類擁有內嵌對象;出於資訊隱藏的考慮,常將其訪問許可權設為“私有”。2023-11-161243.5.2聚合類中的構造函數聚合類對象在創建時作為其組成部件的內嵌對象將首先被系統創建;如果一個聚合類,同時又是一個派生類,那麼它的構造函數初始化列表還應負責為基類構造函數的調用提供參數;當一個類既是聚合類又是派生類時的構造函數定義形式:

<類名>::<類名><形參表><基類><形參表><內嵌對象><形參表> { 類的初始化 }3.5聚合類2023-11-16125例3.8公司的每一個員工都擁有不同的教育背景,其中記錄著畢業學校、最高學歷等基本數據。可以將教育背景抽象為一個類CEducation,擁有上述數據,並提供查詢數據和顯示數據等基本功能。企業員工類CCompanyStaff要使用教育背景類CEducation的功能,但是前者不能繼承後者所具有的特徵,因此可以把CCompanyStaff類處理成一個聚合類,在類中內嵌一個Ceducation類的對象。員工管理系統中的聚合關係:sclass3_8_companyStaff.hsclass3_8_companyStaff.cppsmain3_8.cpp

3.5聚合類2023-11-161263.6繼承應用實例2.6.1問題描述:該公司的員工組成很簡單,主要分成經理,銷售部門經理,銷售人員和行政人員等幾類;所有員工都具有姓名,工作編號,基本工資,獎金,當月出勤記錄等數據,都擁有一定的教育背景;每個員工都存在錄入和顯示資訊等操作,並且需要根據考勤等實際因素來發放工資;不同類別的員工計算工資的辦法也各不相同;

例3.9公司員工管理系統,根源程式:sclass3_9_companyStaff.hsclass3_9_companyStaff.cppsmain3_9.cpp2023-11-16127習題課後習題、作業習題1習題2習題32023-11-16128Thanks!

TheEnd2023-11-16129函數重載第四章多態性132虛函數多態性4運算符重載內容提要2023-11-161304.1多態性基本概念及其實現方式多態性就是一個事物多種形態,就是同一符號或者名字在不同情況下具有不同解釋的現象。多態性有兩種表現形式:一種是不同的對象在收到相同的消息時,產生不同的動作;另一種是同一對象收到相同的消息卻產生不同的函數調用。2023-11-161314.1.2多態的兩種實現方式兩種表現形式分別叫做:編譯時多態和運行時多態。(重載)編譯時多態——也叫靜態多態性,屬於早期綁定,在編譯時就實現了綁定,它是靜態聯編的;運行時多態——也叫動態多態性,屬於晚期綁定,在編譯時還無法確定綁定對象,只有在運行時才能夠實現綁定,它是動態聯編的。通過虛函數實現,動態4.1多態性基本概念及其實現方式2023-11-16132以下兩種情況不是動態聯編的:1、在基類中未使用虛函數、純虛函數。2、在基類中使用了虛函數和純虛函數,但使用對象直接調用。這也不是動態聯編的。使用虛函數、純虛函數,使用基類的指針或者引用。

4.1多態性基本概念及其實現方式2023-11-16133“綁定”——就是讓函數調用與函數體產生關聯。在編譯時就確定——叫“早期綁定”;在程式運行時才確定——叫“晚期綁定”。而C++的多態性在“早期綁定”和“晚期綁定”兩方面都有體現。4.1多態性基本概念及其實現方式2023-11-161344.2虛函數虛函數是在基類中使用了關鍵字virtual的成員函數。慮函數與一般函數重載的區別:1、虛函數定義在基類和派生類中,函數原型完全一致;2、函數重載在同一個類中,或者都在類外定義,函數原型必定不完全相同。虛函數——運行時多態2023-11-161354.2.1虛函數的基本概念及其定義主要通過例子程式說明其機制.4.2虛函數2023-11-16136例4_1:利用對象分別調用不同的Show()——正確調用了特定的Show()函數。s4_1\sclass4_1_student.hs4_1\sclass4_1_student.cpps4_1\smain4_1.cppCPersonoCPerson("德華劉","男"); CStudentoCStudent(20050101,"學友張","男");oCPerson.Show(); oCStudent.Show(); 4.2虛函數2023-11-16137在main()中通過“對象.成員函數”的形式調用show(),分別調用了對象自己的show()。劉德華|男張學友|男|200501014.2虛函數2023-11-16138下麵通過基類指針來調用,會有什麼結果啦?例4_2:利用基類的指針調用不同的Show()。s4_2\smain4_2.cppch4_2\sclass4_2_student.hch4_2\sclass4_2_student.cpp劉德華|男張學友|男4.2虛函數無論讓基類的指針指向基類對象還是派生類對象,系統都無法調用派生類對象oCStudent的Show()函數。實際調用的都是基類的Show()成員函數。

2023-11-16139通過基類的引用,引用派生類對象,會有什麼結果啦?例4_3:利用基類的引用作為參數,調用不同的Show(),依然未達到目的。

s4_3\smain4_3.cppch4_3\sclass4_3_student.hch4_3\sclass4_3_student.cpp劉德華|男張學友|男4.2虛函數2023-11-16140無論引用基類對象還是派生類對象,函數內調用的都是基類的Show()成員函數。通過基類的引用去引用派生類對象,只能看到派生類從基類中繼承而來的部分。這是由於C++的靜態聯編機制造成的。它首先將指向基類的指針與基類成員函數Show()連接在一起。這樣,不管pCPerson指向哪個對象,pCPerson->Show()調用的總是基類的成員函數Show()。4.2虛函數2023-11-16141C++提供了虛函數(virtualfunction)機制解決上述問題。通過虛函數機制,實現了動態聯編。一旦基類的成員函數定義為虛函數,則其派生類的同名成員函數(原型一致)不管前面是否加關鍵字virtual,同樣具有虛特性,同樣是虛函數。虛函數的傳遞性4.2虛函數2023-11-16142動態聯編在多繼承中尤其有用。但虛函數機制也是有缺陷的,為了實現虛特性需要增加一些數據存儲和執行指令的開銷,虛函數的使用也不是越多越好。將一個類中的所有成員函數都定義為虛函數(virtual)也是可行的,它除了會增加一些額外的開銷外,沒有其他更多的壞處,虛函數對於保證類的封裝特性是有好處的。4.2虛函數2023-11-16143虛函數的“動態聯編虛特性”必須通過基類的指針或者基類的引用來表現。例4_4:定義了兩個CPerson的繼承類,具有虛函數的兩個實現版本。s4_4\sclass4_4_student.hs4_4\sclass4_4_student.cpps4_4\smain4_4.cpp虛函數要求原型一致4.2虛函數2023-11-161441:-通過基類的引用調用具有虛特性----德華劉|男學友張|男|20050101孔老師|男|50002:-通過基類指針調用具有虛特性----德華劉|男學友張|男|20050101孔老師|男|50003:-通過對象調用則不具有虛特性----德華劉|男學友張|男|20050101孔老師|男|50004.2虛函數2023-11-16145在例4_4中,其他代碼不變,將CPerson類聲明部分的“virtualvoidShow()const;”函數定義語句去掉關鍵字virtual;改為“voidShow()const;”請問程式的運行結果會如何?4.2虛函數2023-11-161461:-通過基類引用調用(不)具有虛特性---德華劉|男學友張|男孔老師|男2:-通過基類指針調用(不)具有虛特性---德華劉|男學友張|男孔老師|男3:-通過對象調用則不具有虛特性---德華劉|男學友張|男|20050101孔老師|男|50004.2虛函數2023-11-16147虛函數的定義要遵循以下幾條重要規則:1、原型不一致,即使加上了關鍵字virtual,也不會進行滯後聯編。2、類的成員函數才能說明為虛函數。3、靜態成員函數不能是虛函數,靜態成員函數是不受某個對象的限制,它屬於類。虛函數必須是成員函數4.2虛函數2023-11-161484、內聯(inline)函數不是虛函數,內聯函數不能在運行時動態確定位置,它在編譯時採用插入函數體的方式處理。。5、構造函數不虛函數,構造對象的時候,對象還是一片未定型的空間,只有對象構造完成後,對象才是具體類的實例。6、析構函數可以是虛函數,而且通常將析構函數聲明為虛函數。有兒子,則其析構函數需要是虛函數4.2虛函數2023-11-161494.2.2虛函數與重載函數的關係1、重載函數函數名稱相同,參數不同;重載函數是在作用域相同的區域裏定義的相同名字的不同函數.2、虛函數函數原型完全一致,體現在基類和派生類的類層次結構中。3、重載函數可以是成員函數或友員函數,而虛函數只能是成員函數;4、調用重載函數以所傳遞參數序列的差別作為調用不同函數的依據;虛函數則根據對象的不同調用不同類的虛函數;5、重載函數在編譯時表現出多態性,是靜態聯編;而虛函數在運行時表現出多態性,是動態聯編,動態聯編是C++的精髓。4.2虛函數2023-11-161504.2.3虛函數與函數隱藏和函數覆蓋基類和派生類中的原型一致的函數,如果是虛函數,則稱為覆蓋;如果原型一致的函數不是虛函數,就稱為隱藏。原型不一致的話,不管是否虛函數,都稱為隱藏。虛函數才有可能覆蓋4.2虛函數2023-11-16151覆蓋和隱藏適用下述規則(基類和派生類):

1、完全同名,參數不同,不論是否虛函數,基類的函數將被隱藏。2、如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數不是虛函數。此時,基類的函數被隱藏。3、如果派生類的函數與基類的函數同名,並且參數也相同,基類函數也是虛函數。此時,基類的函數被覆蓋了。4.2虛函數2023-11-16152另外,當基類中的虛函數和派生類中的函數原型出現不同時,將按照以下兩種情況處理:1、僅僅返回值類型不同,其餘均相同,系統會當作錯誤處理。僅僅返回值不同的函數本質上是含糊的。2、函數原型不同,僅函數名相同。此時系統將認為它是重定義函數,將丟失虛特性,且該函數還會隱藏基類同名函數。4.2虛函數2023-11-16153例4_5:重載、隱藏和覆蓋。

s4_5\sclass4_5_student.hs4_5\sclass4_5_student.cpps4_5\smain4_5.cpp函數名相同,原型不同的虛函數或非虛函數,在派生類中都被隱藏;原型相同的虛函數在派生類中被覆蓋;原型相同的非虛函數在派生類中被隱藏。4.2虛函數2023-11-161544.2.4多重繼承中的虛函數虛函數具有傳遞性,具有虛函數的基類指針可以指向它的派生類,也可以指向它的派生類的派生類,它們都具有動態特性。為了實現多重繼承中虛函數的傳遞性,在派生時,需要採用public繼承方式,否則無法實現

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论