P05面向?qū)ο笤O(shè)計思想_第1頁
P05面向?qū)ο笤O(shè)計思想_第2頁
P05面向?qū)ο笤O(shè)計思想_第3頁
P05面向?qū)ο笤O(shè)計思想_第4頁
P05面向?qū)ο笤O(shè)計思想_第5頁
已閱讀5頁,還剩78頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

面向?qū)ο蟪绦蛟O(shè)計江漢大學(xué)數(shù)學(xué)與計算機科學(xué)學(xué)院韓海程序設(shè)計思想1主要的程序設(shè)計理念運算符重載——針對對象實現(xiàn)類似于原運算符的功能繼承與組合——如何利用已有的類建立更復(fù)雜的對象虛函數(shù)——指針指向類族中的哪個對象,就調(diào)用該對象相應(yīng)的方法成員抽象類——類中只規(guī)定有哪些行為,但并不具體實現(xiàn)多態(tài)——讓同一種行為應(yīng)用于各種對象、各種情況模板——提高編碼的效率異常處理——設(shè)置錯誤處理代碼,應(yīng)對可能出現(xiàn)的錯誤const——保護數(shù)據(jù),防止被意外破壞流——把不同類型的信息視為一個數(shù)據(jù)序列泛型——忽略數(shù)據(jù)元素在存儲上的差異,強調(diào)行為上的共性,強化代碼的通用性2類是規(guī)定行為的集合 基本類型 自定義類集合示例 int

CMyEmployee元素示例 15 2010053134,張三,男,1982.3.5.

運算 算術(shù)運算 規(guī)定的行為(方法)

類是基本數(shù)據(jù)類型的擴展,兩者的核心都是集合及集合上的運算,基本類型的運算表現(xiàn)為“+”、“-”、“*”、“/”等算術(shù)運算,是固有運算,可直接使用。定義類中包含哪些數(shù)據(jù)元素相對簡單。類的核心問題是定義類的方法成員,通過方法成員表現(xiàn)出類的元素(對象)具有哪些行為。3對象的屬性與操作對象的行為通??梢苑譃橛嬎泐惡头怯嬎泐悺7怯嬎泐惏@示、存儲、傳輸?shù)刃袨?,比如關(guān)于圖形處理建立的區(qū)域類族大體上會有三角形、方形、多邊形等自定義類,各個對象都有在屏幕上畫出該形狀這一行為,而且通常都用同一個成員名來描述這個行為,如“對象.Draw()”。這一現(xiàn)象將在下一節(jié)“抽象類”當(dāng)中討論。

計算類行為是指獲取對象的相關(guān)信息,或者通過對象及其它數(shù)據(jù)計算得到一些信息。比如上述關(guān)于區(qū)域的類族,各個對象通常都需要對外公布(允許外部訪問,public)寬度和高度兩個數(shù)據(jù),這樣的信息稱為“對象的屬性”,通常也有相對固定的成員名,例如取對象的寬度通常寫作“對象.GetWidth()”。這一類行為當(dāng)中包含運算符重載。4為什么要有運算符重載?起因(在上一單元已述,重現(xiàn)):

(1)常規(guī)的運算符只運用于基本數(shù)據(jù)類型,并且有固定的用法,基本上與數(shù)學(xué)上的用法一致?!糜?/p>

(2)類是數(shù)據(jù)與處理(稱為“方法”)的結(jié)合體,有很多處理與常規(guī)的運算有直接聯(lián)系,或者是常規(guī)運算在意思上的延伸,比如CString類的加法延伸為拼接。

——好理解

(3)設(shè)計一個用于實現(xiàn)加法的函數(shù)Add,帶有兩個入口參數(shù)分別記作a和b,則調(diào)用該函數(shù)寫作“Add(a,b)”,這一寫法當(dāng)然不如“a+b”更簡潔易懂?!脤懩繕耍?/p>

希望為類設(shè)計一個與常規(guī)運算符在意義上接近的處理或者計算,并且沿用常規(guī)運算符原有的寫法5運算符重載方式重載方式用成員函數(shù)重載用友元函數(shù)重載 特 點“=”等少量的幾個運算符必須用成員函數(shù)的方式重載重載二元運算符時,右側(cè)的操作數(shù)可以是各種類型,但左側(cè)的操作數(shù)必須是類的對象成員函數(shù)的形參數(shù)目等于運算符操作數(shù)的數(shù)量減1(1)通常用全局函數(shù)實現(xiàn)(2)需要訪問類的私有成員時才需要定義成友元函數(shù)6不能重載的運算符 以下運算符不能被重載:

成員訪問

.

域限制符 ::

條件運算 ?: 取字節(jié)數(shù) sizeof7經(jīng)常被重載的運算符運算符 分類 重載方式+,-,*,/,% 算術(shù)運算 兩種均可>,>=,<,<=,==,!= 關(guān)系運算 兩種均可= 賦值 成員函數(shù)+=,-=,*=等 復(fù)合賦值 兩種均可,建議成員函數(shù)++,-- 自增自減 兩種均可,建議成員函數(shù)[],-> 下標,指向 成員函數(shù)<<,>> 移位/流 兩種均可new與delete 內(nèi)存管理 兩種均可特別地,C++規(guī)定圓括號“()”也可以重載已述,本節(jié)將只講解++、[]和()這幾個運算符的重載8為CClock類定義前置“++”前述CClock類的聲明如下:classCClock

{public:

CClock(int

h,int

m,ints);

~CClock();

int

GetHour(); //取小時數(shù)

int

GetMinit(); //取分鐘數(shù)

CString

GetTime(); //取當(dāng)前時間字符串

voidStepup(); //令時鐘走一步(1秒)

CClockoperator++();private:

int

m_hour,m_minit,m_second;//時分秒};CClock類的對象是時鐘,“++”的功能顯而易見。用成員函數(shù)為CClock類定義前置“++”,首先需要在類中添加相應(yīng)的函數(shù)聲明。注意,類中已有Stepup函數(shù)可以利用9編寫“operator++”利用已有的Stepup函數(shù),前置自增功能很容易實現(xiàn):CClock

CClock::operator++(){ Stepup(); return*this;}C++以“是否有形參”來區(qū)分前置自增和后置自增,后置自增的重載函數(shù)如下:CClock

CClock::operator++(int){ CClockx(*this); //創(chuàng)建臨時對象

Stepup(); //即this->Stepup() returnx;}注意:(1)后置自增函數(shù)帶有一個int型形參,這是區(qū)分前置后置的標記,并無其它含義,函數(shù)體內(nèi)也不使用該參數(shù)值,甚至可以沒有形參的名字;(2)不要忘記在類中添加相應(yīng)的函數(shù)聲明10友元函數(shù)定義前置“++”CClock類的聲明如下:classCClock

{public:

CClock(int

h,int

m,ints);

~CClock();

int

GetHour(); //取小時數(shù)

int

GetMinit(); //取分鐘數(shù)

CString

GetTime(); //取當(dāng)前時間字符串

voidStepup(); //令時鐘走一步(1秒)

friendCClock

operator++(CClock&x);private:

int

m_hour,m_minit,m_second;//時分秒};CClock

operator++(CClock

&x){

c.Stepup(); returnx;}11友元函數(shù)定義前置“++”這是前置自增的友元函數(shù)聲明:

friendCClock

operator++(CClock

&x);后置自增的友元函數(shù)聲明為:

friendCClock

operator++(CClock

&x,int);相應(yīng)的函數(shù)代碼:CClock

operator++(CClock

&x,int){

CClock

m(x);//記得嗎,這將調(diào)用拷貝構(gòu)造函數(shù)

c.Stepup(); returnm;}最初編寫代碼的時候忘記了引用,沒有引用是不行的12為CClock定義下標[]設(shè)x是CClock類的對象,即一個時鐘,原本x[?]是沒有意義的,但不妨人為地做如下定義:

x[0]---時鐘的當(dāng)前小時數(shù)

x[1]---時鐘的當(dāng)前分鐘數(shù)

x[2]---時鐘的當(dāng)前秒數(shù)

x[i]----1,i不是0、1、2時根據(jù)運算符重載的有關(guān)規(guī)則,下標運算“[]”必須用成員函數(shù)實現(xiàn),則在寫法上“x[i]”是函數(shù)調(diào)用“x.operator[](i)”的變形,“k=x[i]”也可以寫成:

k=x.operator[](i)13為CClock類定義“[]”在CClock類中添加關(guān)于[]的成員函數(shù)如下:classCClock{public:

CClock(int

h,int

m,ints);

~CClock();

int

GetHour(); //取小時數(shù)

int

GetMinit(); //取分鐘數(shù)

CString

GetTime(); //取當(dāng)前時間字符串

voidStepup(); //令時鐘走一步(1秒)

CClockoperator[]();private:

int

m_hour,m_minit,m_second;//時分秒};14CClock類的“operator[]”int

CClock::operator[](inti){

if(i==0) returnm_hour;

if(i==1) returnm_minit;

if(i==2) returnm_second; return-1;};15重載“[]”的意義設(shè)x是一個對象,通過運算符重載,可以把原本沒有意義的寫法“x[?]”賦予確定的含義,這與前述的運算符重載有著重大差異。

既然如此,對于任意一個允許重載的運算符,也可以賦予它與原運算符完全沒有關(guān)聯(lián)的功能。但是極少有人這樣用,因為運算符重載的目的在于借用運算符原有的含義、用原有的寫法實現(xiàn)對象的某些處理功能,如果這些功能與被重載的運算符相去甚遠,則重載的效果只會讓人造成概念上的混亂,不利于軟件開發(fā)。正如在定義一個函數(shù)時,通常都以與函數(shù)功能相關(guān)的英文單詞或者編寫作為函數(shù)名。16CClock類的“operator()”對于對象x,運算符重賦予了“x[?]”一定的意義,對于同樣原本沒有意義的“x(?)”也可以依照此例處理。實際上,把對“[]”重載時所有的“[”換成“(”、所有的“]”換成“)”,前述代碼同樣可以編譯通過。很多資料上把“operator()”稱為函數(shù)調(diào)用運算符,這容易在概念上造成混亂。建議:不用管它叫什么名字,因為對于對象x而言,“x(?)”原本沒有意義,現(xiàn)在通過運算符重載規(guī)定了該寫法的含義。17繼承還是組合面向?qū)ο蟪绦蛟O(shè)計的基本設(shè)計單位是“類”,類的本質(zhì)是規(guī)定了對象的數(shù)據(jù)信息和行為。設(shè)計更復(fù)雜的程序時,可以利用已有的類,在利用方法上就有了本頁的標題:繼承還是組合?——本節(jié)只考慮public繼承設(shè)X和Y是兩個類,x是X的對象,y是Y的對象繼承(“isa”關(guān)系)——如果X是Y的派生類,則:

xisay. (見下頁圖元類族示例)組合(“hasa”關(guān)系)——如果y是x的一個子對象,則:

xhasay.例如:CMyEmployee類中包含兩個CString子對象,

Theemployeehasanumber. Theemployeehasaname.18繼承描述“isa”關(guān)系Point(點)isaelement(圖元)Rigion(區(qū)域)isaelement(圖元)Circle(方形)isaRigion(區(qū)域)...... 圖元顏色,尺寸,邊界畫圖,擦除,取邊界點線起點,終點,線型區(qū)域填充模式,透明度直線弧線......曲線......方形......圓形......多邊形......注意,不論是繼承還是組合,在x中都包含一個y的對象作為x的一部分!繼承與組合到底有什么差別?19繼承與組合的差異xisay(繼承)(1)編寫X類的成員函數(shù)代碼時,可以訪問y的public成員和protected成員,包括方法成員也包括數(shù)據(jù)成員(2)在類的外部編寫代碼時,可以訪問x的public成員,也能訪問y的public成員(3)CX的構(gòu)造函數(shù)以“:CY(...)”指明如何調(diào)用基類的構(gòu)造函數(shù)xhasay(組合)(1)編寫X類的成員函數(shù)代碼時,只能訪問y的public成員,包括方法成員和數(shù)據(jù)成員(2)在類的外部編寫代碼時,只能訪問x的public成員(3)CX的構(gòu)造函數(shù)以“:子對象名(...)”指明如何調(diào)用子對象的構(gòu)造函數(shù)20測試1,派生類內(nèi)部CY類的聲明:classCY{public:f1();

intd1;protected:f2();

intd2;private:f3();

intd3;}CX類的聲明:classCX:publicCY//繼承{public:f_x1();

intd_x1;protected:f_x2();

intd_x2;private:f_x3();

intd_x3;}編寫CX的函數(shù)代碼時,允許訪問CX和CY的哪些成員?21測試2,派生類外部CY類的聲明:classCY{public:f1();

intd1;protected:f2();

intd2;private:f3();

intd3;}CX類的聲明:classCX:publicCY//繼承{public:f_x1();

intd_x1;protected:f_x2();

intd_x2;private:f_x3();

intd_x3;}x是CX的對象,“x.?”是合法的訪問?22測試3,組合類內(nèi)部CY類的聲明:classCY{public:f1();

intd1;protected:f2();

intd2;private:f3();

intd3;}編寫CX的函數(shù)代碼時,允許訪問CX和CY的哪些成員?CX類的聲明:classCX //組合{public:f_x1();CYy1;protected:f_x2();CYy2;private:f_x3();CYy3;}訪問CY的成員時,必須指明訪問哪一個子對象的成員,比如“y1.f1()”、“y2.f1()”、“y3.d1=5”23測試4,組合類外部CY類的聲明:classCY{public:f1();

intd1;protected:f2();

intd2;private:f3();

intd3;}CX類的聲明:classCX //組合{public:f_x1();CYy1;protected:f_x2();CYy2;private:f_x3();CYy3;}x是CX的對象,“x.?”是合法的訪問?x.f_x1() x.y1.f1() x.y1.d1=5;24多用組合少用繼承如本頁標題所示,在建立新的類時,多用組合少用繼承。并且把子對象置于private保護之下。原因:面向?qū)ο蟮暮诵乃枷胫皇欠庋b,即允許外部訪問對象的哪些成員。以繼承的方式建立新的類,編寫代碼的人往往容易忽略可以從外部訪問基類的public成員,從而導(dǎo)致一些預(yù)料之外的信息暴露。當(dāng)然,最根本的還是根據(jù)“isa”還是“hasa”關(guān)系來選用繼承和組合。25繼承導(dǎo)致類族轉(zhuǎn)換話題:面向?qū)ο蟮某绦蛟O(shè)計模式中經(jīng)常會設(shè)計一系列有繼承與派生關(guān)系的類,從一個基類開始往下派生出的所有的類形成一個“類族”。類族的設(shè)計思想顯然是為了代碼重用。在一個類族中,對象盡管屬于不同的類,但通常都有一些相同的特征或者行為。即:類族中任意一個類的對象都擁有基類的數(shù)據(jù)成員(雖然有可能因為繼承方式而不能訪問),視作具有相同的特征信息;類族中的對象通常也具有一些同名的函數(shù)成員,視作具有相同的行為,這些函數(shù)的功能相同或相似,但實現(xiàn)的具體代碼可以不同。26類族對象的共性例如,以“圖元”為基類的類族具有顏色、尺寸等共同的數(shù)據(jù)成員,以及畫、擦等同名的方法成員;除此之外,以“區(qū)域”為基類的類族還具有填充模式、透明度這兩個共同的數(shù)據(jù)成員。圖元顏色,尺寸,邊界畫圖,擦除,取邊界點線起點,終點,線型區(qū)域填充模式,透明度直線弧線......曲線......方形......圓形......多邊形......27對圖元類族確定名稱為了后續(xù)敘述的方便,為各個類及各個成員命名CElementcolor,size,borderDraw,Erase,...CPointCSegmentstart,end,styleCAreapattern,transparencyCLineCArc......CCurve......CSquare......CCircle......CPolygon......28創(chuàng)建對象與調(diào)用方法設(shè)有如下的創(chuàng)建對象:

