業(yè)務(wù)與軟件C++語言項目C進階_第1頁
業(yè)務(wù)與軟件C++語言項目C進階_第2頁
業(yè)務(wù)與軟件C++語言項目C進階_第3頁
業(yè)務(wù)與軟件C++語言項目C進階_第4頁
業(yè)務(wù)與軟件C++語言項目C進階_第5頁
已閱讀5頁,還剩31頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1

業(yè)務(wù)與軟件C++語言項目

C++進階

第一章類、接口.................................................7

1.1Handle-Body與接口、抽象接口................................7

1.2多繼承、與菱形缺陷、this跳轉(zhuǎn)等...............................13

1.3C++多態(tài)的兩種多態(tài)形式和區(qū)別..................................18

第二章重載......................................................18

2.1函數(shù)重載.....................................................19

2.2運算符重載................................................20

第三章模板......................................................29

3.1模塊函數(shù)...................................................29

3.2模塊類........................................................31

3.3STL標準模板庫................................................34

附錄:參考資料.....................................................39

ISC基本知識附錄:名詞解釋

前S

我們在C++基礎(chǔ)課程中已經(jīng)了解了C++的一些基本概念,知道了什么是類什么是對象。也了解

了繼承、封裝、多態(tài)等C++面向?qū)ο蟮幕咎卣?,本課程主要是更進一步探討一下C++一些基本模

型的應(yīng)用,加深對概念的理解,由于課程時間有限,C++,模型和內(nèi)容又如此之多,對任何一個模

型都無法深入進去,所以只能泛泛而談。

第一章類、接口

學(xué)習(xí)要求:

1、了解類的繼承、封裝等概念之間的關(guān)系

2、了解什么是接口,什么是虛函數(shù),它有什么樣的特點。學(xué)會使

用接口編程的思想

本章節(jié)主要介紹C++中的類、接口。類,包涵了一組數(shù)據(jù)和一組基于數(shù)據(jù)上的一

組方法。它描述了一個對象的屬性、狀態(tài)和行為;接口,它只是描述了一個對象的簡單

的行為。

有關(guān)類的基本概念:

Classnames

Classmembers

MemberFunctions

StaticMemberFunctions

Unions

C++BitFields

NestedClassDeclarations

TypeNamesinClassScope

MultipleBaseClasses

VirtualFunctions

AbstractClasses

ControllingAccesstoClassMembers

privateMembers

3

1SC基本知識附錄:名詞解釋

protectedMembers

publicMembers

AccessSpecifiersforBaseClasses,priavte,public>protected

Friends

Constructors

Destructors

ConversionFunctions

thenewoperatorandthedeleteoperator

CopyingConstructorFunctions

Interface

1.1Handle-Body與接口、抽象接口

在C++中封裝的概念是把一個對象的外觀接口同實際工作方式(實現(xiàn))分離開來,

但是C++的封裝是不完全的,編譯器必須知道一個對象的所有部分的聲明,以便創(chuàng)建和

管理它。我們可以想象一種只需聲明一個對象的公共接口部分的編程語言,而將私有的

實現(xiàn)部分隱藏起來。C++在編譯期間要盡可能多地做靜態(tài)類型檢查。這意味著盡早捕

獲錯誤,也意味著程序具有更高的效率。然而這對私有的實現(xiàn)部分來說帶來兩個影響:

一是即使程序員不能輕易地訪問實現(xiàn)部分,但他可以看到它;二是造成一些不必要的重

復(fù)編譯。

然而C++并沒有將這個原則應(yīng)用到二進制層次上,這是因為C++的類既是描述了

一個接口同時也描述了實現(xiàn)的過程,示例如下:

classCMyString

