是什么
模板是范型编程的基础,范型编程呢,顾名思义,就是传递的参数类型广泛,不固定,函数或者类自适应参数类型的意思。听起来似乎没什么,但不管怎样,你一定记住,模板可牛逼着呢。
比如说定义了一个如下的函数模板:
template <T> T add( T a, T b ) { return a + b; }
这里的T 我们就可以理解为万能的数据类型,或者说数据类型的一个占位,当我们使用该函数时会根据参数类型自动适配。在这里,函数add可以进行这样的描述:
返回值类型为T,参数a类型为T,参数b类型为T,其中T为任意数据类型。
所以呢,我们再看如下两个调用:
cout << add( 1, 2 ) << endl; //? cout << add( 1.2, 2.1 ) << endl; //?
通过上面的解释,我们应该也很容易猜出结果,add(1,2)传入整型数据,不难看出结果为3;而add(1.2,2.1)传入浮点型数据,结果为3.3。
怎样?简单吧。我们继续,但记住,万变不离其宗,核心永远还是在于范型。
函数模板
总结我们先前所说的,函数模板其实就是可以接受任意类型的函数。其格式为:
template <typename type> ret_type func_name(arr_type_a arr_name_a, arr_type_b arr_name_b) { //函数主体 }
在这里type代表函数中所有指明类型为type的值都会自适应给定的类型。通常arr_type_a、arr_type_b、ret_type可以等于type,这样就可以实现自适应传参了。但值得注意的是:
比如对文章最开始的add函数模板进行如下调用,编译器将会报错:
int a = 1;float b = 2.1; cout << add( a, b ) << endl; //不合法,T被指定两种类型
类模板
格式
相比函数模板,类模板则复杂得多,有很多需要留意的地方。其格式为:
template <typename type> class class_name { //类主体 };
定义
template <typename T> class Shape { public: void setH( T a ) { h = a; } void setW( T a ) { w = a; } T getS() { return h * w; } protected: T h; T w; };
函数解析如下:
类模板Shape定义T为自适应类型,并含有如下成员函数setH()、setW()、getS()以及被保护的属性h和w。其中setH()和setW()都有一个类型为T的参数,getS()返回值类型为T。
使用
Shape<float> A; A.setH( 3.1 ); A.setW( 2.9 ); cout << A.getS() << endl;//?
这里定义了一个Shape的类A,并指定模板T为float,即模板重所有的T被替换为float。可以看出,最后的结果为3.1*2.9,若A被定义为:
Shape<int> A; A.setH( 3.1 ); A.setW( 2.9 ); cout << A.getS() << endl;//?
则结果为2*3 = 6。
类模板的继承
与普通类的继承不同,类模板继承时如需要访问父类的变量,需要使用父类的类作用域限定符,否则会报“identifier not found”错误。比如子类Triangle:
template <typename T> class Triangle: public Shape<T> { public: T getS() { return Shape<T>::h * Shape<T>::w / 2; } T getH(); T getW() { return Shape<T>::w; } }; template <typename T> T Triangle::getH() { return Shape<T>::h; }
其他说明
typename与class
模板的定义有两种:
template <typename T> template <class T>
其实这两种方式完全一样,不必纠结,但typename有另外一个作用:告诉编译器其后是一个类型而不是一个变量。比如:
typename A::a *elem;
该句表示声明一个与类A中变量a类型等同的指针elem。如果没有typename,编译器将会把A::a当作一个变量值处理。
示例代码
以下代码可以作为运行参考。
#include <iostream> using namespace std; template <typename T> T add( T a, T b ) { return a + b; } template <typename T> class Shape { public: void setH( T a ) { h = a; } void setW( T a ) { w = a; } T getS() { return h * w; } protected: T h; T w; }; template <typename T> class Triangle: public Shape<T> { public: T getS() { return Shape<T>::h * Shape<T>::w / 2; } T getH(); T getW() { return Shape<T>::w; } }; template<typename T> T Triangle<T>::getH() { return Shape<T>::h; } int main() { { Shape<float> A; A.setH( 3.2 ); A.setW( 2 ); cout << A.getS() << endl; } cout << add( 1, 2 ) << endl; cout << add( 1.2, 2.1 ) << endl; Shape<float> A; A.setH( 3.1 ); A.setW( 2.9 ); cout << A.getS() << endl; Triangle<int> B; B.setH( 31 ); B.setW( 10 ); cout << B.getW() << endl; cout << B.getH() << endl; cout << B.getS() << endl; return 0; }