目录
函数模板
函数模板的实例化:
隐式实例化
显示实例化
类模板
虽然学习了类和对象,能很方便的处理一些容器类的问题,但是我们还是会遇到如下这种情况。
我们创建一个简单的stack类
class Stack
{
public:
Stack()
{}
private:
int* _a;
size_t _top;
size_t _capacity;
};
我们单独处理int类型需要使用栈结构的时候还好说,假如我们需要使用double或者其他类型的变量时,操作会变得很难受,复制一段改数据类型吗?代码变得膨大不说还难以管理,诚然我们可以使用typedef来方便修改,可是每一次运行都要更改还是挺蛋疼的。
所以为了应对一些函数仅仅只是传参数据类型不同,但是逻辑相同而需要重载函数的情况,C++给出了模板。
template <typename T>
void Swap(T& x,T& y)
{
T tmp = x;
x = y;
y = tmp;
}
用起来嘎嘎顺手,嘎嘎好用。
如上所记述的是函数模板,我们还有类模板,我们一个个来看
函数模板
template <typename T1,typename T2>
void Swap(T& x,T& y)
{
T tmp = x;
x = y;
y = tmp;
}
注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
上文所展示的函数模板其实是类似于蓝图的样式,本身需要我们自己思考编写对应类型的这个过程被模板参数所取代,这个过程取而代之变成了编译器的工作,也就是推导的过程成了编译器的事情,我们可以在这方面偷懒了。
编译器推到的大致过程如下:
当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然
后产生一份专门处理double类型的代码,int也是同理。
模板的这种工作原理很像用模具做冰棍不是吗?同一个模具生产出不同口味的冰棍
函数模板的实例化:
实例化有两种方式:显示实例化,隐式实例化
隐式实例化
我们上文所述的方法就是隐式实例化,通过对函数模板传递参数来让编译器自己推演得到对应的参数类型并生成对应类型的函数。
但其实我们不能去依赖编译器的推演能力,因为它本身的推演能力不太行,具体体现如下文:
虽然说我们上文的交换函数是完全没啥问题的,但是如果换成两个不同的类型让编译器去尝试推导就不行了。比如我们的Swap函数,我们分别传一个int和double进去
因为我们本身的模板参数只有一个T,传过来第一个T编译器推导为int,而第二个是个double就把编译器干懵了,那这个T到底是int还是double?
解决办法:
1.多加一个模板参数
template <typename T1,typename T2> void Swap(T1& x, T2& y) { T1 tmp = x; x = y; y = tmp; }
2.使用者自己强制类型转换
当然对于Swap函数来讲这个也是不起作用的,所以为了举例换成Add函数
当然,还有其他的办法来弥补编译器推演能力不足的问题,显式实例化就是其中之一。
显示实例化
显示实例化的本质就是不再让编译器推演,我们直接指定传入的变量类型,具体操作是在函数名后的<>中指定模板参数的实际类型
int main()
{
int a1 = 10, a2 = 20;
double d1 = 10.0, d2 = 20.0;
Add<int>(a1, d1);
}
我们可以看到,编译器直接将两个模板参数变量都变成了int类型,但其实在这个过程中还是发生了隐式类型转换。
类模板
上文我们把模板参数套在了函数头上,我们也可以套到类形成类模板。
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
以STL举例,STL各种容器指定存放数据类型的基本实现就是依仗于模板的,比如Vector
int main()
{
vector<int> v1;
}
我们在类模板名后传入指定需要的变量类型,编译器就会生成对应类型的类。
学习了基本的模板就可以去玩一玩STL了。