《C++程序設計教程》課件第4章_第1頁
《C++程序設計教程》課件第4章_第2頁
《C++程序設計教程》課件第4章_第3頁
《C++程序設計教程》課件第4章_第4頁
《C++程序設計教程》課件第4章_第5頁
已閱讀5頁,還剩156頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

4.1概念

4.2函數模板與模板函數

4.3重載模板函數

4.4類模板與模板類

4.5類模板的應用

4.6標準模板庫STL

本章要點

練習

若某函數要對一些數據類型進行同樣的處理,則可將所處理的數據類型說明為參數,將該函數改寫為模板。模板可以讓參數表中的數據類型進行同樣方式的處理。

比如swap()函數,要求分別交換整型、實型和字符型數據,就要有三個函數:

(1)?voidSwap(int&a,int&b)

{inttemp=a;a=b;b=temp;}

(2)?voidSwap(float&a,float&b)

{floattemp=a;a=b;b=temp;}

(3)?voidSwap(char&a,char&b)

{chartemp=a;a=b;b=temp;}4.1概念從中不難發(fā)現,三個Swap的主體是一樣的,只是處理的數據類型不同。那么能否編寫一個函數,使之對于任何類型的兩個對象a、b,總能使編譯器理解其意義并實現數據交

換呢?

在C++中引入模板機制,就能解決上述問題。用模板可使一些還未確定數據類型的參數進行一些操作,也就是說把那些完成同樣操作功能的函數用模板來代替。這樣,不同類型對象的操作就可在一個母體上完成。在C++中有類和函數,模板也分為類模板(classtemplate)和函數模板(functiontemplate)。模板是C++中最復雜且功能最強大的特性之一,它可以幫助用戶在編程中創(chuàng)建可重用的代碼。使用模板可以創(chuàng)建通用的函數和類,在此稱為模板函數或模板類。在模板函數和類中,將數據類型指定為參數,所以該函數和類就能適用于不同的數據類型。4.2函數模板與模板函數4.2.1函數模板

1.一般定義形式

template<類型形式參數表>

函數返回類型FunctionName(函數形式參數表){函數體}

所有函數模板的定義都是以關鍵字template開始的,關鍵字之后是用尖括號括起來的類型形式參數表,也可稱為模板參數表。該參數表中的參數之間用逗號分隔。類型形式參數表之后是名為FunctionName的函數返回類型,然后是函數FunctionName的形式參數表。

<類型形式參數表>不能為空,其中參數可以是一個或多個,既可以有待確定的類型,也可以是基本類型,可以包括函數的返回類型,也可以包括在本模板函數中要使用的類型。總之,要列出所有現在還未能確定的類型,再用一個自定義的標識符來代替未來的類型名。

在<類型形式參數表>中每個未確定類型的形式參數之前都要加前綴class或typename。當有多個參數時用逗號分隔。在參數表中若包含基本數據類型,則按常規(guī)標識。

3.聲明函數模板

就像普通函數一樣,函數模板也可以分為聲明與定義。例如,template<classTA,classTB,intK>voidFun(floatx,TAy,TBz);聲明了一個函數模板,該模板中有三個參數,前兩個還未決定類型,后一個是整型。在聲明時要注意,一般函數模板在聲明時必須給出變量名,如Fun中的x,y,z。在函數形式參數表中可以包含基本數據類型,也可以包含<類型形式參數表>中的類型。需要說明的一點是,一般來說,將基本類型放入<類型形式參數表>中的情況并不常用?;绢愋蛥抵皇谴砹艘粋€常量表達式。對于在<類型形式參數表>中已出現的類型,在函數中都可作為已知類型。

4.定義函數模板

通過下面的模板說明和函數體可完成函數模板的定義:

template<classTA,classTB,intK>voidFun(floatx,TAy)

{TBz;z=x+y;cout<<x<<‘+’<<y<<‘=’<<z<<endl;}

但它只是(函數)模板的定義,編譯器不會生成執(zhí)行代碼,它也不占用空間,因為模板是框架,不是函數,y和z是什么類型都未確定。該定義只是描述了該函數模板具有處理一些在<類型形式參數表>中所列出的數據類型的能力。即函數模板定義僅反映了該函數模板的能力。在聲明或定義函數模板的參數類型時,用class或typename都可以。關鍵字typename是最近才被引入到C++中的,引入它的目的是因為有時需要靠它來指導編譯器解釋模板定義,使得我們可以對模板定義進行分析。這個討論超出了本書的范圍,想進一步了解此內容的讀者可閱讀Stroustrup的《DesignandEvolutionofC++》一書[4]。4.2.2模板函數

