C++模板的好处以及应用实例
1. 什么是模板?
模板是c++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数返回值取得任意类型。
模板是一种对类型进行参数化的工具;
通常有两种形式:函数模板和类模板; 函数模板针对仅参数类型不同的函数; 类模板针对仅数据成员和成员函数类型不同的类。
2. 模板的好处
可以编写与类型无关的代码;例如:
注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
2.1 函数模板通式
1、函数模板的格式:
template 返回类型 函数名(参数列表) { 函数体 }
几点说明:
template和class是关键字,class可以用typename 关键字代替,在这里typename 和class没区别
<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。
一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名
模板形参需要调用该模板函数时提供的模板实参来初始化模板形参,一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。
例如:swap的模板函数形式为
template void swap(t& a, t& b){},
当调用这样的模板函数时,编译器会根据传入的实参自动推演出实参的类型,这样一来自动类型t就会被被调用时的类型所代替
比如swap(a,b)其中a和b是int 型,这时模板函数swap中的a和b就会被推演出来是int,形参t就会被int 所代替,模板函数就变为swap(int &a, int &b)。而当swap(c,d)其中c和d是double类型时,模板函数会被替换为swap(double &a, double &b),这样就实现了函数的实现与类型无关的代码。
注意:对于函数模板而言不存在 h(int,int) 这样的调用,不能在函数调用的参数中指定模板形参的类型,对函数模板的调用应使用实参推演来进行,即只能进行 h(2,3) 这样的调用,或者int a, b; h(a,b)。
2.2 类模板通式
类模板的格式为:
template class 类名 { ... };
类模板和函数模板都是以template开始后接模板形参列表组成,模板形参不能为空,一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,即可以在类中使用内置类型的地方都可以使用模板形参名来声明。比如
template class a { public: t a; t b; t hy(t c, t &d); };
在类a中声明了两个类型为t的成员变量a和b,还声明了一个返回类型为t带两个参数类型为t的函数hy。
类模板对象的创建:
a aa; //a这里需要带上类型,因为在模板类实例化出对象时,类型 //在编译时候确定,不存在类型推演过程,因此要显示的给出类型
在类模板外部定义成员函数的方法为:
template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表) { 函数体 }
例:有两个模板形参t1,t2的类a中含有一个void h()函数,则定义该函数的语法为:
template void a::h() {}
注意:当在类外面定义类的成员时template后面的模板形参应与要定义的类的模板形参一致。
注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
3. 非类型模板参数
3.1 非类型模板形参:模板的非类型形参也就是内置类型形参,如template< class t, size_t n> class b{};其中n就是非类型的模板形参。
3.2 非类型形参在模板定义的内部是常量值,也就是说非类型形参在模板的内部是常量。
3.3 非类型模板的形参只能是整型,指针和引用,像double,string, string * 这样的类型是不允许的。但是double &,double *,对象的引用或指针是正确的。
3.4 调用非类型模板形参的实参必须是一个常量表达式,即他必须能在编译时计算出结果。
3.5 任何局部对象,局部变量,局部对象的地址,局部变量的地址都不是一个常量表达式,都不能用作非类型模板形参的实参。全局指针类型,全局变量,全局对象也不是一个常量表达式,不能用作非类型模板形参的实参。
3.6 全局变量的地址或引用,全局对象的地址或引用const类型变量是常量表达式,可以用作非类型模板形参的实参。
3.7 sizeof表达式的结果是一个常量表达式,也能用作非类型模板形参的实参。
3.8 非类型模板形参的形参和实参间所允许的转换
允许从数组到指针,从函数到指针的转换。
如:template < int *a> class a{}; int b[1]; a< b> m;即数组到指针的转换 修饰符的转换。如:template< const int * a > class a{}; int b; a<&b> m; 即从int 到const int 的转换。 提升转换。如:template class a{}; const short b=2; a m; 即从short到int 的提升转换 整值转换。如:template class a{}; a<3> m; 即从int 到unsigned int的转换。
4. 非类型模板参数的应用
题目描述:
不要使用循环,*/ ,实现 1+2+3+4+。。。。。+n
要求:时间复杂度为o(1)
思路:
1. 使用非类型模板参数,使其在编译时确定n
2. 使用递归,n可划分为n+s(n-1) ,n-1可划分为n-1 + s(n-1)…….依次类推,直到1停止推演
3. 使用模板的特化,将1进行特化,使其在编译时推演时能够停下
代码:
template class aa { public: enum { value = aa::value+1//使用递归 } } template<> class aa<1>//将1进行特化 { public: enum { value = 1 }; }
结果: