这篇博客来说一下模板,模板有函数模板和类模板,先来看函数模板,你一听模板这个词就是提前给好一个模具,等我们用的时候在去套用
比如说:我们在实际应用中常常用到swap这个交换函数,但是呢,我们要交换的类型可能有很多种,我们总不能每一种类型都写一个函数,那样代码就特别的冗余和麻烦,这时呢,祖师爷就想到了用模板,既然在每个交换函数中只有类型是不同的,那么就把类型单拿出来,从而就有了一个模板
template<typename T>
void Swap(T& x, T& y) {T tmp = x;x = y;y = tmp;
}
这是一个swap函数的模板,其中第一行的T是我们随便给的一个名字,可以去替换,要和下面的保持一致,typename也可以换成class,在当前阶段我们认为它们是相同的。我们在函数调用的时候就可以传不同类型的参数
那这里不是自动去推算参数的类型吗?那我可不可以用auto呢?当然是不可以的,因为auto是不能做函数参数的类型的,auto只有在变量定义的时候去使用,通过右值自动去推变量的类型
那么再回到上面这个ab和cd交换来,我想问,这两组交换调用的是不是同一个函数呢?答案是不是,因为我们给定的只是一个模板,它要交换的话还是要去生成对应的函数,这些都让编译器给干了,通过汇编的话我们也可以看到
我们可以看到call指令后面调用的函数以及地址都是不同的,这也恰恰印证了我们的上边
现在我们已经弄明白了只有一种模板参数的情况,那如果有多个模板参数呢?其实就在后面在加一个就行了
template <class T1, class T2>
void func(const T1& x, const T2& y) {cout << x << endl;cout << y << endl;
}
int main() {func(1, 1);func(2.2, 2.2);func(3, 6.6);return 0;
}
这么调用都是可以的,虽然我有两个模板参数T1,T2,但是我让它们两个指向同一种类型也是可以的,但是我不能定义两个模板参数,但是只用一个
它的报错是
现在我们也知道了多个模板参数怎么用,在我们的上面的情况当中,编译器都能自动推导出每种模板参数指向的类型,这就叫做推演实例化,但是有的情况编译器是推不出来的,这时就需要我们显示实例化调用了,比如说:
template<class T>
T*& func(int n) {T* ptr = new T[n];return ptr;
}
你去调用的话,通过传递参数是不能推导出模板参数是什么类型,这时我们就需要显式实例化调用,就像下面这样
这段代码就是我就让T是int,我要传一个double的值,它就会做隐式类型转换成int,这就是我们的显式实例化调用
下边是普通函数和函数模板是可以同时存在的,编译器就会有现成的就调用现成的,没有现成的就会用模板去生成一个函数,如果连模板函数都没有的话,就会去调用一个参数类型不同的,但是可以进行隐式类型转换的函数
就像这种情况
说完函数模板之后是我们的类模板,类模板有什么作用呢?
比如说我们要创建两个栈,一个栈要存放整形数据,一个要存放浮点型数据,这时我们的类模板就显得尤为重要了,那你可能会说我们之前不是会进行typedef吗?但我们这里是要求它们同时存在,那你还是得写两份栈的代码才行,所以typedef是不行的,这时我们就可以这么写
template<class T>
class stack {
public:private:T* _a;int _top;int _capacity;
};int main() {stack<int>st1;stack<double>st2;return 0;
}
这时我们就必须得显式实例化调用了,它们显示实例化的类型不同,自然不是同一种类
还有一个问题,对于一个普通类来说,类名就是类型,但是对于我们的类模板创建的类来说,类名<数据类型>才是我们的类型,就比如上面的
我们又知道对于构造函数来说,类名就是函数名,而不是类型名
另外,我们如果想把类里面的成员函数声明定义分离,要这么干
这是一个构造函数,要给定模板句,给定类域和模板参数,并且也不能分到两个文件中