模板函數(templatefunction)是函數模板的一個實例,是一個具體函數的定義,它可由編譯系統(tǒng)生成程序代碼。模板函數才是真正的函數,是實體。函數模板是一個還沒有確定數據類型的框架,而在實現模板函數時則必須確定數據類型。確定方式就是將類型作為實參傳給形參。傳出實參的方式有兩種:

方法一:

函數名(形式參數表);

方法二:

函數名<類型形式參數表>(形式參數表);我們將方法一稱為隱式模板實參,方法二稱為顯式模板實參。若編譯器發(fā)現有一個函數調用,例如:函數名(實在參數表);,將根據實在參數表中的類型,確認匹配哪個函數模板中對應的形式參數表,然后生成一個重載函數。該重載函數的定義體與函數模板的函數定義體相同,而形式參數表的類型則以實在參數表的實際類型為依據。該重載函數稱為模板函數。

1.隱式模板實參

在上面的方法一中,并沒有出現類型形式參數表,參數的類型是由函數隱式給出的,稱此實參為隱式模板實參。

【例4-1】方法一舉例。說明類型參數的確定和實際函數的生成。

#include<iostream>

usingnamespacestd;

template<classT>voidFun(inta,Tb)

{cout<<a<<""<<b<<endl;}voidmain()

{Fun(5,8); //T為int

Fun(4,5.7); //T為float

Fun(6,'A'); //T為char

}注意template<classT>voidFun(inta,Tb),當編譯器讀到該行時就開始創(chuàng)建函數模板,其中T是以后再確定的類型。在main函數中分別使用了三種不同的數據類型來調用模板函數,編譯器自動在原位置處創(chuàng)建三個Fun()的版本,如下所示:

voidFun(inta,intb){cout<<a<<“

”<<b<<endl;}

voidFun(inta,doubleb){cout<<a<<“

”<<b<<endl;}

voidFun(inta,charb){cout<<a<<“

”<<b<<endl;}

也就是說,編譯器為我們編寫了三個函數。這三個模板函數是實際函數,它們將占用內存空間。由此可見,使用模板是為了簡化編程,并沒有改變實際代碼?!纠?-2】演示帶返回值的模板函數。

#include<iostream>

#include<string>

usingnamespacestd;

template<classT>TFun(inta,Tb)

{cout<<a<<""<<b<<endl;returnb;}voidmain()

{intx=8;chary[]=“DesignofC++”;

strings=“Language”;

Fun(1,&x);

//將T定為int*,將輸出地址

Fun(2,y);

//將T定為char*,將輸出DesignofC++

cout<<Fun(3,s)<<endl;//將T定為string,將輸出Language

}例4-2定義了一個名為Fun的函數模板,該函數的返回類型為T。該函數具有處理整型和T類型變量的能力。在main函數中定義了一個模板函數Fun,并將T確定為整型指針、字符型指針和string類型。在實例化函數模板時,要求模板函數的函數名與函數模板名同名且有相同的參數個數。通過例4-2可見,T分別成為了int*、char*和string類型的代名詞。

在實例化函數模板時,函數實參的類型決定了模板參數的類型和值,當然其中也允許有一些參數類型轉換。

【例4-3】說明經過轉換來確定類型參數的方法。#include<iostream>

usingnamespacestd;

template<classT>voidFun(inta,constT*b)

{cout<<b<<endl;}

voidmain()

{char*p="DesignofC++";

intz[]={1,2,3,4};

Fun(1,p); //從char*轉換到constchar*,將輸出“DesignofC++”

Fun(3.3,z); //數組到同類型指針的轉換,將輸出z的地址

}該例中將double型的3.3傳給int型的a,并不是強制轉換,而是自動進行的。因為int是一個已知的內部類型,在模板中被看做常量或常量表達式,因此只要能得到一個該類型的數據或者常量便可,但變量不行。

2.顯式模板實參

有時編譯器不能隱式得到模板實參類型,在這種情況下就要顯式指定。指定方法就是前面介紹的方法二。

【例4-4】方法二舉例。說明顯式模板實參的需要性與提供方法。#include<iostream>

usingnamespacestd;

template<classTA,classTB,charz>voidFun(floatx,TAy);

//聲明,必須給出變量名

voidmain()

{constchara='4';charb='4';floatc=3.4f;

Fun<char*,unsignedint,a>(3.4f,"divide"); //ok,a是常量

//Fun<char,int,b>(3.4f,"divide"); //error,b是變量

Fun<char*,int,'4'>(c,"divide"); //ok

}

template<classTA,classTB,charz>voidFun(floatx,TAy)

