目录
前言
1.函数模板
1.1使用
1.2实现逻辑
1.3实例化
1.4匹配规则
2.类模板
2.1使用
实例化
前言
?️照以前的想法,若我们想实现一个交换函数,需要这样写。
void swap(int& x, int& y){int tmp = x;x = y;y = tmp;}int main(){int a = 5, b = 6;swap(a, b);return 0;}
?️若想写通用的交换函数呢?根本没完没了,换一个类型就要重新写一次,就算有了函数重载也不能减少多少工作量。
void swap(int& x, int& y){int tmp = x;x = y;y = tmp;}void swap(char& x, char& y){int tmp = x;x = y;y = tmp;}void swap(double& x, double& y){int tmp = x;x = y;y = tmp;}......
?️就像古代最早出现的印刷术那样,使用一个模板可以去除掉那些大量并重复的工作。落实到语言中则诞生了模板(泛型编程)。
1.函数模板
1.1使用
?️想要将这个函数定义成模板只需要在函数上加上这句话:template<class T ...> 其中参数的个数取决于实际需要的个数。
?️其中的 class 还可以使用 typename 替换,二者都是定义模板参数关键字,区别不大,但是不可以使用 struct 。
?️这样子,我们便可以使用模板对上面交换函数进行修改。
template<class T>void swap(T& x, T& y){T tmp = x;x = y;y = tmp;}
?️我们可以将这个 T 想象成一个抽象的数据类型,他具体是什么我们不知道,但之后这个 T 会自己进行推演并转化成传入的类型。
1.2实现逻辑
?️让我们思考一下,以下两次 swap 调用的是同一个函数吗?
template<class T>void swap(T& x, T& y){T tmp = x;x = y;y = tmp;}int main(){int a = 5, b = 6;double c = 5.5, d = 6.6;swap(a, b);swap(c, d);return 0;}
?️通过查看汇编,我们可以发现,其中调用的函数地址并不相同,即调用了两个不同的函数。
?️我们曾经将类比作蓝图,对象比作楼房。其实模板也是一样的,它会根据传入参数的不同类型进行推演,之后再进行实例化,生成不同版本的代码。因此不同类型的参数调用的函数并不是同一份。
?️本质上不同类型的函数还是要实现一份的,但使用模板就将实例化这个重复的工作交给了编译器,由编译器代我们实现。
1.3实例化
?️用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。
隐式实例化:让编译器根据实参推演模板参数的实际类型。显式实例化:在函数名后的<>中指定模板参数的实际类型。?️若模板中只有一个数据类型,用两个类型的参数进行调用,编译器不知道该推演成哪个类型,便会出现歧义。
?️可以传参时强制类型转换,使其满足隐式实例化。也可以指定模板参数的实际类型,让参数发生隐式类型转换,使其满足显式类型转换。
1.4匹配规则
?️一个非模板函数和一个同名的函数模板可以同时存在,那么在这种情况下会调用哪一个函数呢?
template<class T>int add(const T& x,const T& y){return x + y;}int add(int x, int y){return x + y;}int main(){int a = 5, b = 6;add(a, b);return 0;}
?️为了节约程序开销,编译器会优先选择成本较低的,参数更加匹配的的函数进行调用。
2.类模板
2.1使用
?️类模板的定义与函数模板类似,根据需要定义任意数量的模板参数,之后便可以使用模板参数作为抽象的数据类型到类中。
template<class T>class A{public:A(T a = 1):_a(a){}private:T _a;};int main(){A<int> a1;return 0;}
?️类模板中函数放在类外进行定义时,需要加模板参数列表。
template<class T>class A{public:A(T a = 1):_a(a){}~A(); //类中声明函数private:T _a;};template<class T> //需加上函数参数列表A<T>::~A() //类外定义函数{ _a = 0;}int main(){A<int> a1; return 0;}
?️在实例化之前,当前模板类并不能算作一个真正的类,只是编译器根据被实例化的类型生成具体类的模具 。
实例化
?️与函数模板实例化不同,类模板实例化时需要在类模板名字后加上 <> ,<>中放的是实例化的类型。因为编译器无法推演出其中的类型应该是如何对应的,因此需要手动指定。
A是类名,A<int>才是类型
?️好了,今天模板基础内容的讲解到这里就结束了,如果这篇文章对你有用的话还请留下你的三连加关注。