OOP筆記(七) 模板與STL
1.函數模板
(1)泛型程序設計:Generic Programming,算法實現時不指定具體要操作的數據的類型
泛型 — 算法實現一遍->適用於多種數據結構
模板定義的語法格式如下:
例:swap函數
template <class T>
void Swap(T & x, T & y)
{
T tmp = x;
x = y;
y = tmp;
}
int main()
{
int n = 1, m = 2;
swap(n,m); //(1)編譯器自動生成 void Swap(int &, int &)函數
//(1)n,m類型確定了模板參數類型int
cout<<"swap;n="<<n<<"; m="<<m<<endl;
double a=3.45,b=6.76;
swap(a,b); //(2)編譯器自動生成 void Swap(double &, double &)函數
//(2)a,b類型確定了模板參數類型double
cout<<"swap;a="<<a<<"; b="<<b<<endl;
return 1;
}
(2)函數模板中可以有不止一個類型參數
(3)顯示指定模板函數變量類型
語法: 函數名<可變類型參數列表>(實參表);
在函數調用時,可以顯式地指定模板參數代表的數據類型,
int main()
{
double x=2.5461;
string str="hello";
print<double,string>(x,str);
}
(4)非類型模板參數
【注意】
1)整型,指針和引用才能作爲非類型形參;
2)而且綁定到該形參的實參必須是常量表達式,即編譯期就能確認結果。
template <class T1,int M> //int M非類型參數
T1 *CreateVector()
{
T1 *p=new T1[M];
return p;
}
template <class T1>
void DeleteVector(T1 *p)
{
delete []p;
}
int main()
{
int *p=CreateVector<int,5>(); //創建整型數組
cout<<"Input 5 int numbers:";
for(int i=0;i<5;i++)
{
cin>>p[i];
cout<<setw(5)<<p[i];
}
cout<<endl;
DeleteVector(p);
return 1;
}
(5)函數模板可以重載, 只要它們的形參表不同即可
(6)函數模板調用順序
類模板
1.如果程序中希望一個類的數據成員的類型不侷限爲某一個具體類型,或者希望某個成員函數的參數或 返回值的類型不侷限爲某一個具體類型,就應該把這 樣的類定義爲類模板。類模板不代表具體的實際的類, 而是代表一類類。
類模板的定義:
語法:
template <class 類型參數表,…>
class 類模板名
{
成員函數和成員變量
};
類型參數表列表的寫法就是:
class 類型參數1, class 類型參數2, …
3、C++類模板定義
類模板裏的成員函數, 如在類模板外面定義時,
template <class 類型參數表>
返回值類型 類模板名<類型參數名列表>::成員函數名(參數表)
{……}
4、C++類模板創建對象
用類模板定義對象的寫法如下:
類模板名 <真實類型參數表> 對象名(構造函數實際參數表);
如果類模板有無參構造函數, 那麼也可以只寫:
類模板名 <真實類型參數表> 對象名;
#include<iostream>
#include<iomanip>
using namespace std;
//定義類模板
template<class T, int maxlen>
//類型參數爲T,最大長度爲maxlen
class Array
{
private:
T *data;
int len;//數組實際長度
public:
Array();//(1)構造函數
~Array();// (2)析構函數
int getLen() const { return len; }
// (3)返回實際長度
void DelArray(int i);
// (4)刪除某下標內容
void InsArray(int i,T x);
// (5)在某位置加入數據
void AddArray(T x);
// (6)在最末位置加入數據
T & operator[](int i) ;
// (7)重載運算符[]
};
//成員函數類外定義
template<class T,int maxlen>
Array<T, maxlen>::Array() ()
// (1)構造函數
{
if(maxlen>0)
data=new T[maxlen];
else data=NULL;
len=0;
}
template<class T,int maxlen>
// (2)析構函數
Array<T,maxlen>::~Array()
{
if(data!=NULL)
delete []data;
}
template<class T,int maxlen>
void Array<T,maxlen>::DelArray(int i)
{// (4)刪除下標爲i的元素
if(len<=0)
return;
for(int j=i+1;j<len;j++)
data[j-1]=data[j];
len--;
}
T & Array<T,maxlen>::operator[](int i) // (7)重載運算符[]
{//返回第i個元素
if(i>=0||i<len)
return data[i];
}
//成員函數類外定義
template<class T,int maxlen>
void Array<T,maxlen>::InsArray(int i,T x)// (5)在下標 i處添加 x
{
if(len>=maxlen||i<0||i>len)
return;
for(int j=len-1;j>=i;j--)
data[j+1]=data[j];
data[i]=x;
len++;
}
template<class T,int maxlen>
void Array<T,maxlen>::AddArray(T x)
{// (6)末尾增加數據
InsArray(len,x);
}
//main函數中進行類模板實例化
int main()//
{
int i,m;
const int n=8;
Array<int,n> a;// (1)用int替換類型參數T,將類模板Array實例化爲對象a a是一個int數組
m=5;
cout<<"Input "<<m<<" integer numbers:"<<endl;
for(i=0;i<m;i++)
cin>>a[i];
//(7)調用運算符重載函數[]
for(i=0;i<a.getLen();i++)
//(3)調用getLen()獲取長度
cout<<setw(6)<<a[i];
cout<<endl;
a.DelArray(3);
//(4)調用DelArray刪除下標爲3的元素
for(i=0;i<a.getLen();i++)
cout<<setw(6)<<a[i]; //遍歷顯示數據
cout<<endl;
a.InsArray(2,-111);
//(5)在下標爲2的地方插入-111
a.AddArray(999);
//(6)在數組尾部添加999
for(i=0;i<a.getLen();i++)
cout<<setw(6)<<a[i]; cout<<endl;
return 1;
}
5、類模板的實例化
編譯器由類模板生成類的過程叫類模板的實例化
• 編譯器自動用具體的數據類型, 替換類模板中的類型參數, 生成模板類的代碼,例如:
Array<int,8>
由類模板實例化得到的類叫模板類
•爲類型參數指定的數據類型不同, 得到的模板類不同
關於類模板的幾點說明:
(1) 類模板名:Array<T, maxlen> (代表一族類)
(2) 模板類名:Array<int, 8>
(即類模板的一種實例化,代表一個具體的類)
(3) 類模板在類外定義成員函數的方法:
template <class T, int maxlen> //每個成員函數重複
返回類型 Array<T,maxlen>::成員函數名(形參表)
{ 函數體}
(5)類模板實例化:只有在用模板類名定義對象或對象指針時,編譯程序纔會對類模板進行實例化,生成一個具體的類。
(6)類模板成員函數的實例化:僅發生在用模板類的一個對象或對象指針調用了該成員函數時。
以上類模板實例化的機制表明,僅有類模板的定義,編譯程序不會產生任何目標代碼。
因此,類模板只能以源程序文件形式保存和重用。
6、模板的模板參數
模板的模板參數就是將一個模板作爲另一個模板的參數,也就是說類型形參可以是類模板。
template<int N, template<int> class F>
class Accumulate
{
public:
enum {RET = Accumulate<N-1,F>::RET + F<N>::RET};
};
template<template<int> class F>
class Accumulate<0,F>
{
public:
enum { RET = F<0>::RET };
};
template<int n>
class square
{
public:
enum { RET = n*n };
};
int main()
{
cout << "1*1 = "<<Accumulate<1,square>::RET << endl;
cout << "1*1+2*2 = "<<Accumulate<2,square>::RET << endl;
cout << "1*1+2*2+3*3 = "<<Accumulate<3,square>::RET << endl;
cout << "1*1+2*2+3*3+4*4 = "<<Accumulate<4,square>::RET << endl;
return 0;
}
STL
使用模板的程序設計法。
將一些常用的數據結構(比如鏈表,數組) 和算法(比如排序,查找)寫成模板,以後則不論數據 結構裏放的是什麼對象,算法針對什麼樣的對象,則都不必重新實現數據結構,重新編寫算法。
標準模板庫 (Standard Template Library) 就是一 些常用數據結構和算法的模板的集合。
容器:可容納各種數據類型【 基本類型的變量, 對象等 】的通用數據結構,如array(數組)(C++內置不屬於STL)、list(串行)、stack(堆棧)、queue(隊列)等,用來存放數據,從實現角度來看,STL容器是一種class template。
迭代器:可用於依次存取容器中元素,類似於指針,所有STL容器都附帶有自己專屬的迭代器
算法:用來操作(如sort、find、copy、for_each)容器中的元素的,從實現的角度來看,STL算法是一種function tempalte
算法本身與他們操作的數據的類型無關,因此他們可以在從簡單數組到高度複雜容器的任何數據結構上使用。
1、序列式容器之一:vector #include、
容器裏面的數據可以進行排序,但是不會自動排序,可以使用算法進行排序;template class vector;
vector(動態數組):元素在內存連續存放。隨機存取任何元素都能在常數時間完成。在尾端增刪元素具有較佳的性能(大部分情況下是常數時間)。
2、序列式容器之二:list #include
雙向鏈表(double linked-list),迭代器必須具備前移、後移的能力template class list;
3、容器配接器之三:stack和queue
stack、queue沒有迭代器,以list作爲以list作爲queue的底層容器, template<classT, class Cont==deque>class stack;
queue<int, list > iqueue;queue q;
4、容器:vector
vector 的數據安排以及操作方式,與C++中的數組非常相似。
區別在於:
(1)C++中的數組是固定大小的,分配給C++數組的空間數量在數組的生存期內是不會改變的。
( 2 )向量vector是動態結構,它的大小不固定,可以在程序運行時增加或減少。
vector中的元素在內存是連續存儲的,可以直接訪問。
例子:
int main()
{
int i;
int a[5] = {1,2,3,4,5};
vector<int>v(5);//初始化5個元素,元素初始值爲0
cout << v.end()- v.begin() << endl;
for( i = 0; i < v.size(); i ++ )
v[i] = i;
v.at(4) = 100;
for( i = 0; i < v.size(); i ++ )
cout << v[i] << "," ; cout << endl;
vector<int> v2(a,a+5);//構造函數
v2.insert(v2.begin() + 2,13); //在begin()+2位置插入13
for( i = 0; i < v2.size(); i ++ )
cout << v2.at(i) << "," ;
return 0;
}