CElementx1; //圖元

CArea x2; //區(qū)域

CSegmentx3; //線

CCirclex4; //圓則,以下調(diào)用非常明確是調(diào)用哪個對象的哪個方法:

x1.Draw(); x2.Draw(); x3.Draw(); x4.Draw();原本沒有疑問的用法會因為指針而產(chǎn)生歧義,見下頁。29重要規(guī)則C++規(guī)定:

派生類的地址可以賦值給指向基類的指針變量你如何理解這個規(guī)則?請先看下面的例子為了說明問題的簡便,改用下面的最簡單的類聲明:classCA{public: voidfun() {cout<<"FunofCA\n";};};classCB:publicCA{public: voidfun() {cout<<"FunofCB\n";}; voidfb(){cout<<"Thisisfb\n";};};記住各函數(shù)的顯示效果!30類族中的指針指向指出下面代碼中的錯誤:main(){ CAx,*p; CBy,*q; p=&y; q=&x; p->fun(); p->fb(); q->fun();}派生類地址可以賦值給指向基類的指針變量,反之不行這一行是對的,不用寫“p=(CA*)&y;”p的類型決定了通過p只能訪問CA的成員,fb是CB的成員但不是CA的成員31指針指向類族中的對象指出下面代碼中的運行結(jié)果:main(){ CAx,*q; CBy;

x.fun(); q=&x; q->fun();

y.fun(); q=&y; q->fun();}最后一行顯示值得探討:q是指向基類CA的指針變量,但此時指向派生類對象y,“q->fun()”到底調(diào)用哪個函數(shù)?另外,編程者希望它調(diào)用哪個函數(shù)?32再談規(guī)則上述規(guī)則的意義:(1)因為代碼重用而產(chǎn)生了類族,類族中的對象都有相同或者相似的行為。在編程者看來,類族中的對象都是“差不多”的。(2)編程者希望借用一個指針變量,不論該指針指向類族中的哪一個對象,都能正確地訪問相應(yīng)的成員。如果派生類對基類的某個方法成員編寫了新代碼(覆蓋),則希望指針指向派生類對象時,能夠訪問新代碼。(3)以“基類*q”定義指針變量,則通過q只能訪問基類的成員,而不能訪問派生類新增的成員,從而保證不會出現(xiàn)訪問一個不存在的成員的現(xiàn)象。派生類地址可以賦值給指向基類的指針變量,反之不行前述示例說明沒能做到這一點33虛函數(shù)虛函數(shù)專門用于解決上述問題:如前例,基類中定義了方法成員fun,派生類更新了該方法。以“CA*q;”定義指針變量,希望當(dāng)q指向基類CA的對象時,“q->fun()”訪問基類CA的成員函數(shù)fun;當(dāng)q指向派生類CB的對象時,“q->fun()”訪問派生類CB更新之后的成員函數(shù)fun。簡言之:希望q指向誰就調(diào)用誰的成員函數(shù)對于上述需求,需要在基類中把該方法成員定義成“虛函數(shù)”。聲明虛函數(shù)的格式如下:

