C++类模板
类模板
//模板不是函数,不能单独编译,所以要将模板信息放在同一个头文件中
template<class T>
class Stack
{
private:
enum{MAX=10};
T items[MAX];
int top;
public:
Stack();
bool isempty();
Stack(const Stack& st);
Stack& operator=(const Stack& st);
};
template<class T>
Stack<T>::Stack(){top=0;}
template<class T>
bool Stack<T>::isempty(){return top==0;}
template<class T>
Stack<T>::Stack(const Stack& st){} //参数中也可以使用Stack<T>
template<class T>
Stack<T>& Stack<T>::operator=(const Stack<T> & st){} //参数中也可以使用Stack
//原型中返回类型是引用,但是在定义是需要定义为Stack<T>
//原型是缩写形式,只能应用在类中
//即指定返回类型或者使用作用域解析符时,必须使用Stack<T>
使用模板类
Stack<int> a;
Stack<string> b;
//类模板必须显式提供所需的类型
非类型参数
template<class T,int n> //n为非类型参数或者表达式参数
class ArrayTp
{
private:
T ar[n]; //未使用new,使用的是栈
public:
ArrayTp() {};
explicit ArrayTp(const T& v);
T& operator[](int i);
};
template<class T, int n>
ArrayTp<T, n>::ArrayTp(const T& V)
{
for (int i = 0; i < n; i++)
{
ar[i] = n;
}
}
template<class T, int n>
T& ArrayTp<T, n>::operator[](int i)
{
return ar[i];
}
- 非类型参数可以是整形、枚举、引用或者指针。
template<class T,double n>
是不合法的,可以是template<class T,double *n>
- 模板代码不能修改非类型参数的值,也不能使用参数的地址。所以不能使用n++或者&n之类的,
- 实例化模板时,非类型参数的值必须是常量表达式
- 每种数组大小都会生成自己的模板
//这会生成两个独立的类声明
ArrayTp<double,10> v1;
ArrayTp<double,20> v2;
使用非类型参数的优缺点:
优点:可以直接使用数组声明即内存栈来维护数组,执行速度偏快
缺点:构造函数法(不使用非类型参数)更通用,因为数组大小是存储在定义中,而不是硬编码,这样可以将不同尺寸的数组进行相互赋值,且也可以动态改变数组的大小
模板多功能性
可以将常规类的技术用于模板类,如模板类可作为基类、作为类型参数等。
template<class T>
class Array
{
private:
T entry;
};
template<class T>
class GrowArray::public Array<Type>
{};
template<class T>
class Stack
{
Array<T> ar;
};
Array<Stack<int>> a;//c++11之前,最后两个符号必须隔开
- 递归使用模板
ArrayTp< ArrayTp<int,5>,10> twoTp
≈int twoTp[10][5]
- 使用多个类型参数
template<class T1,class T2>
class Pair
{
private:
T1 a;
T2 b;
...
};
- 默认类型模板参数
template<class T1,class T2=int>
class Topp{};
注意:可以为类模板类型参数提供默认值,不能为函数模板参数提供默认值。但是函数模板和类模板都可以为非类型参数提供默认值
模板的具体化
- 隐式实例化
//编译器在需要对象之前不会生成类的隐式实例化
ArrayTp<double,20> * pt;//声明一个pointer,目前还不需要对象
pt = newArray<double ,20>;//隐式实例化
- 显示实例化
//使用template来声明显示实例化
template class ArrayTp<string,100>;//将ArrayTp<string,100>生命为一个类
//该声明必须位于模板定义所在的名称空间中
- 显示具体化
template<> class ArrayTp<cosnt char*>{...};
ArrayTp<> a;//使用泛型模板
ArrayTp<const char*> b;//使用具体化模板
- 部分具体化
template<class T1> class Pair<T1,int>{};
//template后面的<>中是未被具体化的类型参数 如果该<>中为空,则是3.显示具体化
//如果有多个模板可供选择,编译器使用具体化程度最高的模板
Pair<double,double> p1;//使用Pair<T1,T2>
Pair<double,int> p2;//使用Pair<T1,int>
Pair<int,int> p3;//使用Pair<T1,int>
为指针提供特殊版本来实现部分具体化
template<class T>
class Feeb{...}
template<class T*>
class Feeb{...}
Feeb<char> fb1;//使用通用模板 T为char
Feeb<char *> fb2;//使用T*,T为char
部门具体化可实现各种限制
//通用模板
template<class T1,class T2,class T3> class Trio{};
//T2与T3相同
template<class T1,class T2> class Trio<T1,T2,T2>{};
//T2、T3是T1的指针类型
template<class T1> class Trio<T1,T1*,T1*>{};
Trio<int,short,char*> t1;//通用模板
Trio<int,short> t2;//Trio<T1,T2,T2>
Trio<char,char*,char*> t3;//Trio<T1>
成员模板
template<class T>
class A
{
private:
template<class V>
class B{}; //嵌入类
B<T> q;
public:
A(T t, int i) {};
template<class U>
U fun(U u, T t) {return u;}
};
A<double> a(3.5, 3);
a.fun(10, 2.3);//自动推断,U为int
//有些编译器是不能在模板中声明B类和fun方法,在外面定义,而有些可以,如果可以需要这样定义:
template<class T>
class A
{
private:
template<class V>
class B;
B<T> q;
public:
A(T t, int i) {};
template<class U>
U fun(U u, T t);
};
template<class T>
template<class V>
class A<T>::B {};
template<class T>
template<class U>
U A<T>::fun(U u, T t) { return u; }
//由于T、V、U是嵌套关系,所以需要这样定义:
template<class T>
template<class V>
//而不能这样:
template<class T,class V>
//另外还需要使用作用域限定符来指出B与fun是A的成员
模板作为参数
template <template <typename T> class T2>
模板参数是template
#定义方式有点类似于非类型参数 template<class T,int n>
//可以混合使用模板参数和常规参数
template<template <typename T> class T2,typename U,typename V>
class Carb{};