模板定义:模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数, 从而实现了真正的代码可重用性。
模版可以分为两类:(1)、函数模版
(2)、类模版。
注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
一、函数模板
.......................................................................................................................................................................................................................................
1、函数模板的格式:
template <class 形参名1,class 形参名2,......>
返回类型 函数名(参数列表)
{
函数体
}
(1)其中 class 是关键字,class 可以用 typename 代替,在这里typename 和class没区别。<>括号中的参数
template <class T>
class Array
{
public:
template <class T1>
void output(T thistype, T1 othertype); //成员模板
};
//成员模板在外部定义
template <class T>
template <class T1>
void Array<T>::output(T thistype, T1 othertype)
{
cout<<"thistype is "<<typeid(thistype).name()<<", othertype is "<<typeid(othertype).name()<<endl;
}
叫 模板形参,模板形参不能为空。一但声明了 模板函数 就可以用模板函数的 形参名 声明类中的成员变量和成员函数,也就是说在该函数中使用内置类型的地方都可以使用模板形参名。模板形参需要调用该模板函数时提供的模板实参来初始化模板形参,一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。比如swap的模板函数形式为
template <class T> void swap(T &a, T &b){ },
当调用这样的模板函数时类型T就会被被调用时的类型所代替,比如swap(a,b)其中a和b是int 型,这时模板函数swap中的形参T就会被int 所代替,模板函数就变为swap(int &a, int &b)。而当swap(c,d)其中c和d是double类型时,模板函数会被替换为swap(double &a, double &b),这样就实现了函数的实现与类型无关的代码。
(2)注意:对于函数模板而言不存在 h(int,int) 这样的调用,不能在函数调用的参数中指定模板形参的类型,对函数模板的调用应使用实参推演来进行,即只能进行 h(2,3) 这样的调用,或者int a, b; h(a,b)。
2、栗子:
#include <iostream>
#include <string>
using namespace std;
template <class T1, class T2>
void print(T1 &ii, T2 &jj)
{
cout << ii << endl;
cout << jj << endl;
}
void main()
{
string strA("Hello BeiJing!");
int B = 50;
print(strA, B);
cin.get();
}
二、类模板
.......................................................................................................................................................................................................................................
1、类模板的声明格式:
template <class 形参名1,class 形参名2,…>
class 类名
{ ... };
模板形参不能为空,一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,也就是说在类中使用内置类型的地方都可以使用模板形参名来声明。比如:
template <class T>
class A
{
public:
T a;
T b;
T hy(T c, T &d);
};
在类A中声明了两个类型为T的成员变量a和b,还声明了一个返回类型为T带两个参数类型为T的函数hy。
2、类模板对象的创建
以上声明的类模板A,用类模板A创建对象的方法是: A<int> m;
在类A后面跟上一个<>尖括号并在里面填上相应的类型,这样的话类A中凡是用到模板形参的地方都会被 int 所代替。
当类模板有两个模板形参时创建对象的方法为: A<int, double> m;
类型之间用逗号隔开。
3、在类模板外部定义成员函数的方法为:
template <模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体}
比如有两个模板形参T1,T2的类A中含有一个void function()函数,则定义该函数的语法为:
template <class T1, class T2>
class A
{
public:
T1 a;
T2 b;
void function();
};
//类模板外部定义成员函数
template <class T1, class T2> void A<T1, T2>::function()
{
...
}
注意:当在类外面定义类的成员时template后面的模板形参应与要定义的类的模板形参一致。
4、类模板的静态数据成员
在C++中,类的成员变量被声明为 static ,表示它被该类的所有实例所共享。
template <class T>
class BClass
{
public:
static int count; //静态成员变量
public:
static long GetCount() //静态成员函数
{
return count;
}
};
如果需要使用类模板的静态成员变量,必须在程序中该类外部对该 static 静态成员变量进行定义:
template <class T> int BClass<T>::count = 0;
如果要使用类模板的静态成员函数,可以创建对象由对象调用,或者直接由类的作用域调用。
BClass<int> myO;
long n1 = myO.GetCount();
long n2 = BClass<int>::GetCount();
5、再次提醒注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
三、成员模板
.......................................................................................................................................................................................................................................
模板可以用作结构、类或模板类的成员。一个模板可以在类或者类模板中声明,这样的模板叫做“成员模板”,成员模板的定义可以再内部定义,也可以在外部定义。栗子:
template <class T>
class Array
{
public:
template <class T1>
void output(T thistype, T1 othertype); //成员模板
};
//成员模板在外部定义
template <class T>
template <class T1>
void Array<T>::output(T thistype, T1 othertype)
{
cout<<"thistype is "<<typeid(thistype).name()<<", othertype is "<<typeid(othertype).name()<<endl;
}
四、友元模板
.......................................................................................................................................................................................................................................
模板类的友元分3类:(1)非模板友元。
(2)约束(bound)模板友元,即友元的类型取决于类被实例化时的类型。
(3)非约束(undound)模板友元,即友元的所有具体化都是类的每一个具体化的友元。
1.模板类的非模板友元函数
template<class T>
class HasFriend
{
friend void counts(); //friend to all HaFriend instantiations
friend class B;
};
上诉声明使 counts()函数 和 类B 成为模板所有实例化的友元。
counts()函数不是通过对象调用的,也没有对象参数。它可以访问全局对象;可以使用全局指针访问非全局对象;可以创建自己的对象;可以访问独立于对象的模板类的静态数据成员。
2.模板类的约束模板友元函数
修改前一个上面的范例,使友元函数本身成为模板。使类的每一个具体化都获得与友元匹配的具体化。包含以下三步:
首先,在类定义的前面声明每个模板函数:
template <class T> void counts();
template <class T> void report(T &);
然后在函数中再次将模板声明为友元。这些语句根据类模板参数的类型声明具体化:
template <class TT>
class HasFriend
{
friend void counts<TT>();
friend void report<>(HasFriend<TT> &);
...
};
上述声明中的<>指出这是模板具体化。对于report(),<>可以为空,这是因为可以从函数参数推断出模板类型参数(HasFriend<TT>)。不过,也可以使用report<HasFriend<TT> >(HasFriend<TT> &)。但counts()函数没有参数,因此必须使用模板参数句法(<TT>)来指明其具体化。还需要注意的是,TT是HasFriend类的参数类型。
否则链接失败。
当然也可以指定类型的函数,例如 friend void report<char>(char &);
假设声明了这样一个对象:
HasFriend<int> squack;
则编译器将用int替换TT,并生成下面的类定义:
class HasFriend<int>
{
friend void counts<int>();
friend void report<>(HasFriend<int> &);
...
};
于是,模板具体化counts<int>()和report<HasFriend<int> >()被声明为HasFriend<int>类的友元。
注意:类模板的实例化不会实例化一个友元函数,只是声明友元而不实例化。
3.模板类的非约束模板友元函数
通过在类内部声明友元函数模板,可以创建非约束友元函数,即每个函数具体化都是每个类具体化的友元:
template <class T>
class ManyFriend
{
...
template <class C,class D> friendvoid show(C &,D &);
};
在类外定义该友元:
template <class C,class D>
void show(C &c,D &d){ ... }
假如创建ManyFriend<int>类对象 hfi1 和ManyFriend<double>类对象 hfi2 ,传递给show(),那么编译器将生成如下具体化定义:
void show<ManyFriend<int> &,ManyFriend<double> &> (ManyFriend<int> &c,ManyFriend<double> &d){ ... }//使用时才实例化