(

private:

constintm_cch;

char*m_psz;

public:

CMyString(constchar*psz);

-CMyStringO;

intLength()const;

4

1SC基本知識附錄:名詞解釋

intIndex(constchar*psz)const;

CMyStirng對外過多的暴露了內(nèi)存布局實現(xiàn)的細節(jié),這些信息過度的依賴于這些成員變

量的大小和順序,從而導(dǎo)致了客戶過度依賴于可執(zhí)行代碼之間的二進制耦合關(guān)系,這樣

的接口不利于跨語言跨平臺的軟件開發(fā)和移植。

1.1.1Handle-Body模式

解決這個問題的技術(shù)有時叫句柄類(handleclasses)或叫“CheshireCat”[1]。

有關(guān)實現(xiàn)的任何東西都消失了,只剩一個單一的指針“m_pThis”。該指針指向一個結(jié)

構(gòu),該結(jié)構(gòu)的定義與其所有的成員函數(shù)的定義一樣出現(xiàn)在實現(xiàn)文件中。這樣,只要接口

部分不改變,頭文件就不需變動。而實現(xiàn)部分可以按需要任意更動,完成后只要對實現(xiàn)

文件進行重新編譯,然后再連接到項目中。

這里有這項技術(shù)的簡單例子。頭文件中只包含公共的接口和一個簡單的沒有完全指

定的類指針。

classCMyStringHandle

(

private:

classCMyString;

CMyString*m_pThis;

public:

CMyStringHandle(constchar*psz);

?CMyStringHandle();

intLength()const;

intIndex(constchar*psz)const;

};

CMyStringHandle::CMyStringHandle(constchar*psz)

5

ISC基本知識附錄:名詞解釋

:m_pThis(newCMyString(psz));

(

)

CMyStringHandle::-CMyStringHandle()

(

deletem_pThis;

)

intCMyStringHandle::Length()

(

returnm_pThis->Length();

)

intCMyStringHandle::Index(constchar*psz)

(

returnm_pThis->Index(psz);

)

這是所有客戶程序員都能看到的。這行

classCMyString;

是一個沒有完全指定的類型說明或類聲明(一個類的定義包含類的主體)。它告訴編譯

器,Cheshire是一個結(jié)構(gòu)的名字,但沒有提供有關(guān)該結(jié)構(gòu)的任何東西。這對產(chǎn)生-一個

指向結(jié)構(gòu)的指針來說已經(jīng)足夠了。但我們在提供一個結(jié)構(gòu)的主體部分之前不能創(chuàng)建一個

對象。在這種技術(shù)里,包含具體實現(xiàn)的結(jié)構(gòu)主體被隱藏在實現(xiàn)文件中。

在設(shè)計模式中,這就叫做Handle-Body模式,Handle-Body只含有一個實體指針,

服務(wù)的數(shù)據(jù)成員永遠被封閉在服務(wù)系統(tǒng)中。

Handle-Body模式如下:

6

1SC基本知識附錄:名詞解釋

classHandle

圖1Handle-Body模式(句柄類做為接口)

Handle-Body的布局結(jié)構(gòu)永遠不會隨著實現(xiàn)類數(shù)據(jù)成員的加入或者刪除或者修改而導(dǎo)致

Handle-Body的修改,即Handle-Body協(xié)議不依賴于C++實現(xiàn)類的任何細節(jié)。這就有效的

對用戶的編譯器隱藏了這些斜街,用戶在使用對這項技術(shù)時候,Handle-Body接口成了

它唯一的入口。

然而Handle-Body模式也有自己的弱點:

1、接口類必須把每一個方法調(diào)用顯示的傳遞給實現(xiàn)類,這在一個只有一個構(gòu)造

和一個析構(gòu)的類來說顯然不構(gòu)成負擔(dān),但是如果一個龐大的類庫,它有上百

上千個方法時候,光是編寫這些方法傳遞就有可能非常冗長,這也增加了出

錯的可能性。

2、對于關(guān)注于性能的應(yīng)用每一個方法都得有兩層的函數(shù)調(diào)用,嵌套的開銷也不

理想

3、由于句柄的存在依然存在編譯連接器兼容性問題。

接口和實現(xiàn)分離的Handle-Body。

1.1.2抽象接口

使用了“接口與實現(xiàn)的分離”技術(shù)的Handle-Body解決了編譯器/鏈接器的大部

分問題,而C++面向?qū)ο缶幊讨械某橄蠼涌谕瑯邮沁\用了“接口與實現(xiàn)分離”的思想,

而采用抽象接口對于解決這類問題是一個極其完美的解決方案。

7

1SC基本知識附錄:名詞解釋

1、抽象接口的語言描述:

classIMyString

virtualintLength()const=0;〃這表示是一個純虛函數(shù),具有純虛函數(shù)的接口

virtualintIndex(constchar*psz)const=0;

2、抽象接口的內(nèi)存結(jié)構(gòu):

圖2抽象接口的內(nèi)存布局

3、抽象接口的實現(xiàn)代碼:

接口:

classIMyString

virtualintLength()const=0;〃這表示是一個純虛函數(shù),具有純虛

〃函數(shù)的接口

virtualintIndex(constchar*psz)const=0;

):

實現(xiàn):

8

1SC基本知識附錄:名詞解釋

classCMyString:publicIMyString

(

private:

constintm_cch;

char*m_psz;

public:

CMyString(constchar*psz);

virtual-CMyString();

intLength()const;

intIndex(constchar*psz)const;

)

從上面采用抽象接口的實例來看,抽象接口解決了Handle-Body所遺留下來的全

部缺陷。

抽象接口的一個典型應(yīng)用:

抽象工廠(AbstractFactroy)

