




版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 住宅室內(nèi)裝修合同
- 三農(nóng)村基層法治建設(shè)與實踐方案
- 模板安裝施工方案
- 建筑施工工程承包合同條例
- 鋪筑施工方案
- 洗手間防水卷材的施工方案
- 《大數(shù)據(jù)技術(shù)導(dǎo)論》-教案
- 安徽省宿州市靈璧縣2024-2025學(xué)年上學(xué)期八年級數(shù)學(xué)期末試卷(原卷版+解析版)
- 自貢賓館消防施工方案
- 年產(chǎn)1000噸微生物菌劑項目環(huán)評報告表
- 廣電和通信設(shè)備調(diào)試工(高級)理論考試復(fù)習(xí)題庫(含答案)
- 產(chǎn)房應(yīng)急預(yù)案及流程
- 泉州市中學(xué)生五祖拳健身操教案
- 培智三年級生活數(shù)學(xué)(下)教學(xué)計劃
- 【MOOC】現(xiàn)代郵政英語(English for Modern Postal Service)-南京郵電大學(xué) 中國大學(xué)慕課MOOC答案
- 巨量千川營銷師(初級)認證考試復(fù)習(xí)題庫(含答案)
- 2024解析:第十章 浮力、阿基米德原理及其應(yīng)用-基礎(chǔ)練(解析版)
- 2019年山東省普通高校招生春季考試英語試題
- 假性動脈瘤護理
- QC小組診斷師培訓(xùn)班考試試卷含部分答案
- 部編版(2024)三年級道德與法治上冊第12課《生活離不開規(guī)則》教學(xué)課件
評論
0/150
提交評論