模板
模板使類和函數可在編譯時定義所需處理和返回的數據類型。
一個模板並非一個實實在在的類或函數,僅僅是一個類或函數的描述。
模板一般分爲模板函數和類模板,以所處理的數據類型的說明作爲參數的類叫類模板,以所處理的數據類型的說明作爲參數的函數,稱爲函數模板。
模板的一般定義形式爲:
template <類型形參表> 返回類型 類名<類型名錶>::成員函數名1(形參表){成員函數1定義體}......
模板參數
模板的參數可以是某些類型type或者非類型nontype,類型參數可用typename或class關鍵字指明。如:
template <class T1,class T2,class T3> class classname;
template <typename T1,typenameT2,typenameT3> class classname;
非類型參數就是通常的參數定義,表示在模板實例化爲類時所用的常量。如:
template<class type,int size【=默認值】> class Queue;
typename關鍵字告訴了編譯器把一個特殊的名字解釋成一個類型,在下列情況下必須對一個name使用typename關鍵字:
1、一個唯一的name,嵌套在另一個類型中。
2、依賴於一個模板參數,即模板參數在某種程序上包含這個name。當模板參數使編譯器在指認一個類型時便會產生誤解。
#include <iostream>
using namespace std;
class Y {
public:
class id { //Y類嵌套了id類
public:
void g(){cout<<"hello !"<<endl;}
};
};
template<class T>
class X {
typename T::id i; //告訴編譯器 T::id 爲一個類型
public:
void f(){i.g();}
};
int main() {
Y y;
X<Y> xy;
xy.f();
return 0;
}
運行結果:
hello !
爲了保險起見,在所以編譯器可能錯把一個type當成一個變量的地方使用typename。
函數模板
一般定義形式爲:
template <類型1 變量1,類型2 變量2,類型3 變量3 ......> 返回類型 函數名(形參表){函數定義體}
編譯器根據調用該函數實參數據類型,生成相應的重載函數,該重載函數稱爲模板函數,是一個實實在在的函數。
#include <iostream>
using namespace std;
template<class T>
T min(T ii,T jj,T kk)
{
T temp;
if((ii<jj)&&(ii<kk))
{
temp=ii;
}else if((jj<ii)&&(jj<kk))
{
temp=jj;
}else{
temp=kk;
}
return temp;
}
int main() {
cout<<min(100,20,30)<<endl;
cout<<min(10.60,10.64,53.21)<<endl;
cout<<min('c','a','C')<<endl;
return 0;
}
運行結果:
20
10.6
C
使用函數模板與同名的非模板函數重載,就是函數的定製。定製須遵循以下規定:
1、尋找一個參數完全匹配的函數,找到了就調用它。
2、失敗,尋找一個函數模板,使其實例化,產生一個匹配的模板函數,找到就調用它。
3、失敗,再試低一級的對函數重載的方法,如通過類型轉換可產生的參數匹配等,找到調用它。
4、失敗,則報錯。
#include <iostream>
#include <string.h>
using namespace std;
//函數定製
//模板函數
template<class T>
T min(T ii,T jj,T kk)
{
T temp;
if((ii<jj)&&(ii<kk))
{
temp=ii;
}else if((jj<ii)&&(jj<kk))
{
temp=jj;
}else{
temp=kk;
}
return temp;
}
//非模板函數
const char* min(const char* ch1, const char* ch2,const char* ch3)
{
const char* temp;
int result1=strcmp(ch1,ch2);
int result2=strcmp(ch1,ch3);
int result3=strcmp(ch2,ch1);
int result4=strcmp(ch2,ch3);
if((result1<0)&&(result2<0))
{
temp=ch1;
} else if ((result3<0)&&(result4<0))
{
temp=ch2;
} else{
temp=ch3;
}
return temp;
}
int main() {
cout<<min(100,20,30)<<endl;
cout<<min(10.60,10.64,53.21)<<endl;
cout<<min('c','a','C')<<endl;
cout<<min("Anderson","Washington","Smith")<<endl;
return 0;
}
運行結果:
20
10.6
C
Anderson
函數模板實例
字符串轉換系統:
#include <iostream>
#include <string>
#include <complex>
using namespace std;
//定義了一個函數模板,實現輸入功能
template <typename T>
T fromString(const string& s)
{
istringstream is(s);
T t;
is>>t;
return t;
}
//定義了一個函數模板,實現輸出功能
template <typename T>
string toString(const T& t)
{
ostringstream s;
s<<t;
return s.str();
}
int main() {
int i=1234;
cout<<"i== \" "<<toString(i)<<" \"\n";
float x=567.89;
cout<<"x == \" "<<toString(x)<<" \"\n";
complex<float> c(1.0,2.0);
cout<<"c == \""<<toString(c)<<" \"\n";
cout<<endl;
i=fromString<int>(string("1234"));
cout<<"i == "<<i<<endl;
x=fromString<float>(string("567.89"));
cout<<"x == "<<x<<endl;
c=fromString<complex<float>>(string("(1.0,2.0)"));
cout<<"c == "<<c<<endl;
return 0;
}
運行結果:
i== " 1234 "
x == " 567.89 "
c == "(1,2) "
i == 1234
x == 567.89
c == (1,2)
內存分配系統:
#include <cstdlib>
#include <iostream>
#include <cstring>
using namespace std;
/*
* 內存分配系統,分配的內存分佈如下
* [[一個整型所佔的字節數存放分配的元素個數][分配的存放元素的內存,oldmem指針指向該地方]]
*/
template <class T>
//oldmem表示指針引用,elems表示需要分配的大小
void getmem(T*& oldmem,int elems)
{
typedef int cntr;
const int csz=sizeof(cntr); //獲取一個整型數據所佔的字節數
const int tsz= sizeof(T); //獲取一個元素數據所佔的字節數
if(elems==0) //當傳遞過來的參數爲0時,表示釋放掉該塊內存
{
//oldmem並沒有指向內存的開始空間,把內存的起始空間給了計數器使用,當我們需要釋放掉這塊內存時,需要倒退掉這個指針cntr所佔用的字節數.
free(&(((cntr*)oldmem)[-1]));
return;
}
T* p=oldmem;
cntr oldcount=0;
if(p)
{
cntr* tmp= reinterpret_cast<cntr*>(p); //將oldmem指針轉換成cntr指針
p= reinterpret_cast<T*>(--tmp); //將tmp倒退一個cntr所佔用的字節數,再轉換成T類型指針
oldcount=*(cntr*)p;//獲取P指針最開始的cntr所佔用字節數的數據,用作計數器用。
}
T* m=(T*)realloc(p,elems*tsz+csz); //調用realloc調整空間
if(m==0) //判斷是否分配失敗
{
cout<<"ERROR!"<<endl;
exit(1);
}
*((cntr*)m)=elems;//將需分配的元素個數填進新分配的空間的起始cntr所佔用字節數地方
const cntr increment=elems-oldcount;//獲取增加的空間
if(increment>0)
{
long startadr=(long)&(m[oldcount]);
startadr+=csz;
memset((void*)startadr,0,increment*tsz);//將增加的內存以0填充
}
oldmem=(T*)&(((cntr*)m)[1]); //將分配的內存除掉計數器使用的內存塊賦給oldmem帶出
}
template <class T>
inline void freemem(T* m)
{
getmem(m,0); //傳遞的參數0,則表示釋放掉這塊內存。
}
int main() {
int* p=0;
getmem(p,10);
for(int i=0;i<10;i++)
{
cout<<p[i]<<' ';
p[i]=i;
}
cout<<'\n';
getmem(p,20);
for(int j=0;j<20;j++)
{
cout<<p[j]<<' ';
p[j]=j;
}
cout<<'\n';
getmem(p,25);
for(int k=0;k<25;k++)
{
cout<<p[k]<<' ';
}
freemem(p);
cout<<'\n';
float* f=0;
getmem(f,3);
for(int u=0;u<3;u++)
{
cout<<f[u]<<' ';
f[u]=u+3.14159;
}
cout<<'\n';
getmem(f,6);
for(int v=0;v<6;v++)
{
cout<<f[v]<<' ';
}
cout<<'\n';
getmem(f,2);
for(int v=0;v<2;v++)
{
cout<<f[v]<<' ';
f[v]=v+0.1234;
}
cout<<'\n';
getmem(f,8);
for(int v=0;v<8;v++)
{
cout<<f[v]<<' ';
}
freemem(f);
return 0;
}
運行結果:
0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 0 0 0 0 0
0 0 0
3.14159 4.14159 5.14159 0 0 0
3.14159 4.14159
0.1234 1.1234 0 0 0 0 0 0
類模板
一般定義形式爲:
template <類型形參表> class 類名 {類聲明體}
建立類模板之後,可以用類型實參定義模板類,並創建實例。
在類模板的使用中,需要注意以下幾個問題:
1、所以出現類模板的地方,不能直接用類名,需要在類名後面加上<>,指定模板類型
2、在類模板定義體中可省略<>
3、在一個類模板的各個實例之間沒有特殊的聯繫(形成一個個獨立的類)
4、模板的實例可用在一般類可以使用的任何地方,用模板實例定義的對象和一般的對象用法完全相同。
5、實例化的時機:在需要時實例化,比如定義指針或引用不需要實例化,定義具體的變量或常量時會實例化,而訪問對象的成員時會實例化。
類模板中的友元定義有很多情況,主要分以下幾種情形:
1、非模板函數、類成爲所以實例類的友元。
2、模板函數、模板類成爲同類型實例類的友元。
3、模板函數、類成爲不同類型實例類的友元。
類模板實例
#include <iostream>
#include <ostream>
using namespace std;
template <class Type> class Queue;
template <class Type> class QueueItem;
template <class Type> ostream& operator<<(ostream& os,const Queue<Type>& item);
template <class Type>
class Queue
{
public:
Queue():front(NULL),back(NULL){}
~Queue();
Type& remove();
void add(const Type&);
bool is_empty(){return front==NULL;}
protected:
friend ostream& operator<<<>(ostream& os, const Queue<Type>& item);
private:
QueueItem<Type> *front;
QueueItem<Type> *back;
};
template <class Type>
void Queue<Type>::add(const Type& newItem)
{
QueueItem<Type> *pNew=new QueueItem<Type>;
pNew->item=newItem;
pNew->nextItem=NULL;
if(this->front==NULL)
{
this->front=this->back=pNew;
} else{
this->back->nextItem=pNew;
this->back=pNew;
}
}
template <class Type>
Queue<Type>::~Queue()
{
QueueItem<Type> *p,*q;
p=front;
while(p!=NULL)
{
q=p->nextItem;
delete p;
p=q;
}
}
template <class Type>
Type& Queue<Type>::remove()
{
QueueItem<Type> *pNew=this->front;
this->front=this->front->nextItem;
return *pNew;
}
template <class Type>
ostream& operator<<(ostream& os, const Queue<Type>& item)
{
QueueItem<Type>* pNew=item.front;
while(pNew!=item.back)
{
os<<pNew->item<<"\t";
pNew=pNew->nextItem;
}
os<<pNew->item<<endl;
return os;
}
template <class Type>
class QueueItem
{
public:
Type item;
QueueItem* nextItem;
};
int main() {
Queue<int> IntQueue;
IntQueue.add(5);
IntQueue.add(6);
IntQueue.add(7);
IntQueue.add(8);
cout<<IntQueue<<endl;
return 0;
}
運行結果:
5 6 7 8
#include <iostream>
#include <ostream>
using namespace std;
class A
{
int j;
public:
A(){}
A(int x):j(x){}
A(A* x){j=x->j;}
void operator!(){cout<<"J="<<j<<endl;}
};
template <class T>
class B
{
int i;
T *X;
public:
B(int xa,T *p):i(xa){X=new T(p);}
void operator!(){cout<<"I="<<i<<endl;!*X;}
};
int main() {
A a(1);
B<A> b(2,&a);
!b;
return 0;
}
運行結果:
I=2
J=1
#include <iostream>
using namespace std;
class Student
{
int number;
static Student *ip;
Student *p;
public:
Student(){p=NULL;}
Student(int n);
static Student* get_first(){return ip;}
int get_number(){return this->number;}
Student* get_next(){return this->p;}
};
Student::Student(int n):number(n)
{
p=NULL;
if(ip==NULL)
{
ip=this;
}else{
Student *temp=ip;
if(n<ip->number)
{
ip=this;
p=temp;
} else{
while(temp)
{
if(n<temp->p->number)
{
p=temp->p;
temp->p=this;
break;
}else{
if(temp->p->p==NULL)
{
temp->p->p=this;
break;
}
}
temp=temp->p;
}
}
}
}
Student* Student::ip=NULL;
template <class T>
class Class
{
int num;
T *p;
public:
Class(){}
Class(int n):num(n){p=NULL;}
T* insert(int n){p=new T(n);return p;}
void list_all_member(T* x)
{
T *temp=x;
while(temp)
{
cout<<temp->get_number()<<",";
temp=temp->get_next();
}
}
};
int main() {
Class<Student> t_class(9707);
t_class.insert(23);
t_class.insert(12);
t_class.insert(38);
t_class.insert(22);
t_class.insert(32);
t_class.list_all_member(Student::get_first());
return 0;
}
運行結果:
12,22,23,32,38,
模板安全
模板根據參數的類型進行實例化,因爲通常事先不知道其具體類型,所以也無法確切知道將在哪兒產生異常。
注意: 對構造函數或析構函數上的function-try-block,當控制權到達了異常處理函數的結束點時,被捕獲的異常被再次拋出。對於一般的函數,此時是函數返回,等同於沒有返回值的return語句,對於定義了返回類型的函數此時的行爲未定義。
類模板實例
擴展vector功能:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
template <class T>
class Sorted:public vector<T>
{
public:
void sort();
};
template <class T>
void Sorted<T>::sort()
{
for(int i=this->size();i>0;i--)
{
for(int j=1;j<i;j++)
{
if(this->at(j-1)>this->at(j))
{
T t=this->at(j-1);
this->at(j-1)=this->at(j);
this->at(j)=t;
}
}
}
}
int main() {
Sorted<int> is;
for(int i=15;i>0;i--)
{
is.push_back(i);
}
for(int l=0;l<is.size();l++)
{
cout<<is[l]<<"\t";
}
cout<<endl;
is.sort();
for(int l=0;l<is.size();l++)
{
cout<<is[l]<<"\t";
}
cout<<endl;
return 0;
}
運行結果:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
list容器類設計:
#include <iostream>
#include <ostream>
#include <string>
using namespace std;
template <class T>
class ListElement
{
public:
T data;
ListElement<T>* next;
ListElement(T& i_d,ListElement<T>* i_n):data(i_d),next(i_n){}
ListElement<T>* copy()
{
return new ListElement(data,(next? next->copy() : 0));
}
};
template <class T>
class ListIterator
{
public:
T operator()();
int operator++();
int operator!();
ListElement<T>* rep;
};
template <class T>
T ListIterator<T>::operator()()
{
if(rep)
{
return rep->data;
} else{
T tmp;
return tmp;
}
}
template <class T>
int ListIterator<T>::operator++()
{
if(rep)
{
rep=rep->next;
}
return (rep!=0);
}
template <class T>
int ListIterator<T>::operator!()
{
return (rep!=0);
}
template <class T> class List;
template <class T>ostream& operator<<(ostream& os,const List<T>& l);
template <class T>
class List
{
public:
friend class ListIterator<T>;
List();
List(const List&);
~List();
List<T>& operator=(const List<T>&);
T head();
List<T> tail();
void add(T&);
friend ostream& operator<<<>(ostream& os,const List<T>& l);
public:
void clear();
ListElement<T>* rep;
};
template <class T>
List<T>::List()
{
rep=0;
}
template <class T>
List<T>::List(const List<T>& l)
{
rep=l.rep? l.rep->copy() : 0;
}
template <class T>
List<T>& List<T>::operator=(const List<T>& l)
{
if(rep!=l.rep)
{
clear();
rep=l.rep? l.rep->copy() : 0;
}
return *this;
}
template <class T>
List<T>::~List()
{
clear();
}
template <class T>
void List<T>::clear()
{
while(rep)
{
ListElement<T>* tmp=rep;
rep=rep->next;
delete tmp;
}
rep=0;
}
template <class T>
void List<T>::add(T& i)
{
rep=new ListElement<T>(i,rep);
}
template <class T>
T List<T>::head()
{
if(rep)
{
return rep->data;
} else{
T tmp;
return tmp;
}
}
template <class T>
List<T> List<T>::tail()
{
List<T> tmp;
if(rep)
{
if(rep->next)
{
tmp.rep=rep->next->copy();
}
}
return tmp;
}
template <class T>
ostream& operator<<(ostream& os,const List<T>& l)
{
if(l.rep)
{
ListElement<T>* p=l.rep;
os<<"< ";
while(p)
{
os<<p->data<<"\t";
p=p->next;
}
os<<">\n";
} else
{
os<<"Empty list\n";
}
return os;
}
int main() {
List<int> l;
cout<<l;
int i=1;
l.add(i);
i=2;
l.add(i);
i=3;
l.add(i);
cout<<"l is "<<l<<endl;
cout<<"head of l is "<<l.head()<<endl;
List<int> m=l.tail();
List<int> o;
o=m;
i=4;
m.add(i);
cout<<"m is "<<m<<endl;
cout<<"o is "<<o<<endl;
List<char> clist;
char ch;
ch='a';
clist.add(ch);
ch='b';
clist.add(ch);
cout<<clist<<endl;
List<string> ls;
string a("hello");
string b("world");
ls.add(a);
ls.add(b);
cout<<"List of strings"<<endl;
cout<<ls<<endl;
List<List<int>> listlist;
listlist.add(o);
listlist.add(m);
cout<<"List of lists of ints\n";
cout<<listlist<<endl;
List<List<List<int>>> lllist;
lllist.add(listlist);
lllist.add(listlist);
cout<<"List of lists of lists of ints\n";
cout<<lllist<<"\n";
List<List<string>> slist;
slist.add(ls);
slist.add(ls);
cout<<"List of lists of strings\n";
cout<<slist<<"\n";
return 0;
}
運行結果:
Empty list
l is < 3 2 1 >
head of l is 3
m is < 4 2 1 >
o is < 2 1 >
< b a >
List of strings
< world hello >
List of lists of ints
< < 4 2 1 >
< 2 1 >
>
List of lists of lists of ints
< < < 4 2 1 >
< 2 1 >
>
< < 4 2 1 >
< 2 1 >
>
>
List of lists of strings
< < world hello >
< world hello >
>