virtual

返回值類型函數(shù)名(形參表);34聲明虛函數(shù)下面是定義CA和CB兩個類,在適當(dāng)?shù)奈恢眉由咸摵瘮?shù)標記virtual:classCA{public: voidfun() {cout<<"FunofCA\n";};};classCB:publicCA{public: voidfun() {cout<<"FunofCB\n";}; voidfb(){cout<<"Thisisfb\n";};};virtual35有關(guān)虛函數(shù)的說明類的靜態(tài)成員函數(shù)和內(nèi)聯(lián)函數(shù)不能聲明為虛函數(shù)定義成員函數(shù)為虛函數(shù),并不代表該函數(shù)是“虛”的,而是為了通過指針變量能夠訪問正確的方法成員,通過對象名訪問成員與虛函數(shù)無關(guān)一旦在一個類中定義了虛函數(shù),則以該類為起點的類族中該函數(shù)都是虛函數(shù),派生類中的相應(yīng)函數(shù)不再需要用virtual說明構(gòu)造函數(shù)不能聲明為虛函數(shù),析構(gòu)函數(shù)往往聲明為虛函數(shù)基類中聲明虛函數(shù)時必須明確形參,派生類中相應(yīng)函數(shù)不僅要同名,也要求形參相同36測試classCX{public:virtualint

fa();

int

fb();}classCY:publicCX{public:virtualint

fb();}classCZ:publicCY{public:virtualint

fa(intn);

int

fb(intn);}classCW:publicCZ{public:virtualint

fa();

int

fb();}說明各個類的對象能訪問哪些函數(shù)成員?哪些類的哪些函數(shù)構(gòu)成一組虛函數(shù)畫出層次結(jié)構(gòu)圖37虛函數(shù)的實現(xiàn)原理面向?qū)ο蟪绦蛟O(shè)計允許函數(shù)重載(overload)與函數(shù)覆蓋(override),前者導(dǎo)致函數(shù)同名但形參不同,后者則是同名同形參。前面已有若干示例說明后者的有關(guān)規(guī)則,核心問題是當(dāng)代碼中出現(xiàn)一個函數(shù)調(diào)用時,究竟是調(diào)用哪一段具體的函數(shù)代碼。解決問題的方法是對虛函數(shù)采用動態(tài)聯(lián)編方式。動態(tài)聯(lián)編——也稱動態(tài)綁定,是與靜態(tài)聯(lián)編相對而言的。當(dāng)類族中含有虛函數(shù)時,為類族的每個對象安排虛函數(shù)表(是各個虛函數(shù)入口地址的列表,與數(shù)據(jù)成員安排在一起)。對于通過指向基類的指針訪問函數(shù)成員的情況,編譯時處理成“從虛函數(shù)表中找函數(shù)的入口地址”。38設(shè)計圖元類族的方法成員Q:請設(shè)計CElement類的Draw方法的功能及代碼CElementcolor,size,borderDraw,Erase,...CPointCSegmentstart,end,styleCAreapattern,transparencyCLineCArc......CCurve......CSquare......CCircle......CPolygon......Draw的功能好說,就是在指定位置畫出這個圖元。代碼沒法寫,因為在CElement類中并不知道圖元的具體情況。如果真的要寫代碼,只能是空代碼,什么也不做!39純虛函數(shù)與抽象類采用繼承與派生的層次式設(shè)計構(gòu)建一個類族時,越上層的類就越抽象,越下層的類就越具體。有時上層的方法根本無法明確行為,比如“圖元”的Draw

