C++ Primer 学习笔记_75_模板与泛型编程 --模板定义
模板與泛型編程
--模板定義
引言:
所謂泛型程序就是以獨立于不論什么特定類型的方式編寫代碼。使用泛型程序時,我們須要提供詳細(xì)程序?qū)嵗僮鞯念愋突蛑怠?/span>
模板是泛型編程的基礎(chǔ)。使用模板時能夠無須了解模板的定義。
泛型編程與面向?qū)ο缶幊桃粯?/span>,都依賴于某種形式的多態(tài)性。面向?qū)ο缶幊讨械亩鄳B(tài)性在執(zhí)行時應(yīng)用于存在繼承關(guān)系的類。我們能夠編寫使用這些類的代碼,忽略基類與派生類之間類型上的差異。僅僅要使用基類的引用或指針,基類類型或派生類類型的對象就能夠使用同樣的代碼。
在泛型編程中,我們所編寫的類和函數(shù)能夠多態(tài)地用于跨越編譯時不相關(guān)的類型。一個類或一個函數(shù)能夠用來操縱多種類型的對象。標(biāo)準(zhǔn)庫中的容器、迭代器和算法是非常好的泛型編程的樣例。標(biāo)準(zhǔn)庫用獨立于類型的方式定義每一個容器、迭代器和算法,因此差點兒能夠在隨意類型上使用標(biāo)準(zhǔn)庫的類和函數(shù)。
在C++中,模板是泛型編程的基礎(chǔ)。模板是創(chuàng)建類或函數(shù)的藍(lán)圖或公式。
編寫重載函數(shù):
int compare(const string &v1,const string &v2) {if (v1 < v2){return -1;}else if (v1 > v2){return 1;}return 0; }int compare(const double &v1,const double &v2) {if (v1 < v2){return -1;}else if (v1 > v2){return 1;}return 0; }這些函數(shù)差點兒同樣,它們之間唯一的差別是形參的類型,每一個函數(shù)的函數(shù)體是同樣的。
每一個要比較的類型都須要反復(fù)函數(shù)的函數(shù)體,不僅麻煩并且easy出錯。更重要的是,須要事先知道空間可能會比較哪些類型。假設(shè)希望將函數(shù)用于未知類型,這樣的策略就不起作用了。
一、定義函數(shù)模板
我們能夠不用為每一個類型定義一個新函數(shù),而是定義一個函數(shù)模板。函數(shù)模板是一個獨立于類型的函數(shù),能夠作為一種方式,產(chǎn)生函數(shù)的特定類型版本號。
template<typename T> int compare(const T &v1,const T &v2) {if (v1 < v2){return -1;}else if (v1 > v2){return 1;}return 0; }模板定義以keywordtemplate開始,后接模板形參表,模板形參表是用尖括號括住的一個或多個模板形參的列表,形參之間以逗號分隔。并且模板形參表不能為空。
1、模板形參表
模板形參表類似于函數(shù)形參表,表示能夠在類或函數(shù)的定義中使用的類型或值。比如,compare 函數(shù)聲明一個名為T的類型形參。在compare內(nèi)部,能夠使用名字T引用一個類型,T表示哪個實際類型由編譯器依據(jù)所用的函數(shù)而確定。
模板形參能夠是表示類型的類型形參,或者是表示常量表達(dá)式的非類型形參。類型形參跟在keywordclass或typename之后定義。
2、使用函數(shù)模板
使用函數(shù)模板時,編譯器會判斷哪個(或哪些)模板實參綁定到模板形參。一旦編譯器確定了實際的模板實參,就稱它實例化了函數(shù)模板的一個實例。
推導(dǎo)出實際模板實參后,編譯器使用實參取代相應(yīng)的模板形參產(chǎn)生編譯該版本號的函數(shù)。
int main () {// 綁定到compare(const int&, const int&)cout << compare(1, 0) << endl;// 綁定到compare(const string&, const string&)string s1 = "hi", s2 = "world";cout << compare(s1, s2) << endl;return 0; }3、inline函數(shù)模板
inline說明符放在模板形參表之后、返回類型之前,不能放在keywordtemplate之前。
template<typename T> inline int compare(const T &v1,const T &v2); //OKinline template<typename T> int compare(const T &v1,const T &v2); //Error//P528 習(xí)題16.1 template<typename T> inline T abs(T val) {return val > 0 ? val : -val; }int main () {cout << abs(-1) << endl;cout << abs(-0.98) << endl;cout << abs(short(3.4)) << endl;return 0; }
//習(xí)題16.2 template<typename T> ostream &write(ostream &os,T val) {return os << val << endl; }int main() {write(cout,1);write(cout,12.3);write(cout,"Hello World");ofstream outFile("output");write(outFile,"Hello");write(outFile,123);string temp;ostringstream strStream(temp);write(strStream,"Hello_World");cout << strStream.str() << endl; }
二、定義類模板
為了舉例說明類模板,我們將為標(biāo)準(zhǔn)庫queue類實現(xiàn)一個自己的版本號。
我們自己定義的Queue類必須能夠支持不同類型的對象,所以將它定義為類模板。Queue所能支持的操作:
1)push:在隊尾加入一項
2)pop:從隊頭刪除一項
3)front:返回隊頭的引用
4)empty:指出隊列是否為空
template<typename Type> class Queue { public:Type &front();const Type &front() const;void push(const Type &);void pop();bool empty() const;private://... };類模板也是模板,因此必須以keywordtemplate開頭,后接模板形參表。
除了模板形參表外,類模板的定義看起來與隨意其它類類似。在類和類成員的定義中,能夠使用模板形參作為類型或值的占位符,在使用類時再提供那些類型或值。
使用類模板
與調(diào)用函數(shù)模板形成對照,使用類模板時,必須為模板形參顯式指定實參:
Queue<int> qi;Queue< vector<double> > qc;Queue<string> qs;編譯器使用實參來實例化這個類的特定類型版本號。實質(zhì)上,編譯器用用戶提供的實際特定類型取代Type,又一次編寫Queue類。在這個樣例中,編譯器將實例化三個Queue類:第一個用int取代 Type,第二個用vector<double>取代 Type,第三個用string取代 Type。
//P529 習(xí)題16.5 template<typename T> T getBigger(const T &val1,const T &val2) {return val1 > val2 ? val1 : val2; }//習(xí)題16.6 template<typename Type> class List { public:List();void push_back(const Type &);void push_front(const Type &);std::size_t size() const;void insert(Type *ptr,const Type &val);bool empty();private://... };
三、模板形參
像函數(shù)形參一樣,為模板形參選擇的名字沒有本質(zhì)含義:
template<typename Glorp> int compare(const Glorp &v1,const Glorp &v2) {if (v1 < v2){return -1;}else if (v1 > v2){return 1;}return 0; }該代碼與前面定義的compare模板一樣。
能夠給模板形參授予的唯一含義是差別是類型形參還是非類型形參。假設(shè)是類型形參,我們就知道該形參表示未知類型,假設(shè)是非類型形參,我們就知道它是一個未知值。
假設(shè)希望使用模板形參所表示的類型或值,能夠使用與相應(yīng)模板形參同樣的名字。比如,compare函數(shù)中全部的Glorp引用將在該函數(shù)被實例化時確定為同一類型。
1、模板形參作用域
模板形參的名字能夠在聲明為模板形參之后直到模板聲明或定義的末尾處使用。
模板形參遵循常規(guī)名字屏蔽規(guī)則:
typedef double T; template <class T> T calc(const T &a,const T &b) {//此處T為template形參表中的T,全局名字被屏蔽T tmp = a;//...return tmp; }2、使用模板形參名字的限制
用作模板形參的名字不能在模板內(nèi)部重用:
template <class T> T calc(const T &a,const T &b) {typedef double T; //ErrorT tmp = a;//...return tmp; }這一限制還意味著模板形參的名字僅僅能在同一模板形參表中使用一次:
template <class T,class T> T calc(const T &a,const T &b); //Error
正如能夠重用函數(shù)形參名字一樣,模板形參的名字也能在不同模板中重用:
template <class T> T calc(const T &a,const T &b);template <class T> int compare(const T &,const T&); //OK3、模板聲明
像其它隨意函數(shù)或類一樣,對于模板能夠僅僅聲明而不定義。聲明必須指出函數(shù)或類是一個模板:
template <class T> int compare(const T &,const T&);同一模板的聲明和定義中,模板形參的名字不必同樣:
template <class T> T calc(const T &,const T &);template <typename U> U calc(const U&,const U&);template <class Type> Type calc(const Type &,const Type &);每一個模板類型形參前面必須帶上keywordclass或typename,每一個非類型形參前面必須帶上類型名字,省略keyword或類型說明符是錯誤的:
template<typename T,U> T calc(const T &,const U &); //Errortemplate<typename T,class U> T calc(const T &,const U &); //OK//P531 習(xí)題16.9 template <typename Type,typename T> Type find(Type begin,Type end,const T &val) {while (begin != end){if (*begin == val){return begin;}++ begin;}return end; }int main() {int ia[] = {01,1,1,999,2,3,2,34,4,3,4};int *p;if ((p = find(ia,ia+sizeof(ia)/sizeof(*ia),999)) != ia + sizeof(ia)/sizeof(*ia)){cout << *p << endl;}else{cout << "Not Found!" << endl;}vector<int> iVec(ia,ia + sizeof(ia)/sizeof(*ia));vector<int>::iterator iter;if ((iter = find(iVec.begin(),iVec.end(),888)) != iVec.end()){cout << *iter << endl;}else{cout << "Not Found!" << endl;}ifstream inFile("input");vector<string> strVec;string val;while (inFile >> val){strVec.push_back(val);}vector<string>::iterator it;if ((it = find(strVec.begin(),strVec.end(),"hello")) != strVec.end()){cout << *it << endl;}else{cout << "Not Found!" << endl;} }
總結(jié)
以上是生活随笔為你收集整理的C++ Primer 学习笔记_75_模板与泛型编程 --模板定义的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Visual Studio使用技巧,创建
- 下一篇: setsockopt()使用方法(參数具