C++学习笔记(十四):模板

时间:2023-03-09 00:12:10
C++学习笔记(十四):模板

模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了真正的代码可重用性。
模版可以分为两类,一个是函数模版,另外一个是类模版。
Java中对应的技术称为泛型。

函数模板:

格式:

 template <class 形参名,class 形参名,......>
返回类型 函数名(参数列表)
{
函数体
}

其中template和class是关见字,class可以用typename 关见字代替,在这里typename 和class没区别,<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。
一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名。

例如:

 #include <iostream>

 using std::cout;
using std::endl; //声明一个函数模版,用来比较输入的两个相同数据类型的参数的大小,class也可以被typename代替,
//T可以被任何字母或者数字代替。
template <class T>
T min(T x,T y)
{
return(x<y)?x:y;
} void main( )
{
int n1=,n2=;
double d1=1.5,d2=5.6;
cout<< "较小整数:"<<min(n1,n2)<<endl;
cout<< "较小实数:"<<min(d1,d2)<<endl;
system("PAUSE");
}

另外使用typename可以显示指定类型:

 #include <iostream>

 using std::cout;
using std::endl; //声明一个函数模版,用来比较输入的两个相同数据类型的参数的大小,class也可以被typename代替,
//T可以被任何字母或者数字代替。
template <typename T>
T min(T x,T y)
{
return(x<y)?x:y;
} void main( )
{
int n1=,n2=;
double d1=1.5,d2=5.6;
cout<< "较小整数:"<<min<int>(n1,n2)<<endl;
cout<< "较小实数:"<<min<double>(d1,d2)<<endl;
system("PAUSE");
}

类模板:

格式:

 template<class 形参名,class 形参名,…>
class 类名
{
...
};

类模板和函数模板都是以template开始后接模板形参列表组成,模板形参不能为空,一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,即可以在类中使用内置类型的地方都可以使用模板形参名来声明。

在类模板外部定义成员函数的方法为:

 template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体}

例如比如有两个模板形参T1,T2的类A中含有一个void h()函数,则定义该函数的语法为:

 template<class T1,class T2> void A<T1,T2>::h(){}

示例:

 // ClassTemplate.h
#ifndef ClassTemplate_HH #define ClassTemplate_HH template<typename T1,typename T2> class myClass{ private: T1 I; T2 J; public: myClass(T1 a, T2 b);//Constructor void show(); }; //这是构造函数
//注意这些格式
template <typename T1,typename T2>
myClass<T1,T2>::myClass(T1 a,T2 b):I(a),J(b){} //这是void show();
template <typename T1,typename T2>
void myClass<T1,T2>::show()
{ cout<<"I="<<I<<", J="<<J<<endl; } #endif // Test.cpp #include <iostream> #include "ClassTemplate.h" using std::cout; using std::endl; void main() { myClass<int,int> class1(,); class1.show(); myClass<int,char> class2(,'a'); class2.show(); myClass<double,int> class3(2.9,); class3.show(); system("PAUSE"); }

模板的形参

(1)类型形参

类型形参由关见字class或typename后接说明符构成。
模板形参表示的是一个未知的类型。
模板类型形参可作为类型说明符用在模板中的任何地方,与内置类型说明符或类类型说明符的使用方式完全相同,即可以用于指定返回类型,变量声明等。

如:

 template<class T> void h(T a){};//其中T就是一个类型形参,类型形参的名字由用户自已确定

(2)非类型形参

  1. 模板的非类型形参也就是内置类型形参,如template<class T, int a> class B{};其中int a就是非类型的模板形参。
  2. 非类型形参在模板定义的内部是常量值,也就是说非类型形参在模板的内部是常量。
  3. 非类型模板的形参只能是整型,指针和引用,像double,String, String **这样的类型是不允许的。但是double &,double *,对象的引用或指针是正确的。
  4. 调用非类型模板形参的实参必须是一个常量表达式,即他必须能在编译时计算出结果。
  5. 任何局部对象,局部变量,局部对象的地址,局部变量的地址都不是一个常量表达式,都不能用作非类型模板形参的实参。全局指针类型,全局变量,全局对象也不是一个常量表达式,不能用作非类型模板形参的实参。
  6. 全局变量的地址或引用,全局对象的地址或引用const类型变量是常量表达式,可以用作非类型模板形参的实参。
  7. sizeof表达式的结果是一个常量表达式,也能用作非类型模板形参的实参。
  8. 当模板的形参是整型时调用该模板时的实参必须是整型的,且在编译期间是常量,比如template <class T, int a> class A{};如果有int b,这时A<int, b> m;将出错,因为b不是常量,如果const int b,这时A<int, b> m;就是正确的,因为这时b是常量。
  9. 非类型形参一般不应用于函数模板中,比如有函数模板template<class T, int a> void h(T b){},若使用h(2)调用会出现无法为非类型形参a推演出参数的错误,对这种模板函数可以用显示模板实参来解决,如用h<int, 3>(2)这样就把非类型形参a设置为整数3。显示模板实参在后面介绍。

非类型模板形参的形参和实参间所允许的转换:

  • 允许从数组到指针,从函数到指针的转换。如:template <int *a> class A{}; int b[1]; A<b> m;即数组到指针的转换
  • const修饰符的转换。如:template<const int *a> class A{}; int b; A<&b> m; 即从int *到const int *的转换。
  • 提升转换。如:template<int a> class A{}; const short b=2; A<b> m; 即从short到int 的提升转换
  • 整值转换。如:template<unsigned int a> class A{}; A<3> m; 即从int 到unsigned int的转换。
  • 常规转换。

(3)模板形参(以模板作为模板的参数)

就是将一个模板作为另一个模板的参数。
例子:

 Grid<int,vector<int> > myIntGrid;  

注意其中int出现了两次,必须指定Grid和vector的元素类型都是int。
如果写成:

 Grid<int,vector> myIntGrid;  

因为vector本身就是一个模板,而不是一个类型,所以这就是一个模板模板参数。指定模板模板参数有点像在常规的函数中指定函数指针参数。
函数指针类型包括返回类型和函数的参数类型。在声明模板模板参数的时候也要包括完整的模板声明:
首先要知道作为参数的模板的原型,比如vector

 template<typename E,typename Allocator=allocator<E> >
class vector
{...};

然后就可以定义:

 template<typename T,template<typename E,typename Allocator=allocator<E> >class Container=vector>
class Grid
{
public:
//Omitted for brevity
Container<T>* mCells;
};