在C++中,允許一個類只規(guī)定方法成員的首部而不編寫函數(shù)體,這樣的方法稱為“純虛函數(shù)”,含有純虛函數(shù)的類稱為“抽象類”。純虛函數(shù)是在類聲明中用如下形式定義函數(shù)成員:

virtual函數(shù)值類型函數(shù)名(形參表)=0;注意,上述格式中的“=0”表示該函數(shù)是純虛函數(shù)。雖然在語法上virtual不是必須的,但一般只有類族中基類的虛函數(shù)才設(shè)計成不編寫函數(shù)體。40抽象類的作用抽象類中至少含有一個純虛函數(shù),試圖調(diào)用純虛函數(shù)將導(dǎo)致沒有相應(yīng)的代碼可以執(zhí)行,所以,不允許創(chuàng)建抽象類的對象!抽象類的作用表現(xiàn)在規(guī)定由此向下的一個類族(或者類族分支)中有哪些共同的方法成員,并統(tǒng)一這些方法成員的函數(shù)名稱及參數(shù)形式。比如,關(guān)于“圖元”的類族中,“圖元”定義方法成員“Draw”,“線”定義方法成員“GetLength”,區(qū)域定義方法成員“GetArea”,這些方法都無法編寫代碼,但在相應(yīng)的類族中有相同的名稱,并且有相同的形參列表。41什么是多態(tài)多態(tài)(Polymorphism)——簡言之,“一個接口,多種實現(xiàn)”接口,是指函數(shù),包括普通函數(shù)和成員函數(shù)一個接口,是指同名函數(shù),顯然不是一個函數(shù)而是一組函數(shù)多種實現(xiàn),是指同一個函數(shù)名之下有多種不同的代碼以應(yīng)對不同的參數(shù)、對象、環(huán)境等因素接口,泛指實體與外界聯(lián)系的方式、通道。對象與外界聯(lián)系的方式可以有數(shù)據(jù)成員和函數(shù)成員,但“封裝”導(dǎo)致對于多數(shù)類而言,外界不能直接訪問對象的數(shù)據(jù)成員,只能通過類所提供的方法成員獲取信息,或者令對象產(chǎn)生相應(yīng)的行為。所以,面向?qū)ο笾械摹敖涌凇蓖ǔV赋蓡T函數(shù)。42多態(tài)的種類有資料把多態(tài)劃分為通用多態(tài)和特定多態(tài),前者包括參數(shù)多態(tài)和包含多態(tài),后者包括重載多態(tài)和強制多態(tài),但這樣的劃分值得商榷,原因見下頁1.參數(shù)多態(tài)——由函數(shù)模板產(chǎn)生的模板函數(shù)(同名函數(shù)處理不同類型的數(shù)據(jù)),由類模板產(chǎn)生的模板類,除了數(shù)據(jù)類型不同,其它部分相同2.包含多態(tài)——類族中同名成員函數(shù),在指針指向某對象時,動態(tài)綁定相應(yīng)的函數(shù)成員3.重載多態(tài)——函數(shù)重載、運算符重載,以同名函數(shù)處理不同數(shù)量、不同類型的數(shù)據(jù)4.強制多態(tài)——某些運算符具有自動數(shù)據(jù)類型轉(zhuǎn)換功能,如“int+double”時,先把int轉(zhuǎn)換成double,再進行加法運算多態(tài)的四種形式43歸并一下多態(tài)的種類多態(tài)的作用在于以相同或者相似的形式處理不同類型、不同數(shù)量的數(shù)據(jù),或者處理不同類的對象。分析前述劃分,可以再歸并一下,得到三個種類:1.函數(shù)重載(overload),以同一范圍內(nèi)的一組同名函數(shù)應(yīng)對不同數(shù)量、類型的數(shù)據(jù)2.函數(shù)覆蓋(override),以類族中的同名函數(shù)應(yīng)對類族中的各個對象3.自動轉(zhuǎn)換(含運算符重載),基本運算符應(yīng)對不同類型的數(shù)據(jù)/對象只有類模板不在此列。先看看什么是類模板,什么是模板類,似曾相識的概念。44函數(shù)模板:由template開始編寫的一段函數(shù)定義,表示一組函數(shù),除了類型標記不同、其它部分都相同模板函數(shù):由函數(shù)模板生成的函數(shù)函數(shù)模板并不是函數(shù)定義,為什么可以直接調(diào)用? 因為由函數(shù)模板可以生成相應(yīng)的函數(shù)定義由誰來生成函數(shù)定義代碼?在什么時候生成? 編譯器在首次遇到對模板函數(shù)調(diào)用的時候生成生成函數(shù)代碼時其中未定的類型怎么處理? 根據(jù)調(diào)用時參數(shù)的類型將相應(yīng)的類型標識符代替模板中的類型標記復(fù)習(xí):函數(shù)模板與模板函數(shù)函數(shù)模板:模板函數(shù):函數(shù)模板并不是函數(shù)定義,為什么可以直接調(diào)用?由誰來生成函數(shù)定義代碼?在什么時候生成?生成函數(shù)代碼時其中未定的類型怎么處理?45類模板:由template開始編寫的一段類聲明(含其中的成員函數(shù)代碼),其中除了含有特定的類型標記,表示一組類聲明,除了類型標記不同、其它部分都相同模板類:由類模板生成的類類模板并不是類,為什么可以直接定義它的對象? 因為由類模板可以生成相應(yīng)的類定義,即類聲明由誰來生成類聲明的代碼?在什么時候生成? 編譯器在首次遇到使用模板類的時候生成生成類聲明的代碼時其中未定的類型怎么處理? 使用模板類時需要指明類模板中的類型標記用什么具體的類型標識符代替類模板與模板類46一個簡單的類模板先來個最簡單的。如果想根據(jù)下面的類來定義一個類模板,讓其中的int可以是各種數(shù)據(jù)類型:

classCA { public: CA(int

n){mm=n;}; voidf(){cout<<mm<<endl;};

intmm; };template<typename

T>TTCA<T>如果想在類聲明之后再寫函數(shù)體,怎么辦?47template<typenameT>classCA{public: CA<T>(Tn){mm=n;}; voidf(){cout<<mm<<endl;}; Tmm;};類模板后面寫成員函數(shù)代碼template<typenameT>voidCA<T>::f(){cout<<mm<<endl;}不就是把這個函數(shù)體移到后面去嗎?;48template<typenameT>classCA{public: CA<T>(Tn){mm=n;}; voidf(); Tmm; //演示!}; template<typenameS>voidCA<S>::f(){ cout<<mm<<endl;}voidmain(){ CA<int>x(3);

x.f(); CA<double>y(4.567);

y.f();}看一看效果49含雙參數(shù)的類模板如果類模板中需要兩個不同類型的參數(shù),怎么辦?classCA{public:CA(inta,doubleb){mm=a;nn=b;};voidf(){cout<<mm<<','<<nn<<endl;};

intmm;

double

nn;};template<typename

S,typename

T>SSTTCA<S,T>把這個函數(shù)體移出去也不難;template<typename

S,typenameT>voidCA<S,T>::f(){cout<<mm<<','<<nn<<endl;}50template<typename

S,typename

T>classCA{public: CA<S,T>(Sa,T

b){mm=a;nn=b;}; voidf();private: Smm; Tnn; };template<typename

X,typename

Y>voidCA<X,Y>::f(){ cout<<mm<<'('<<sizeof(mm)<<")\n";

cout<<nn<<'('<<sizeof(nn)<<")\n";}voidmain(){ CA<char,int>t1('1',2); CA<double,int>t2(3.45,6); CA<int,double>t3(7,8.9); t1.f(); t2.f(); t3.f();}再看看效果51類模板只聲明了生成若干個類的可能性,只有在編譯器遇到對類的實際使用時(比如定義類的對象),才會生成相應(yīng)的類——稱為類模板的實例化類模板實例化時必須顯式指明模板所含參數(shù)的類型類模板中除了在class后面首次出現(xiàn)的類名之外,其它用到類名的時候都要寫“類名<參數(shù)表>”的完整寫法,但構(gòu)造函數(shù)、析構(gòu)函數(shù)則可以省略為只用“類名”在類聲明之后編寫成員函數(shù)代碼時,需要重新寫template及參數(shù),參數(shù)的數(shù)量必須相同,參數(shù)名稱可變,而且類限制符“::”前面的類名必須用完整寫法類模板規(guī)則要點52針對下面的要求設(shè)計類模板CArray:能夠存放一批數(shù)據(jù)成員函數(shù)GetData能夠根據(jù)輸入情況確定存放多少個數(shù)據(jù),并從鍵盤上讀取這一批數(shù)據(jù)成員函數(shù)Display能夠顯示當(dāng)前存放的數(shù)據(jù)成員函數(shù)Sort能夠?qū)Ξ?dāng)前存放的數(shù)據(jù)排序(升序)為了測試,安排主函數(shù)如下,并要求替換其中的double為int、float、char等常用類型多次測試

voidmain() {CArray<double>x;

x.GetData();

x.Display();

x.Sort();

x.Display(); }復(fù)雜一點的類模板53template<typenameT>classCArray{public:

CArray(){m_arr=NULL;m_count=0;};

~CArray(){if(m_arr)delete[]m_arr;};

int

GetData(); voidDisplay(); voidSort();private: T*m_arr; //根據(jù)需求申請存儲空間

int

m_count;};定義類模板54template<typenameT>int