圖3抽象工廠模式

1.2多繼承與菱形缺陷、this跳轉(zhuǎn)等

9

1SC基本知識附錄:名詞解釋

多重繼承是C++語言獨有的繼承方式,其它幾乎所有語言都秉承了單一繼承的思

想。這是因為多重繼承致命的缺陷導(dǎo)致的:

1.2.1菱形缺陷

當繼承基類時,在派生類中就獲得了基類所有的數(shù)據(jù)成員副本。假如類B從A1

和A2兩個類多重繼承而來,這樣B類就包含Al、A2類的數(shù)據(jù)成員副本。

考慮如果Al、A2都從某基類派生,該基類稱為Base,現(xiàn)在繼承關(guān)系如下:

Base

圖4菱形繼承關(guān)系

我們C++語言來描述這種繼承關(guān)系:

classBase{....};

classAl:publicBase{....};

classA2:publicBase{....};

classB:publicAl,publicA2{....};

那么Al、A2都具有Base的副本。這樣B就包含了Base的兩個副本,副本發(fā)生了重

疊,不但增加了存儲空間,同時也引入了二義性。這就是菱形缺陷,菱形缺陷時

間是兩個缺陷:

1、子對象重疊

2、向上映射的二義性。

菱形缺陷的其中一種解決辦法將

10

1SC基本知識附錄:名詞解釋

在C++世界里最廣泛的使用虛擬繼承解決菱形缺陷的應(yīng)用便是標準C++的輸入/輸

出iostream;

圖5標準C++的輸入/輸出

1.2.2多重接口與方法名沖突問題(Siamesetwins)

對繼承而來的虛函數(shù)改寫很容易,但是如果是在改寫一個“在兩個基類都有相同原

型”的虛函數(shù)情況就不那么容易了。

提出問題:

假設(shè)汽車最大速度的接口為ICar,潛艇最大速度的接口為IBoat,有一個兩棲類的

交通工具它可以奔跑在馬路上,也可以航行在大海中,那么它就同時擁有ICar、IBoat

兩種交通工具的最大速度特性,我們定義它的接口為ICarBoat;

classICar

(

virtualintGetMaxSpeed()=0;

};

classIBoat

(

virtualintGetMaxSpeed()=0;

};

我們先對ICarBoat的接口做一個嘗試:

classCCarBoat

11

1SC基本知識附錄:名詞解釋

virtualintGetMaxSpeed();〃既完成ICar的GetMaxSpeed()接口方法又

〃完成IBoat的接口方法?顯然不能夠

);

解決問題:

顯然上面這個嘗試根本就無法成功,只用一個實現(xiàn)方法,怎么能夠求出這個ICarBoat

交通工具奔跑在馬路上的最高時速,同時也能夠求出航行在大海上的最大航行速度呢。

上面這一問題矛盾就在一一個方法,卻需要兩個答案。看來ICarBoat要返回兩個答

案就必須有兩個方法了,我們假設(shè)一個方法是求在陸地上奔跑的速度,名稱為

GetCarMaxSpeed();另一個方法是求在大海上航行的最大速度,名稱為

GetBoatMaxSpeed();那這兩個方法又怎么和GetMaxSpeed()接口方法聯(lián)系起來呢;

幸運的是,我們找到了解決辦法,而且解決辦法有很多種,下面介紹一下繼承法。

classIXCar:publicICar

(

virtualintGetMaxSpeed()

(

GetCarMaxSpeed();

}

virtualintGetCarMaxSpeed()=0;

);

classIXBoat:publicIBoat

{

virtualintGetMaxSpeed()

(

GetBoatMaxSpeed();

)

virtualintGetBoatMaxSpeed()=0;

);

classCCarBoat:publicIXCar,publicIXBoat

12

1SC基本知識附錄:名詞解釋

virtualintGetCarMaxSpeed()

virtualintGetBoatMaxSpeed()

圖6多重接口與方法名沖突問題

1.2.3this跳轉(zhuǎn)

this跳轉(zhuǎn)是指的“對象同一性”問題。

在單一-繼承的世界內(nèi),無論繼承關(guān)系怎么復(fù)雜,針對于同一對象,無論它的子類

或者父類的this指針永遠相等。即如果有下面的模型:

13

ISC基本知識附錄:名詞解釋

圖7B從A繼承的關(guān)系圖

那么對于一個已經(jīng)實例化B類的對象bObject,永遠有(B*)febObject

==(A*)&bObject成立。

但是在多繼承的世界內(nèi),上面的等式就不能恒成立,對象的同一性受到了挑戰(zhàn)。

特別的是,在多繼承世界內(nèi)如果圖四的菱形關(guān)系存在情況下,如果對于已經(jīng)實例

化B類的對象bObject;(Base*)(Al*)febObject!=(Base*)(A2*)&bObject成

立,當這種事情發(fā)生的時候我們就只能特殊處理了。這種情況在COM應(yīng)用中處處都會發(fā)

生。

1.3C++多態(tài)的兩種多態(tài)形式和區(qū)別

C++有兩種多態(tài)多態(tài)形式:

1、編譯時刻多態(tài),編譯時刻多態(tài)依靠函數(shù)重載或者模板實現(xiàn)

2、運行時刻多態(tài)。運行時刻多態(tài)依靠需函數(shù)虛接口實現(xiàn)

第二章重載

學(xué)習(xí)要求:

1、了解什么是函數(shù)重載,什么是運算符重載

2、學(xué)會運用智能指針,仿函數(shù)

14

1SC基本知識附錄:名詞解釋

在C++的世界里,有兩種重載:函數(shù)重載和運算符重載,函數(shù)重載就采用采用參數(shù)匹配

的原則,進行重載的,它是一種編譯時刻的多態(tài)。而運算符重載,使采用改寫或者說重

新定義C++的內(nèi)嵌運算符的方法。

有關(guān)重載的基本概念:

OverloadedFunctions

OverloadedOperators

DeclarationMatching

ArgumentMatching

ArgumentTypesMatching

ArgumentCountsMatching

C++UnaryOperators

BinaryOperators

SmartPointer

Functionobjects

1.1函數(shù)重載

函數(shù)重載方法是在當前范圍內(nèi)選擇一個最佳匹配的函數(shù)聲明供調(diào)用該方法者使用。

如果一個適合的函數(shù)被找到后,這個函數(shù)將會被調(diào)用,在這里“適合的”是指按下列順

序匹配的符合下面條件的:

1、一個精確匹配的函數(shù)被找到

2、一個參數(shù)只有細微的差別,兒乎可以忽略不計的。

3、象類似通過子類向父類轉(zhuǎn)化達到參數(shù)匹配的

4、通過正常轉(zhuǎn)化函數(shù)進行類型轉(zhuǎn)換,能夠達到參數(shù)匹配到的。

5、通過用戶自定義的轉(zhuǎn)化函數(shù)(如轉(zhuǎn)化運算符或者構(gòu)造函數(shù))達到參數(shù)匹配的

6、參數(shù)是采用省略符號

函數(shù)重載的方法基本上有:

1、根據(jù)函數(shù)參數(shù)數(shù)據(jù)類型的不同進行的重載;

2、根據(jù)參數(shù)個數(shù)的不同進行的重載;

3、缺省參數(shù)上的重載

15

ISC基本知識附錄:名詞解釋

我們在這里把缺省參數(shù)也稱為一種函數(shù)重載,實際上它并不是嚴格意義上的重載。

在使用缺省參數(shù)時必須記住兩條規(guī)則。第一,只有參數(shù)列表的后部參數(shù)才可是缺省的,

也就是說,我們不可以在一個缺省參數(shù)后面又跟一個非缺省的參數(shù)。第二,一旦我們開

始使用缺省參數(shù),那么這個參數(shù)后面的所有參數(shù)都必須是缺省的。第三,缺省參數(shù)只能

放在函數(shù)聲明中。第四,缺省參數(shù)可以讓聲明的參數(shù)沒有標識符。

4、返回值重載

特別注意,在C++中并沒有根據(jù)返回返回值的不同進行重載的,即我們不能定義

這樣的函數(shù):

voidf();

intf();

在C++中這樣的函數(shù)聲明方法是被禁止的,但是我們有時間可能又需要這樣的重載

方法,我們又怎么實現(xiàn)呢,其實很簡單,jiang函數(shù)的參數(shù)進行擴展,將這個函數(shù)返回

值的數(shù)據(jù)類型,做為擴展參數(shù)的數(shù)據(jù)類型來。如下:

voidf(void);

voidf(int);

此時這個例子中的參數(shù)列表的數(shù)據(jù),只在編譯時刻起到分練函數(shù)的作用,在運行時

刻并不起到傳值作用,模板中經(jīng)常都應(yīng)用到了這種方法。

1.2運算符重載

你可以重新定義C++絕大多數(shù)內(nèi)嵌運算符的實現(xiàn)方法和功能,這些重定義的或者

說重載的運算符,有可能全局作用的,也有可能作用在類基礎(chǔ)之上的,運算符重載的實

現(xiàn)可能以類的成員函數(shù)的形式出現(xiàn),也有可能以全局性的函數(shù)的身份出現(xiàn)。

在C++中重載運算符的名字為operatorX,在這里x是一個可重載的運算符,如:

重載加法運算符,你需要定義一個名為operator+的函數(shù),然后實現(xiàn)他,其它的類似

定義就可以了,例如:

Classcomplex//verysimplifiedcomplex

16

1SC基本知識附錄:名詞解釋

doublere,im;

public:

complex(doubler,doublei):re(r),im(i){};

complexoperator+(complex);

complexoperator*(complex);

);

定義了complex這個復(fù)數(shù)的一個簡單的實現(xiàn)概念模型。一個復(fù)數(shù)是由一對double

類型的數(shù)據(jù)組成,并定義了這個復(fù)數(shù)的兩個方法,加法運算complex::operartor+()

和乘法運算complex::operator*().現(xiàn)在我們就能夠?qū)崿F(xiàn)下面的復(fù)數(shù)表達式了:

voidf()

complexa二complex(1,3.1);

complexbcomplex(1.2,2);

complexcb;

a=b+c;

b=b+c*a;

c=a*b+complex(1,2);

1.3.1C++可重載的和C++不可重載的運算符

17

ISC基本知識附錄:名詞解釋

可重載運算符表:

OperatoNameTypeOperatoNameType

rr

9CommaBinary->*Pointer-to-memberBinary

selection

1LogicalNOTUnary/DivisionBinary

1=InequalityBinary/=Division/assignmentBinary

%ModulusBinary<LessthanBinary

%=Modulus/assignmentBinary?LeftshiftBinary

&BitwiseANDBinary?=LeftBinary

shift/assignment

&Address-ofUnary<=LessthanorequaltoBinary

&&LogicalANDBinary=AssignmentBinary

&=BitwiseAND/assignBinary==EqualityBinary

()Functioncall——>GreaterthanBinary

*MultiplicationBinary>=GreaterthanorBinary

equalto

*PointerdereferenceUnary?RightshiftBinary

*=Muitiplication/assignBinary?=RightBinary

shift/assignment

+AdditionBinary[]Arraysubscript——

+UnaryPlusUnaryExclusiveORBinary

++Increment1Unary=ExclusiveBinary

OR/assignment

+=Addition/assignmentBinary1BitwiseinclusiveORBinary

-SubtractionBinary1=BitwiseinclusiveBinary

OR/assignment

-UnarynegationUnaryIILogicalORBinary

--Decrement1UnaryOne,scomplementUnary

-=Subtraction/assignBinarydeletedelete——

18

ISC基本知識附錄:名詞解釋

->MemberselectionBinarynew

不可重載運算符表:

OperateName

r

?Memberselection

,*Pointer-to-memberselection

::Scoperesolution

?:Conditional

#Preprocessorsymbol

##Preprocessorsymbol

在上面可重載的運算符可以看出運算符重載共分為兩類:-元運算符重載和二元運算

符重載

一元運算符重載:

在聲明一個類的非靜態(tài)的一元運算符重載函數(shù)時,你必須聲明的形式如

下:

ret-typeoperatoro/?()(1)

在這里ret-type是指返回數(shù)據(jù)類型op是指一元運算符

在聲明一個全局的一元運算符重載函數(shù)時,你必須聲明的形式日下:

ret-typeoperatorarg)(2)

在這里ret-type與op和上面的意思一一樣,arg是指這個運算符所作用

的數(shù)據(jù)類型

二元運算符重載:

19

1SC基本知識附錄:名詞解釋

在聲明一個類的非靜態(tài)的二元運算符重載函數(shù)時,你必須聲明的形式如

下:

ret-typeoperator(3)

(3)式和二式基本相同arg可以是任何一個

在聲明一個全局的二元運算符重載函數(shù)時,你必須聲明的形式日下:

ret-typeoperatoro/?(,az^7,arg2)(4)

在這里ret-type與op和上面的意思一樣,argl,arg2,是指這個運算

符所作用兩個數(shù)據(jù)類型

1.3.2幾類特殊的運算符重載

1、類型轉(zhuǎn)換運算符

所有的數(shù)據(jù)類型均可以定義構(gòu)造函數(shù),包括系統(tǒng)定義的數(shù)據(jù)類型和用戶自定義

的數(shù)據(jù)類型,如:

classCString

(

operatorLPCSTR()const;

);

應(yīng)用:

CStringstr="12345”;

LPCSTRIpsz=str;〃此處會進行LPCSTR運算

這只是一個簡單的應(yīng)用的示例,其實有時間類型轉(zhuǎn)換具有無比強大的功能。我

曾經(jīng)就是用類型裝換運算符重載解決一個跨平臺通信的問題。

2、bool運算符重載

int、float,bool等運算符也是可以重載的,例如重載bool運算符,但是重載

運算符bool時候,需要注意有很多麻煩和臆想不到的東西