{cout<<z<<""<<y<<""<<x<<endl;}通過與方法一中的模板函數定義比較發(fā)現,在方法二中多用了<類型實在參數表>,例如Fun<char,int,'4'>(3.4f,"divide");。其主要原因是:有兩個類型參數TB和z并沒有在形式參數表中出現,也就是不能確定類型,那么函數模板就不能成為模板函數,所以要用<類型實在參數表>告知。告知方法是:對于未確定的類型要給出類型,對于基本類型的實參必須給出常量或常量表達式。也可以將例4-4中的程序修改如下,就可省略<類型實在參數表>:

#include<iostream>

usingnamespacestd;

template<classTA,classTB>voidFun(floatx,TAy,TBz); //聲明

voidmain(){Fun(3.4f,"divide",4);}

template<classTA,classTB>voidFun(floatx,TAy,TBz)

{cout<<z<<""<<y<<""<<x<<endl;}通過改進我們仍用隱式指定參數類型,但并不是說顯式指定就可以不要了。顯式指定可以明確地規(guī)定函數的實參只能是什么類型。例如:

#include<iostream>

usingnamespacestd;

template<classTA,classTB>voidFun(TAx,TBy)

{cout<<y<<“

”<<x<<endl;}

voidmain()

{Fun<float,char*>(3.4f,"divide");}通過<float,char*>的顯式指定,函數的參數只能是float、char*,若再傳其他類型給函數就會出錯。從中可見,隱式指定就是以默認方式指定;顯式指定就是以強制方式指定。有時我們希望函數有不同的返回類型,也可以用顯式指定的方法。

【例4-5】函數的返回類型TC并沒有在函數參數表中出現,這就要顯式指定TC的類型。

#include<iostream>

usingnamespacestd;

template<classTA,classTB,classTC>TCFun(TAx,TBy)

{TCa=x+y;returna;}

voidmain()

{cout<<Fun<int,double,int>(4,3.4)<<endl;

//指定函數類型為int

cout<<Fun<int,double,double>(4,3.4)<<endl;

//指定函數類型為double

}

運行結果:

7

7.44.2.3函數模板的需要性

通常,函數的參數與類型有關,而與數據大小無關,但函數模板卻是與數據類型無關。因此不管參數類型如何,函數模板都具有相同行為的操作,可見用函數模板來完成相同的工作既方便又簡捷。

1.與宏定義比較

要想實現由一段代碼完成多種類型數據的交換,在C中我們只能用宏來實現。它的缺點是不作類型檢查。對于同樣的數據交換,可用下面帶參的宏定義。#include<stdio.h>

#defineFun(a,b,temp)temp=a;a=b;b=temp;

//帶參宏定義

main()

{inta=6,b=9,c;

floatu=6.6f,v=9.9f,w;

charx='A',y='a',z;

Fun(a,b,c);

//交換整型

printf("a=%d,b=%d\n",a,b);

Fun(u,v,w); //交換實型

printf("u=%f,v=%f\n",u,v);

Fun(x,y,z); //交換字符型

printf("x=%c,y=%c\n",x,y);

}它們之所以能得到正確的結果,其原因是a、b和temp為同一數據類型。當a、b和temp為不同類型數據時,也會發(fā)生數據交換,但結果是錯誤的。C中的宏不作類型檢查,它不管a、b與temp是什么類型,都作交換,其結果有可能是錯的。而函數模板具有類型檢查的能力,它解決了通用性與嚴密性之間的矛盾。

2.與重載比較

重載時要求程序員寫出多個同名函數,而用模板時只需寫一個,即函數模板是一個框架。有了函數模板之后,重載也就顯得不那么必要了。4.2.4函數模板的應用

通過前面小節(jié)的分析,可以總結出在應用函數模板時要分兩步實現:第一步,先定義函數模板,在其中實現所要求的功能,對于一些還不能確定的數據類型只用一個符號作代表;第二步,確定函數模板中可操作的數據類型,從而定義模板函數。

【例4-6】通過數據比較,演示實參是什么數據類型就進行什么類型數據的比較,并返回該類型數據。#include<iostream>

usingnamespacestd;

template<classT>TMax(Ta,Tb)

{returna>b?a:b;}

voidmain()

{cout<<“Max(3.3,8.8)is:”<<Max(3.3,8.8)<<endl;

cout<<“Max(‘3’,‘8’)is:”<<Max(‘3’,‘8’)<<endl;

}

運行結果:

Max(3.3,8.8)is:8.8

Max('3','8')is:8【例4-7】演示用模板函數實現數據交換。

#include<iostream>

#include<string>

usingnamespacestd;

template<classT>

voidFun(T&a,T&b){Ttemp=a;a=b;b=temp;}

//函數模板,參數為引用

voidmain()

{inta=6,b=9;floatc=6.6f,d=9.9f;chare=‘A’,f=‘a’;

stringsa=“Jonas”,sb=“Landy”;

Fun(a,b);Fun(c,d);Fun(e,f);Fun(sa,sb); //模板函數

cout<<“a=”<<a<<“b=”<<b<<endl;

cout<<“c=”<<c<<“d=”<<d<<endl;

cout<<“e=”<<e<<“f=”<<f<<endl;

cout<<"sa="<<sa<<"sb="<<sb<<endl;

}運行結果:

a=9b=6

c=9.9d=6.6

e=af=A

sa=Landy sb=Jonas

該例正確地完成了任意兩個數據的交換。在交換時若有兩個不同類型的數據要發(fā)生交換,編譯時就報錯,這就解決了類型檢查問題。

【例4-8】演示具有多個參數的模板函數的應用。

#include<iostream>

usingnamespacestd;

template<classType1,classType2>

//有多個參數,要用逗號分隔

Type2Fun(Type1&a,Type2&b,floatc)

//返回類型為Type2,它有三個類型形參

{cout<<“a=”<<a<<“

”<<“c=”<<c<<endl;returnb;}

voidmain()

{inta=6;charb=‘A’;floatc=6.6f;

cout<<“b=”<<Fun(a,b,c)<<endl;

}

運行結果:

a=6c=6.6

b=A

【例4-9】演示在類型形式參數表中包含基本數據類型的使用方法。

#include<iostream>

usingnamespacestd;

template<classType,intn> //函數模板

TypeFun(Typea[])

{Typemin=10,max=0;intj=0,k=0;

for(inti=0;i<n;i++)本例在Fun()中應該給出類型;因為形參中有一個是基本類型intn,所以要給出數據?;绢愋偷膶崊⒈仨毷且粋€常量或常量表達式,或者必須在編譯時就能確定其值。若模板參數不能在編譯時確定,就會出錯。例如:

#include<iostream>

usingnamespacestd;

template<int*p>voidFun(floaty) //int*是基本類型

{cout<<y<<endl;}

voidmain()

{Fun<newint(57)>(3.4f);} //error,編譯時57所在的地址不能被確定修改如下:

#include<iostream>

usingnamespacestd;

template<int*p>voidFun(floaty){cout<<y<<endl;}

intx=57;

voidmain()

{Fun<&x>(3.4f);} //ok,編譯時x的地址是常量并且已被確定在類型形式參數表中包含基本類型,這種方法目前不常用,在此僅作為一種討論。對于例4-9,建議改用如下方法:

#include<iostream>

usingnamespacestd;

template<classType>TypeFun(Typea[],intn){同例4-9}

voidmain()

{floatd[]={9.98f,8.92f,8.87f,9.32f,9.75f,8.15f};

cout<<"最后得分:"<<Fun(d,sizeof(d)/sizeof(d[0]))<<"分"<<endl;

}

【例4-10】求an。第一次調用參數是int型,結果是long型。第二次調用參數和結果都是double型。

#include<iostream>

usingnamespacestd;

template<classBase,classSum>Sumpower(Basea,intn)

{Sumsum=1;

for(inti=1;i<=n;i++)

sum*=a;

returnsum;

}voidmain()

{cout<<power<int,long>(2,10)<<endl;

cout<<power<double,double>(10,9)<<endl;

}函數可以重載,同樣地,模板函數也可以被重載,通過重載就可以在不改變原程序的情況下執(zhí)行新的功能。在模板函數與普通函數之間也能重載,這樣在使用模板函數時就如同使用普通函數一樣方便,而且有關函數的功能和性質對模板函數都適用。

在模板函數之間也可以實現重載,其中并沒有什么新的內容,只要符合重載的規(guī)則就可以了。

【例4-11】演示模板函數之間的重載。通過重載,分別從兩個數、三個數或一個字符串中找出最大值。4.3重載模板函數#include<iostream>

usingnamespacestd;

template<classT>TMax(Ta,Tb) //兩個參數

{returna>b?a:b;}

template<classT>TMax(Tx,Ty,Tz) //三個參數

{Tm;

if(x<y)m=(y<z)?z:y;

elsem=(x<z)?z:x;

returnm;

}charMax(char*p)

{charc;

for(c=*p;*p!='\0';p++)

if(*p>=c)c=*p;

returnc;

}

voidmain()

{cout<<"Themaxis"<<Max(9.7,9.1)<<endl; //Max<float>(float,float)

cout<<"Themaxis"<<Max(9.7,9.1,9.8)<<endl; //Max<float>(float,float,float)

cout<<"Themaxis"<<Max("program")<<endl; //Max(char*p)

}模板函數在重載時不能根據參數類型和參數順序來區(qū)分不同的匹配,而要根據參數個數來區(qū)分。對于模板函數之間的重載,匹配時要考慮優(yōu)先級。若是嚴格匹配,則優(yōu)先匹配該函數,否則再尋找轉換匹配模板函數。例如:

#include<iostream>

usingnamespacestd;

template<classT>voidFun(constT*b){cout<<b;cout<<“\tIamconsrT*”<<endl;}

template<classT>voidFun(T*b){cout<<b;cout<<“\tIamT*”<<endl;}

voidmain()

{Fun(“DesignofC++”);}//T為char*,將輸出DesignofC++

IamT*對于Fun("DesignofC++");來說,Fun(T*b)是嚴格匹配,Fun(constT*b)是轉換后的匹配。對于模板函數與非模板函數之間的重載,在匹配時同樣有優(yōu)先級。若非模板函數是嚴格匹配,則優(yōu)先匹配非模板函數,否則匹配模板函數。

【例4-12】演示匹配優(yōu)先級。#include<iostream>

usingnamespacestd;

template<classT>TMax(Ta,Tb){cout<<“Thetemplateis:”;returna>b?a:b;}

doubleMax(doublea,doubleb){cout<<“TheFuntionis:”;returna>b?a:b;}

voidmain()

{cout<<Max(2.2,4.4)<<endl; //匹配非模板函數Max(double

a,doubleb)

cout<<Max(2,4)<<endl; //匹配模板函數Max<int>(2,4)

cout<<Max(2.2f,4.4f)<<endl;//匹配模板函數Max<float>(2.2f,4.4f)

}4.4.1類模板