CArray<T>::GetData(){ int

i,n;

cout<<"Howmanynumbers:";

cin>>n;

if(n<=0) return0;//沒有數(shù)據(jù)需要存儲

if(m_arr) delete[]m_arr;//刪除原數(shù)據(jù)

m_arr=newT[n];

if(!m_arr) return-1;//申請內(nèi)存失敗

m_count=n;

for(i=0;i<n;i++) { cout<<"X["<<i<<"]=";//操作提示

cin>>m_arr[i]; } returnn;}編寫GetData55template<typenameT>voidCArray<T>::Display(){ inti;

if(m_count<=0)

cout<<"Arrayisempty.\n"; else {cout<<m_count<<"numbersinArray.\n";

for(i=0;i<m_count;i++) { cout.width(8);

cout<<m_arr[i]; }

cout<<"\n\n"; }}編寫Display56template<typenameT>voidCArray<T>::Sort(){ int

i,j; Tt;

for(i=1;i<m_count;i++)

for(j=0;j<m_count-i;j++)

if(m_arr[j]>m_arr[j+1]) { t=m_arr[j];

m_arr[j]=m_arr[j+1]; m_arr[j+1]=t; }}//終于編寫完了,測試一下吧編寫Sort57高手編的軟件不會死下面是一個常見現(xiàn)象:通常,要么等待數(shù)秒之后軟件被關(guān)閉,要么就這么一直等下去。如何看待這一現(xiàn)象?58軟件中的BUG什么原因造成了軟件崩潰?

(1)非法命令 (2)錯誤的語序例,指出以下代碼中存在的問題或者錯誤:voidmain(){int

i,n;double*x;x=newdouble[n];

cin>>n;

for(i=n;i>0;i--){ x[i]=i/(i-1); out<<i<<','<<x[i]<<endl;}}有借無還對不存在的變量賦值除以0次序錯誤還有兩個可能的問題:輸入到n的值不合適,比如負數(shù);用new申請內(nèi)存空間可能失敗59錯誤種類這里的“非法命令”不是指語法錯誤(語法錯誤由編譯器處理),而是指命令計算機執(zhí)行一個不正確的操作,包括以下幾種情況:非法訪問內(nèi)存。比如前例中對x[n]賦值非法操作。比如前例中可能存在的除以0非法訪問硬件。連接到計算機的各種設(shè)備各有各的使用特點,有些只讀,有些只寫,有些要求先做A再做B,等等“非法語序”往往會造成變量中存儲的數(shù)據(jù)不正確,從而使得后續(xù)語句達不到預(yù)期的執(zhí)行效果60處理可能的錯誤代碼中不允許出現(xiàn)語法錯誤和邏輯錯誤,比如前例中對x[n]賦值,這是編碼、調(diào)試、測試階段必須解決的對可能出現(xiàn)問題的命令要有應(yīng)對措施,例如:

cin>>n;

if(n<MIN||n>MAX)cout<<"..."; else {x=newdouble[n];

if(x==NULL)cout<<"..."; else

for(i=n-1;i>=0;i--) {x[i]=...; out<<i<<','<<x[i]<<endl; } }61函數(shù)調(diào)用造成的困難func_A(){......}func_B(){......

func_A();......}func_C(){......

func_B();......}main(){......

func_C();......}一旦最底層函數(shù)調(diào)用時出了問題,比如問題在func_A中,通常需要以函數(shù)值的方式逐層“上報”,在某一層決定最終如何處理這個問題62異常處理機制出現(xiàn)問題逐級上報的處理方法代碼較大,異常處理機制是解決該問題的另一種思路:不論哪一級出現(xiàn)問題,只需要“報告一下”(不一定是對直接上級),稱為“拋出異常”。在某個函數(shù)中設(shè)置一段代碼來統(tǒng)一處理這些報告。統(tǒng)一處理異常由try和一組catch構(gòu)成,格式見下頁;拋出異常則相對簡單:

throw(參數(shù));其中,表示異常情況的參數(shù)可以是任何類型的數(shù)據(jù),也可以是某個類的對象,用途是向處理機制報告出現(xiàn)了什么樣的異常。63處理異常的編寫格式處理異常的代碼部分由兩段構(gòu)成:try{可能拋出異常的代碼}catch(形參A){處理A}catch(形參B){處理B}catch(形參C){處理C}......這一段可能有問題,沒關(guān)系,做就是了,有問題就報告根據(jù)不同的錯誤報告采取相應(yīng)的處理措施工作方式:在執(zhí)行try后面的代碼的過程中,只要有異常拋出,立即轉(zhuǎn)到catch部分,根據(jù)拋出的異常的類型執(zhí)行對應(yīng)的處理代碼,如果沒有對應(yīng)的代碼則產(chǎn)生運行錯誤。處理結(jié)束后執(zhí)行catch后面的語句64仍然可能造成軟件崩潰voidfunca(){intn;

cout<<"InputinA:";

cin>>n;

if(n<0)throw("A");}voidfuncb(){doublex;

funca();

cout<<"InputinB:";

cin>>x;

if(x<0)throw(x);elseif(x==0)

throw("B");

funca();}voidmain(){try{ funcb(); }

catch(char*x){cout<<x<<endl;}

catch(intx){ cout<<"x="<<x<<endl;}

cout<<"==========\n";}看看以上代碼在輸入不同的數(shù)據(jù)時的運行結(jié)果65異常處理不是萬能的異常處理機制僅僅是把編寫代碼時在各個函數(shù)中對出現(xiàn)的問題進行處理改為在一個函數(shù)中統(tǒng)一處理,如果代碼中出現(xiàn)非法訪問內(nèi)存、除以0等現(xiàn)象仍將導(dǎo)致軟件崩潰,另一種導(dǎo)致崩潰的原因是對拋出的異常沒有安排相應(yīng)的處理代碼。66防止意外修改數(shù)據(jù)被意外修改是另一個安全隱患。以編寫函數(shù)為例,為了減少參數(shù)傳遞的開銷,有時以引用作為形參,但這樣做的后果是函數(shù)內(nèi)部對形參的修改將直接導(dǎo)致實際數(shù)據(jù)的改變。例如:doublefunc(double

&x){if(x>0) x=(int)(x*100+0.5)*0.01;else x=(int)(x*100-0.5)*0.01;returnx;}沒有“&”是常規(guī)用法。如果用引用作為形參,在完成求值計算的同時會改變實際參數(shù)的值。這樣的修改往往并非有意為之,而是一種疏忽。67const有關(guān)規(guī)則A用const可以限制數(shù)據(jù)被修改,前述“常變量”是const的最初應(yīng)用。const的主要用法:(1)定義普通變量,限定不允許改變變量的值,即前述“常變量”:

const類型變量名=初值;或者 類型const變量名=初值;(2)定義普通對象,限定不允許訪問對象的任何成員,不論是public、protected還是private:

const類對象名(初值);或者 類const對象名(初值);如此定義的對象又稱“常對象”,用作初始化其它對象、給其它對象賦值等68const有關(guān)規(guī)則B(3)const用于定義指針變量時有所不同:① const類型*變量名=初值;不允許改變指針所指變量的值,如果指針指向?qū)ο?,也不允許訪問對象的任何成員,但可以令指針變量指向其它位置② 類型*const變量名=初值;不允許改變指針變量所指位置,但可以改變指針所指變量的值,如果指針指向?qū)ο?,允許訪問對象的public成員③ const

類型*const變量名=初值;兩者都不允許69const有關(guān)規(guī)則C(4)const用于定義引用:

const類型&引用名=目標;不允許通過引用改變目標的值,如果是對象的引用,也不允許通過引用訪問對象的任何成員(5)const用于函數(shù)形參這種用法除了是在參數(shù)傳遞時確定初值之外,與前述相應(yīng)各種情況的規(guī)則對應(yīng)相同70什么是“流”這里所說的“流”是以字節(jié)為單位進行串行數(shù)據(jù)傳輸?shù)男问?。計算機中還有一種是以“位”為單位的流,稱“比特流”。對于一般的傳輸,總是需要有建立連接、傳輸、撤除連接三個步驟,這里的“流”加上一個特點:單向。對于面向?qū)ο蟮木幊潭?,最常見的“流”是文件和標準輸入輸出設(shè)備。71“流”在文件上的應(yīng)用“文件”是存放在外存上的數(shù)據(jù)集合。最早的“文件”完全按照“流”的方式設(shè)計:(1)創(chuàng)建文件/打開文件。建立內(nèi)存與文件的連接,并且要規(guī)定數(shù)據(jù)傳輸方向,因此有“為讀打開”、“為寫打開”等等。(2)讀/寫文件。在最早的文件系統(tǒng)中,讀和寫是分離的,“為讀打開”的文件就只能讀,“為寫打開”的文件就只能寫。以寫為例,先在內(nèi)存中把數(shù)據(jù)準備好,即內(nèi)存緩沖區(qū),然后把這些數(shù)據(jù)一字節(jié)一字節(jié)地送到文件中存放。(3)關(guān)閉文件。撤除第(1)步建立的連接?,F(xiàn)在的文件系統(tǒng)除了允許同時為“讀”和“寫”建立一個連接之外,其它方面沒有改變。72cin與cout

cin是標準輸入設(shè)備流,一般直接對應(yīng)鍵盤,流的方向是由鍵盤向內(nèi)存;cout是標準輸出設(shè)備流,一般直接對應(yīng)顯示器,流的方向是由內(nèi)存向顯示器。計算機開機后,由系統(tǒng)建立cin、cout和內(nèi)存的連接,并且始終處于連接狀態(tài),使用完畢后不需要也不能關(guān)閉。73cin、cout與類型轉(zhuǎn)換

cin和cout都按“文本形式”工作,并帶有數(shù)據(jù)類型轉(zhuǎn)換功能。以cout為例,cout<<x,不論變量x是int、char、double等哪一種類型,cout都能夠把數(shù)據(jù)的內(nèi)存形式轉(zhuǎn)換成文本的ASCII形式,再逐個符號地傳送到顯示器上。

cin和cout具有可擴展性、對于類、結(jié)構(gòu)體等不是基本數(shù)據(jù)類型的情況,可以通過對“<<”和“>>”進行運算符重載,使得cin、cout支持自定義類型。當(dāng)然,類通常會設(shè)計輸入、輸出對應(yīng)的方法成員完成相應(yīng)的工作。74“在一批數(shù)據(jù)中找一個滿足的”,這是非常常用的操作,可以設(shè)計出針對各種類型的數(shù)組進行查找的函數(shù)模板:

template<typenameT>

int

Find(Tx[],intn,Tkey) { inti;

for(

溫馨提示

  • 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)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論