template<classT>

20

ISC基本知識附錄:名詞解釋

classtestbool

(

operatorbool()constthrow()

(

returnm_pT!=0;

}

private:

T*m_pT;

}

下面結(jié)果均通過編譯

testbool<int>spl;

testbooKstd::string>sp2;

if(spl==sp2)

if(spl!=sp2)

boolb=spl

intI=spl*10;

從上面可以看得出bool的表現(xiàn)已經(jīng)遠遠超過bool本身了,所以建議大家不

要輕易對bool進行重載操作。

3、地址運算符重載

在DCOM應(yīng)用中,我們有一個重載運算符的例子:

STDAPICoCreatelnstance(

REFCLSIDrclsid,

LPUNKNOWNpUnkOuter,

DWORDdwClsContext,

REFIIDriid,

LPVOID*ppv);

我們看最后一個參數(shù)LPVOID指針的指針,這里是一個輸出參數(shù),返回

一個接口的指針。

一般情況下我們應(yīng)用如下

IUnknown*pUn;

CoCreateInstance(void**)&pUn);(5)

21

1SC基本知識附錄:名詞解釋

然而我們也可以這樣寫:

IUnknown*pUn;

CComPtrcomPtr(pUn);

CoCreateInstance(.,(void**)&comPtr);(6)

之所以能夠這么寫這是因為CComPtr重載了運算符,如下:

template<classT>

classCComPtr

(

public:

CComPtr(T*Ip)

(

if((p=Ip)!=NULL)

p->AddRef();

)

T**operator&()

(

ATLASSERT(p==NULL);

return&p;

)

private:

T*p;

);

&comPtr實際上是得到了

一般的情況下,我們并不能對pUn的地址,所以(5)式和(6)式其實傳入

的參數(shù)是一樣當都是傳入了pUn的地址。

雖然我們能夠?qū)\算符進行重載,但一般情況下我們并不是很提倡這種操作,

這是因為:

A、暴露了封裝對象的地址,如上面CComPtr對pUn的封裝其實不起任何

作用,任何時候我都可以直接訪問和修改pUn指針,這就意味著所有權(quán)

的完全喪失,封裝不起任何意義

B、對于unaryoperator&的重載使得重載對方永遠無法與STL容器進行任

何融合,甚至無法參與任何泛型編程。

22

1SC基本知識附錄:名詞解釋

一個對象的地址是一個對象最基本的概念,在一般情況下,我們并不提倡,

也請大家慎用地址運算符的重載。

4、指針運算符重載

指針運算符,有一個及其特殊且及其重要的機制:

當你對某個型別實施operator-〉而這個型別并非原生指針時候:編譯器會

從這個型別中找出用戶自定義的operator-),并實施后,編譯器將繼續(xù)對這個

operator-)返回的結(jié)果實施operator-)直到找到一個原生指針。

這種機制導(dǎo)致了一個特有的技術(shù):(preandpostfunctioncalls),"前調(diào)

用”及后調(diào)用技術(shù)。應(yīng)用如下:

classCallDoSomething

(

public:

voidDoCallQ

(

TRACE("DoCall\n");

}

);

template<classT>

classCalllnMutiThread