1.類模板的定義

類模板定義的一般形式為:

template<類型形式參數表> class類標識{類聲明體};

其中,類型形式參數表說明了該類模板可接受的類型。例如:

template<classT>classclassName{voidMemberFuncName(intx,floaty,Tz);}4.4類模板與模板類

2.類模板的實現

類模板的實現即成員函數的定義,一般形式為:

template<類型形式參數表>函數返回類型類標識<類型名表>::函數名(形式參數表){成員函數定義體}

例如:

template<classT>voidclassName<T>::MemberFuncName(intx,floaty,Tz){成員函數定義體};

其中,className<T>是類名。觀察上面兩步可以看出,類模板的定義就是普通類的定義再加上template<類型形式參數表>標記。定義成員函數時,在原類名處用類標識<類型名表>代替。

經過上面兩步,就完成了類模板的說明(包括成員函數的定義),也是類的描述。其中,template<classT>是類模板標記,className<T>是類模板的類名。但是,類模板是一個未確定參數類型的類。4.4.2模板類

1.模板類的定義

類模板定義之后也只是一個框架,是一個沒有規(guī)定具體類型的框架。類模板不是對象,它不占用內存空間。那么要明確一下該框架是一個什么類,框架中可操作什么類或類型數據,這就是模板類的任務。

模板類明確了類模板可對哪些類型數據進行操作,也可以說模板類是類模板的類型化,它把一個框架變?yōu)橐粋€實際的類。

在有了類模板的定義之后,使用className<float>就是定義模板類。該模板類將未知類型明確為float型。但是,要注意它是模板類,還是類,它只不過使得類模板中的未知參數類型得到了確定。

2.模板類對象的創(chuàng)建

將模板類實例化后就成為該模板類的對象。該對象的產生經歷了類模板→模板類→對象的過程,從概念上講還是由類來定義的。

創(chuàng)建模板類對象的方法是:

類名<類型實在參數表>對象名其中,后半部分的對象名由程序員自定義,前半部分的類名<類型實在參數表>稱為模板類。

類型實在參數表的作用是說明所建類的類型。例如className<float>object;就確定了float參數的類,并用該類創(chuàng)建了一個對象object。若要創(chuàng)建多個對象可以用逗號分隔,例如className<float>object1,object2,object3;。下面是一個簡單的進行數據交換的例子。

【例4-13】交換不同類型的數據。Exch<string>ObjectC("jonas","Landy");

ObjectA.Exchange(); //調用成員函數

ObjectB.Exchange();

ObjectC.Exchange();

ObjectA.Display();

ObjectB.Display();

ObjectC.Display();

}運行結果:輸出1.1~6.1(間隔為1)和ASCII值為1~6的字符。

在主函數中的Array<float>floatArray,就是通過模板類Array<float>定義了一個叫floatArray的對象。其中的<類型名表>中的實參是類型,而不是變量或對象。數組類模板是還沒有確定類型的數組,在使用中既可以將它作為實型,也可以將它作為字符型等。由例4-14可以得出類模板的應用方法,主要步驟分為下面四步:①先按原來的方法定義一個數組類和一些相關的通用操作,如插入、刪除和排序等;②將原數組類的定義改為類模板的定義;③將原數組的類型改為T類型;④將原各函數中參數的數據類型改為T類型。4.5.1實現通用棧

