模板
模板使类和函数可在编译时定义所需处理和返回的数据类型。
一个模板并非一个实实在在的类或函数,仅仅是一个类或函数的描述。
模板一般分为模板函数和类模板,以所处理的数据类型的说明作为参数的类叫类模板,以所处理的数据类型的说明作为参数的函数,称为函数模板。
模板的一般定义形式为:
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 >
>