(

classLockProxy

(

public:

LockProxy(T*pT)

:m_pT(pT)

(

TRACE("Lock\n");

)

~LockProxy()

(

TRACE("UnLock\n");

)

T*operator->()

(

returnm_pT;

)

private:

T*m_pT;

public:

23

1SC基本知識附錄:名詞解釋

CallInMutiThread(T*pT)

:m_pT(pT)

(

}

LockProxyoperator->()

(

returnLockProxy(m_pT);

}

private:

T*m_pT;

);

上面CallDoSomething是函數(shù)調(diào)用,假設(shè)這個類原來是在單線程中運行的,但

是現(xiàn)在已經(jīng)移植到了多環(huán)境中,所以我們就增加了CalllnMutiThread對原始類

進行配接使之適應(yīng)與多線程環(huán)境,調(diào)用過程如下:

CallDoSomethingDoSomthing;

CallInMutiThread<CallDoSomething>MutiThread(&DoSomthing);

MutiThread->DoCall();

調(diào)用結(jié)果如下:

Lock

DoCall

UnLock

從上面可以看出在調(diào)用CallDoSomething的成員函數(shù)DoCall之前調(diào)用了

Lock方法,在調(diào)用結(jié)束后有調(diào)用了UnLock。這就是所謂的“前調(diào)用”和“后調(diào)用”,

其實并不僅僅是多線程問題可以采用此辦法,所有的“前調(diào)用”和“后調(diào)用”模

式均可由此解。

重載“-〉”運算符,同時引出了智能指針的概念,參見下頁。

5、括號運算符重載

語法特征:

primary-expression(expression-1istopt)

括號運算符是一個同“-〉”運算符一樣也是一個及其重要的運算符

24

1SC基本知識附錄:名詞解釋

在MSDN上說括號運算符是一個二元運算符,我覺得這個說法是完全錯誤的,在

所有C++運算符重載中,括號運算符,應(yīng)該是唯一沒有規(guī)定參數(shù)元的個數(shù)的。它

的參數(shù)可以從0個到N個。

示例:

classPoint

(

public:

Point(){_x=_y=0;}

Point&operator()(intdx,intdy)

{_x+=dx;_y+=dy;return*this;}

private:

int_x,_y;

);

調(diào)用如下:

Pointpt;

pt(3,2);

從上面可以看出,括號運算符,調(diào)用形式如下:

object{parameterlist);

看起來和函數(shù)的形式是完全一樣的:

function(^parameterlist);

所以根據(jù)這一特點我們稱之為仿函數(shù)。

第三章模板

學(xué)習(xí)要求:

1、了解什么是模板

2、學(xué)會運用模板函數(shù),模版類和STL

模板(templates),以及以模版為基礎(chǔ)的泛型編程和泛型模式,是當今C++中最

活躍的?項編程技術(shù),模版的第一個革命性的應(yīng)用就是StandardTemplateLibrary(簡

25

ISC基本知識附錄:名詞解釋

稱STL)oSTL將templates技術(shù)廣泛應(yīng)用于STL容器和STL算法上,在這一領(lǐng)域template

技術(shù)發(fā)揮到了極致。

本章介紹C++templates的基本概念和語言特性

1.1認識模板

1、模板的基本語法是:

template<[typelist][,[arglist]]>declaration

這個template描述了一個參數(shù)化的類(模板類)或者是一個參數(shù)化的函數(shù)(模板

函數(shù)),這個模板參數(shù)列表是用逗號分隔的類型列表(在這個表單忠使用class或者是

typename來標識這個數(shù)據(jù)類型)。在某些情況下這個模板體內(nèi)可能不存在任何的數(shù)據(jù)

類型。declaration域必須是一個函數(shù)或者類的聲明。

1.4模板函數(shù)

語法定義:

template<comma-separated-1ist-of-parameters>

function-name(parameterlist)

(

)

例如:

template<typenameT>

inlineTconst&max(Tconst&.a,Tconst&b)

(

//ifa<bthenusebelseusea

returna<b?b:a;

)

調(diào)用形式:

1:通過調(diào)用的參數(shù)來識別模板的各參數(shù)類型

MAX(4,4.2)://OK,buttypeoffirstargumentdefinesreturntype

2:明確指定參數(shù)的類型:

MAX<int,float>(4,4.2);//OK

在我們的例子中這個參數(shù)列表是typenameT,其實在這里typename是可以用

class替換的,typename是在C++演化過程中逐漸形成的,而class是一個歷史性的概

念,typename表達了一個比class更抽象意義上的概念。

26

1SC基本知識附錄:名詞解釋

有如下定義如:

classtypenamedef

(

typedefintINT_TYPE;

);

如果這樣表達是正確的:

template<classT>

classtesttypename:publictypenamedef

(

public:

typenameT::INT_TYPE;

INT_TYPEmjnt;

);

但是如果把此處的typename換成class就會報錯

1.4.1重載模板函數(shù)(OverloadingFunctionTemplates)

和普通的函數(shù)?樣,模板函數(shù)也可以被重載,也就是說對象同的函數(shù)名,你能夠具

有不同的函數(shù)定義,在調(diào)用的時候再由C++編譯器決定,那?個候選函數(shù)更有資格被匹

配調(diào)用。

下面這個簡單的例子說明了重載模板函數(shù)的方法和過程:

//maximumoftwointvalues

inlineintconst&max(intconst&a,intconst&b)

(

returna<b?b:a;

)

//maximumoftwovaluesofanytype

template<typenameT>

inlineTconst&max(Tconst&a,Tconst&b)

(

returna<b?b:a;

)

//maximumofthreevaluesofanytype

template<typenameT>

inlineTconst&max(Tconst&a,Tconst&b,Tconst&c)

(

returnmax(max(a,b),c);

27

1SC基本知識附錄:名詞解釋

intmain()

(

::max(7,42,68);//callsthetemplateforthreearguments

::max(7.0,42.0);//callsmax<double>(byargumentdeduction)

::max('a',b);//callsmax<char>(byargumentdeduction)

::max(7,42);//callsthenontemplatefortwoints

::max<>(7,42);//callsmax<int>(byargumentdeduction)

::max<double>(7,42);//callsmax<double>(noargumentdeduction)

}

上面這個例子也說明了普通的函數(shù)與模板函數(shù)可以擁有同一個名字,而且可以被

初始化為同一類型,如:

max(7,42)

調(diào)用匹配非模板函數(shù)也匹配模板函數(shù)。

1.5模板類

基本的語法定義:

template<comma-separated-list-of-parameters>

classclass-name

(

);

具有缺省參數(shù)的模板定義形式

template<typenameT,typenameAlloc=alloc>

classclass-name

(

);

在模板中用到了大量非習(xí)慣性思維方法,大家在學(xué)習(xí)模板之前需要了解這些模板

設(shè)計的思維方法:

申明并不一定需要定義:

1、申明一個函數(shù),并不實現(xiàn)

在C++中我們可能因為禁止某個缺省函數(shù)的調(diào)用操作而申明該缺省函數(shù),但

不定以它,例如:

classtestDeclare

(

public:

testDeclareQ;

28

1SC基本知識附錄:名詞解釋

我們對上面的testDeclare的缺省構(gòu)造函數(shù)進行了聲明,但是我們并沒有

構(gòu)造函數(shù)的的定義,當我們執(zhí)行

testDeclaredeclare;

上面這個申請創(chuàng)建一個對象的操作會被編譯系統(tǒng)所禁止

當然,我們也可以對缺省的重載運算符實施同樣的手段

2、申明一個函數(shù)而不實現(xiàn)可能是為了模板函數(shù)的泛化

泛化:

template<typenameT>

TtestFun();

特化:

templateo

inttestFun()

(

return10;

)

3、申明一個函數(shù)可能僅僅為了獲得特殊某一項功能

例如:

TMarkT();

charTest(T);

intTest(...);

sizeof(MarkT());

上面的例子其實就是求T類的的字節(jié)數(shù),其實在-一般情況下,我們直接寫

sizeof(T)就可以了,然而有的時候系統(tǒng)并不允許我們這樣做,所以我們就可

以通過上面的例子MarkT()函數(shù),其實上面的MarkT(),charTest(T)函

數(shù)intTest(...)都是沒有定義的,但是由于sizeof是編譯時刻的運算,所

以它并不需要關(guān)心這些函數(shù)是否實現(xiàn)。

?申明一個類而不實現(xiàn)

例如我們在禁止模板類的泛化過程中就可以實現(xiàn)

template<typenameT>

classtestClass;〃泛化只申明

templateo

classtestClass<int>〃特化進行實現(xiàn)

29

ISC基本知識附錄:名詞解釋

如果我們有

testClass<char>test;//error

〃系統(tǒng)會調(diào)用泛化時發(fā)現(xiàn)沒有沒有實現(xiàn)二產(chǎn)生編譯錯誤

testClass<int>test;//OK

〃系統(tǒng)調(diào)用特化故OK

1.5.1模板設(shè)計基本方法

A、編譯器斷言

template<classT,classU>

(

typedefcharsmall;

classbig{chardummy[2]};

staticsmalltest(U);

staticbigtest(...);

staticTmarkT();〃函數(shù)定義只是為了得到?個返回類型

public;

enum{value=sizeof(test(makT()))==sizeof(Small)};

B、模板特化

template<typenameI,typenameO>

structtestClass

(

testClass(){count?"I,On?endl;}

);

template<typenameT>

structtestClass<T*,T*>

(

testClass(){count?”T*,T*n?endl;}

C、常數(shù)映射型別

template<intv>

structtestClass

(

enum{value=v};

30

ISC基本知識附錄:名詞解釋

D、型別映射型別

template<typenameT>

structtestClass

(

typedefTOriginalType;

);

1.6STL標準模板庫

容器

?序列容器

-vector,list、deque、stack(沒有迭代器)、queue(沒有迭代器)、即stack、

queue不允許遍歷行為

?關(guān)聯(lián)容器

-set(標準)、map(標準)、hash_table>RB-tree

?通用算法

,begin()>end()>size()>empty()>erase(iterator_position)>clear()

迭代器

迭代器的基本算法

?能夠進行+、一、++、—>+=、一=、==、!=等運算

,是一種智能性指針,實現(xiàn)operator*operator->的重載

?根據(jù)迭代器的特點,迭代器又稱循環(huán)子

迭代器前閉后開區(qū)間[first,last)

型別

?單向迭代器

???赡娴?/p>

?。隨機迭代器

31

ISC基本知識附錄:名詞解釋

?迭代器的繼承關(guān)系

InputIteratorOutputIterator

ForwardIterator

Y

BidirectionalIterator

I

RandomAccessIter

溫馨提示

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

評論

0/150

提交評論