棧是十分常見的一種數據結構形式。在我們使用棧的時候,必須為它制定一種數據類型。當我們要存放不同類型數據時必須建立多個新棧,這樣很不方便。下面通過模板來使我們能輕而易舉地改變棧的數據類型。

【例4-15】通過類模板創(chuàng)建可分別存放實型數據和字符數據的順序棧。4.5類模板的應用//********stack.h********創(chuàng)建一個棧類

#ifndefSTACK_H

#defineSTACK_H

#include<assert.h>

template<classT>classStack //定義類模板

{public:

Stack(int=10);

//構造函數

~Stack(){delete[]pTop;} //析構函數從該例可看出,類定義時使用了template<classT>,其余與普通類的定義沒有區(qū)別。template<classT>的作用就是告訴編譯器,在我的類中要用到一個現在還不能確定的類型T。當在主函數中用到了Stack<float>floatStack(8);時,T就被float所取代。到此,棧floatStack才有了確切的數據類型,于是在構造函數中的pTop=newT[maxsize];語句才解釋為在堆中申請一個單精度數組。4.5.2實現鏈表

鏈表是一種常見的數據結構,一般有兩種方法建立鏈表:復合類和嵌套類。在此介紹嵌套類的實現方法。

【例4-16】創(chuàng)建一個鏈表模板。

//********list.h********創(chuàng)建一個鏈表類

#ifndefLIST_H

#defineLIST_H

#include<iostream>

usingnamespacestd;template<classT>

voidList<T>::Add(T&t)

{Node*temp=newNode; //從堆中申請一個結點

temp->pT=&t;

//將該結點中的指針指向t對象

temp->pNext=pFirst; //將該結點作為鏈表的頭結點

pFirst=temp;

//將頭指針指向該結點

}在成員函數的實現template<classT>voidList<T>::Add(T&t){…}中,template<classT>是類模板,List<T>是類名,T是類模板形參。

//******list.cpp*******實現文件

#include"list.h"

voidmain()

{List<float>floatList; //創(chuàng)建一個對象floatList運行結果:

實型鏈表輸出

6.65.64.63.62.61.6

整型鏈表輸出

6 5 4 3 2 1

刪除3.6后輸出

6.65.64.62.61.64.5.3在類模板中使用混合形參

當某類中具有多個未確定的類型時,可以使用具有多個形參的類模板。對于多個自定義的形參,使用中各形參要用逗號分隔。

【例4-17】演示多個形參的類模板。制一張表格,第一次將數據成員指定為double型,第二次將數據成員指定為char*和char型。

#include<iostream>

usingnamespacestd;

template<classT1,classT2>classTable //定義類模板運行結果:

No:3C++:87.5English:99

No:1C++:93.5English:88

No:8特長:打籃球: 性別:M

No:6特長:游泳: 性別:W

【例4-18】類模板的參數也可以使用默認參數。

#include<iostream>

usingnamespacestd;4.5.4在類模板中使用友元

在類模板中也可以聲明友元,既可以將普通函數、類和成員函數聲明為類模板的友元,也可以將函數模板和類模板聲明為另一類模板的友元。

【例4-19】演示函數模板作為友元,實現用普通函數模板訪問對象的私有成員。

#include<iostream>

usingnamespacestd;

template<classT>classStud運行結果:

JonasC++program

總成績4.8

【例4-20】演示類模板作為另一類模板的友元。

#include<iostream>

usingnamespacestd;

template<classT>classStud; //類模板的聲明voidmain()

{Stud<double>ObjA;

Teacher<double>ObjB;

ObjB.Set(ObjA,89); //Teacher給出Stud的成績

ObjA.Show();

//Stud查看成績

ObjB.Show(ObjA); //Teacher查看成績

Stud<char*>ObjC;

Teacher<char*>ObjD;

ObjD.Set(ObjC,"C++program");

ObjD.Show(ObjC);

}運行結果:

89

89

c++program

其中語句friendclassTeacher<T>;的語意為Teacher類的實例,即模板類是Stud的友元。由于是實例化,因此在編譯器看到friendclassTeacher<T>時,前面必須有Teacher類的定義,而不是聲明。4.5.5在類模板中使用函數模板

函數或函數模板可以是一個普通類的成員,函數模板也可以作為類模板的成員。用模板作為類的成員,即為成員模板。成員模板的定義方式是:在成員前加關鍵字template及類型形式參數表。

【例4-21】演示成員模板的實現。

#include<iostream>

usingnamespacestd;

template<classT>classStud

{private:運行結果:

Landy34

Landyscore4.5

Landyisgood

本例在Stud中聲明了一個函數模板作為它的成員,這也就是說,有可能在一個Stud的實例——模板類中包含許多名為Show的成員函數,例如Stud<char*>::Show(3,4)、Stud<char*>::Show("score",4.5)、Stud<char*>::Show("is","good")等。4.6.1vector類

在標準C++中,vector類是C++標準類庫的一部分,但它不叫數組,而叫向量(vector)。vector類為內置數組提供了一種替代表示。因為向量是一個類模板,所以為了使用vector,就要包含相關的頭文件。例如:

#include<vector>

//要定義向量就要包含頭文件

#include<string>

vector<int>a(10);

//定義了包含10個整型對象的向量a

vector<string>b(10);

//定義了包含10個字符串對象的向量b4.6標準模板庫STL向量與數組的主要區(qū)別是:第一,vector向量可以在運行時動態(tài)增長,而數組長度的概念是靜態(tài)的,定義的同時必須指定大??;第二,vector代表了設計方法的重要轉變,它并沒有提供一個巨大的操作集,而只提供了一個最小集,包括等于、小于、size()、empty等操作。能應用到向量中的操作是相當多的,但是它們并沒有作為vector類模板的成員函數提供,而是以一個獨立的泛型算法集的形式由標準庫提供,如表4-1所示。表4-1vector類成員函數表(泛型算法集)

1.數組習慣

數組習慣的使用方法如:vector<int>a(9);,即創(chuàng)建了一個整型數組a,這與inta[9]相似,而且其使用方法也如同數組一樣。除此之外,我們還可以使用vector類的成員函數。

【例4-22】演示成員函數begin()、size()和泛型算法find()的使用方法。

#include<vector>

#include<algorithm>

#include<iostream>

usingnamespacestd;

voidmain()訪問vector中的元素時要注意下標不能越界。例如下面的程序雖然在編譯時可以通過,但在運行中就會出錯,因為d的最大下標為4。我們只能索引vector中已經存在的元素。

#include<vector>

#include<iostream>

usingnamespacestd;

voidmain()

{vector<int>d(5);d[4]=5;

//d[5]=6; //error,后面將介紹調用函數的方法

cout<<d.size()<<endl; //輸出5

d.resize(2*d.size()); //resize為重新設置容器長度

d[5]=6; //正確

}

【例4-23】演示拷貝的方法。

#include<iostream>

#include<vector>

usingnamespacestd;{vector<int>a(10,-1); //把數組a內的10個元素都初始化為-1

Show(a);cout<<endl;

intb[]={9,7,3,6,2,8,5,1,4,3};

vector<int>d(b,b+10);//把數組b內的b[0]~b[9]元素拷貝到d中

Show(d);cout<<endl;

vector<int>e(&b[0],&b[3]);

//將數組b內的b[0]~b[2]元素拷貝到e中

Show(e);cout<<endl;

}經過拷貝后,d的長度為10,e的長度為3。我們也可以將一個vector拷貝給另一個vector,但是不能將數組拷貝給一個vector。例如:

voidmain()

{vector<int>d(10);

intb[]={9,7,3,6,2,8,5,1,4,3};

vector<int>e(d); //ok

vector<int>f(b); //error,b是數組,不是向量,要給出一

段地址空間

}

當我們指定了vector的大小后,任何一個插入操作都將增加vector的長度,而不是覆蓋掉某個現有的元素?!纠?-24】演示vector的動態(tài)增長。

#include<vector>

#include<iostream>

usingnamespacestd;

voidmain()

{vector<int>a(5);

cout<<a.size()<<endl;//輸出5

for(inti=0;i<7;++i)

a.push_back(i);//使用推入函數push_back,進行逐個插入

cout<<a.size()<<endl;//輸出12

}推入函數push_back的作用是向順序容器尾部插入一個元素,即只能將新增元素插入到最后。凡是使用push和pop等都是按規(guī)定進行操作。程序結束時a中共有12個元素,a中放入的元素從a[5]起,即從最后位置開始。若想要插入到中間位置,可以使用如下方法:

#include<vector>

#include<iostream>

usingnamespacestd;

voidmain()

{intb[]={11,22,33};vector<int>a(5,-1);

a.insert(a.begin(),66); //在首位插入66

a.insert(a.begin()+a.size()/2,b,b+3);//在中間位置插入b的內容

for(inti=0;i<a.size();++i)

cout<<a[i]<<“

”;

}

運行結果:

66-1-1112233-1-1-1

insert函數有兩種形式。插入單個內容:insert(參數1,參數2);,其中,參數1給出插入位置,參數2給出插入內容。插入一段內容:insert(參數1,參數2,參數3);,其中,參數1給出插入位置,參數2給出被插入區(qū)間的開始位置,參數3給出被插入區(qū)間的結束位置。

對于插入一段內容,若參數2給出一個數據而非地址,則表示插入者的個數,同時參數3就成為被插入的值。例如:

vector<int>a(5,-1);

a.insert(a.begin()+1,3,89);

//從第二位,即a[1]起插入3個89

2.STL習慣

vector在STL中的用法完全不同。我們定義一個空的vector,然后用插入函數對它賦值。

【例4-25】演示vector在STL中的用法。

#include<iostream>

#include<string>

#include<vector>

usingnamespacestd;

【例4-26】演示vector的刪除。

刪除元素用erase()或pop_back。erase刪除由iterator指向的一段區(qū)間。pop_back刪除容器的最后一個元素,它并不返回該元素。

#include<iostream>

#include<algorithm>

#include<vector>

usingnamespacestd;

voidmain()

{vector<int>a(5,-1);4.6.2string類

string類是C++庫函數中的字符串類,它包含了字符處理操作。它可提供的操作為:支持用字符序列或字符串對象初始化另一個字符串對象;支持字符串之間的拷貝;支持讀/寫單個字符;支持字符串的比較(即ASCII碼的比較);支持兩個字符串的連接;支持對字符串長度的查詢;支持字符串的判空。使用時只要包含<string>便可。有關它的使用可以參考C++類庫。

【例4-27】演示字符串的連接。

#include<iostream>

#include<string>

usingnamespacestd;

voidmain()

{stringa=“Ilearn”,b=“C++language”;

a=a+b;

cout<<a<<endl;

}

【例4-28】演示一些字符串的常用操作。

#include<iostream>

#include<string>

usingnamespacestd;

voidmain()

{stringa("ThisisC++program");//用字符串初試化對象

cout<<"Thesizeofa:"<<a.size()<<endl;//不含\0

stringb(a); //用對象a初試化對象b

cout<<b<<endl;

if(b==a)cout<<"b=a"<<endl;//字符串比較

elsecout<<"b!=-a"<<endl;

stringc;if(c.empty())cout<<"cisempty"<<endl;

c=a; //拷貝

if(!c.size())cout<<"cisempty"<<endl;

elsecout<<"cisn'tempty"<<endl;

char*p="Jonas";

stringd="study";

stringe("C++");

d=p+d+e+"\n"; //連接cout<<d;

stringf;f=p; //不能是p=f;

cout<<f<<endl;

f=a.substr(8,3); //從第8位置處開始,長度為3的子串

cout<<f<<endl;

a.at(12)='P'; //將12位置處的字符改為P

cout<<a<<endl;

}運行結果:

Thesizeofa:19

ThisisC++program

b=a

cisempty

cisn‘tempty

JonasstudyC++

Jonas

C++

ThisisC++Program4.6.3complex類

標準C++類庫提供的complex類(復數類)是基于對象的抽象類的完美模型。通過重載操作符,我們幾乎可以像使用內部類型一樣使用complex類對象。

【例4-29】演示對complex類的操作。

#include<iostream>

#include<complex>

usingnamespacestd;

voidmain(){complex<double>a(2,3); //a=2+3i

complex<float>b(0,3); //b=0+3i

complex<int>c; //c=0+0i

complex<double>d(a); //d=2+3i

complex<double>e[2]={complex<double>(1,2),complex<double>(3,4)};

complex<double>*p=&e[0]; //指針a+=d; //支持復合運算符

cout<<a<<endl;

//cout<<*p<<endl; //輸出(1,2)

//cout<<*++p<<endl;//輸出(3,4)

a=*p+*++p;

cout<<a<<endl; //輸出(6,8)

doublex=a.real(); //用doublex=real(a);也可

doubley=a.imag(); //與doubley=imag(a);等價

cout<<x<<""<<y<<endl;

}4.6.4stack類

stack類是堆棧類。標準C++類庫提供的stack類是基于對象的抽象類的類模板。它的操作原則是后進先出。通過包含相關頭文件#include<stack>,我們幾乎可以像使用內部類型一樣使用stack類對象。在C++中我們稱stack類為棧容器。表4-2列出了stack類的成員函數。表4-2stack類成員函數表【例4-30】演示對棧類的操作。

#include<iostream>

#include<stack>

usingnamespacestd;

voidmain()

{stack<int>a;

for(inti=1;i<8;++i)

a.push(i);

cout<<a.size()<<endl;

while(!a.empty())

{cout<<a.top()<<"";a.pop();}

cout<<'\n'<<a.size()<<endl;

}4.6.5queue類

queue是隊列類。標準C++類庫提供的queue類是基于對象的抽象類的類模板。它的操作原則是先進先出。通過包含相關頭文件#include<queue>,我們幾乎可以像使用內部類型一樣使用queue類對象。在C++中我們稱queue類為隊列容器。表4-3列出了queue類的成員函數。表4-3queue類成員函數表【例4-31】演示隊列類的操作。

#include<iostream>

#include<queue>

usingnamespacestd;

voidmain()

{queue<int>a;

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論