/*
类模板:
以关键字template开头,后接模板形参表。
在函数模板形参表中,关键字typename和class具有相同含义,可以呼唤使用,而且两个关键字都可以在同一模板形参表值哦功能使用,例如:
template<typename T,class V> calc(const T&,const U&);
在模板定义内部指定类型:
如果要在函数模板内部使用类定义类型成员,则必须告诉编译器我们正在使用的名字指的是一个类型,必须显式这样做,因为编译器不知到由类型形参定义的名字何时是一个类型,何时是一个值。例如:
template <class Parm,class U> Parm fcn(Parm* array,U value){
//不能判定size_type是一个类型还是一个数据成员
//编译器默认它是一个数据成员
Parm::size_type *P;
}
我们直到size_type肯定是绑定到Parm的那个类型成员,但我们不知道size_type是一个类型成员还是一个数据成员的名字,默认情况下,编译器假定这样的名字指定数据成员,而不是类型。
如果希望编译器将size_type当作类型,则必须显式告诉编译器这样做:
typelate<class Parm,class U> Parm fcn(Parm* array,U value){
//显式告诉编译器size_type是一个类型
typename Parm::size_type* p;
}
通过在成员名前加上关键字typename作为前缀,可以告诉编译器将成员当成类型。
(注:如果不确定是否需要以typename指明一个名字是一个类型,那么指定它是个好主意,在类型之前指定typename没有害处,即使typename是不必要的,也没有关系。)
非类型模板形参:在调用函数时非类型形参将用值代替,值的类型在模板形参表中指定,例如:
template<class T,size_t N> void array_init(T (&parm)[N]){
for(size_t i=0;i!=N;++i){
parm[i]=0;
}
}
模板非类型形参是模板定义内部的常量值,在需要常量表达式的使用,可使用非类型形参指定数组的长度。
编写独立于类型的代码的两个重要原则:
1.模板的形参是const引用。
2.函数体中的测试只用<比较。
类型形参的是惨的受限转换:
1.const转换:接受const引用或const指针的函数可以分别用非const对象的引用或指针来调用,无需产生新的实例化。如果函数接受非引用类型,形参类型和实参都忽略const,即无乱传递const或非const对象给接受非引用类型的函数,都使用相同的实例化。
2.数组或函数到指针的专换:如果模板形参不是引用类型,则对数组或函数类型的的实参应用常规指针转换。数组实参将当作指向其第一个元素的指针,函数实参当作指向函数类型的指针。
模板编译模型:
1.包含编译模型:
编译器必须看到所有模板的定义。一般而言,可以通过在声明函数模板或类模板的头文件中添加一条#include指示使定义可用,该#include引入了包含相关定义的源文件,例如:
//utilities.h头文件的内容
#ifndef UTLITIES_H
#define UTLITIES_H
template<class T> int compare(const T&,const T&);
//其他声明
#include "utilities.cpp"
#endif
//utilities.cpp源文件的内容
template<class T> int compare(const T &v1,const T &v2){
if(v1<v2) reutrn -1;
if(v2<v1) return 1;
return 0;
}
2.分别编译模型:
编译器会为我们跟踪相关的模板定义。但是必须让编译器直到要记住给定的模板定义,可以使用关键字export来做这件事情。
export关键字能够指明给定的定义可能会需要在其他文件中产生实例化。
一般在函数模板的定义中指明函数模板为export的,这是通过在关键字template之前包含export关键字而实现的,例如:
export template <typename Type> Type sum(Type t1,Type T2){}
对于类模板使用export,应该在类的实现文件中使用export,而不是在头文件中使用。
//头文件
template <typename Type>class Queue{..};
//类定义源文件
#include "Queue.h"
export template <typename Type>class Queue{}
export类的成员自动声明为export的。也可以将类模板的个别成员声明为export的,只要把关键字export不再类模板本身指定,而是只在要export的特定成员上指定。
类模板的名字:(1)独立于模板形参的那些名字,(2)依赖于模板形参的那些名字
设计者的责任:保证所有不依赖于模板形参的名字在模板本身的作用域中定义。
模板用户的责任:保证与用来实例化模板的类型相关的所有函数、类型和操作符的声明可见。即实例化类模板的成员或函数模板的时候,用户必须保证这些声明是可见的。
*/
/*
//Queue类接口
template <class Type> class Queue{
public:
Queue();
Type &front();
const Type &front () const;
void push(const Type &);
void pop();
bool empty() const;
};*/
/*
类模板中的友元声明:
1.普通非模板或函数的友元声明,将友元关系授予明确指定的类或函数。
2.类模板或函数模板的友元声明,授予对友元所有实例的访问权
3.值授予对类模板或函数模板的特定实例访问权的友元声明。
普通友元,例如:
template <class Type> class Bar{
//类Foobar和函数fcn都可以访问Bar的任意实例的private成员
friend class Foobar;
friend void fcn();
};
一般模板友元关系:
template <class Type> class Bar{
//Foo1和temp1_fcn1的所有实例都是友元
template<class Type> friend class Foo1;
template<class Type> friend void temp1_fcn1(const Type&);
};
特定的模板友元关系:
template<class T> class Foo2;
template<class T> void temp1_fcn2(const T&);
template<class T> class Bar{
//只有形参为char*的Foo2和temp1_fcn2的特定实例可以访问Bar的每个实例。
friend class Foo2<char*>;
friend void temp1_fcn2<char*>(char* const &);
};
更一般的特定模板友元关系:
template<class T> class Foo3;
template<class T> void temp1_fcn3(const T&);
template<class Type> class Bar{
//只有与给定Bar实例有相同模板实参的那些Foo3和temp1_fcn3版本是友元。
friend class Foo3<Type>;
friend void temp1_fcn3<Type>(const Type&);
};
*/
#include<iostream>
using namespace std;
//声明Queue类
template<class Type> class Queue;
//队列的节点类
template<class Type> class QueueItem{
public:
//将Queue设为友元
friend class Queue<Type>;
QueueItem(const Type &t):item(t),next(0){}
Type getItem(){
return item;
}
QueueItem* getNext(){
return next;
}
private:
//数据
Type item;
//指向下一个元素的指针
QueueItem<Type>* next;
};
//队列类
template <class Type> class Queue{
public:
//默认构造器
Queue():head(0),tail(0){}
//复制构造器
Queue(const Queue &Q):head(0),tail(0){
copy_elems(Q);
}
//析构函数,当调用析够函数时,删除所有的元素释放内存
~Queue(){destroy();}
//返回头元素
Type& front(){
return head->item;
}
//返回头元素的const对象
const Type &front() const{
return head->item;
}
//声明入队函数
void push(const Type &);
//声明出队函数
void pop();
//判断是否为空的函数
bool empty() const{
return head==0;
}
QueueItem<Type> *head;
QueueItem<Type> *tail;
private:
//删除所有元素
void destroy();
//根据传入的参数,复制实参的元素
void copy_elems(const Queue&);
};
/*
在类体外定义类模板成员函数:
1.必须以关键字template开头,后接类的模板形参表。
2.必须指出它是哪个类的成员。
3.类名必须包含其模板参数,例如
template<class T> ret-type className<T>::member-name
*/
//destroy函数
template<class Type> void Queue<Type>::destroy(){
while(!empty())
pop();
}
//pop函数
template<class Type> void Queue<Type>::pop(){
QueueItem<Type>* p=head;
head=head->next;
//删除该节点
delete p;
}
//push函数
template<class Type> void Queue<Type>::push(const Type &val){
QueueItem<Type> *pt=new QueueItem<Type>(val);
if(empty()){
head=tail=pt;
}
else{
//指针指向下一个元素
tail->next=pt;
//修改末尾节点
tail=pt;
}
}
//copy_elems函数
template<class Type> void Queue<Type>::copy_elems(const Queue &orig){
//遍历该容器的节点
for(QueueItem<Type> *pt=orig.head;pt;pt=pt->next){
//把没个节点的数据都push到当前对象
push(pt->item);
}
}
//重载输出操作符号
template<class Type> ostream& operator<<(ostream &os,const Queue<Type> &q){
os<<"<";
QueueItem<Type> *p;
for(p=q.head;p;p=p->getNext()){
os<<p->getItem()<<" ";
}
os<<">";
return os;
}
int main(){
Queue<int> test;
for(size_t i=1;i!=10;++i){
test.push(i);
}
cout<<test<<endl;
return 0;
}