一、泛型编程
为了引出模板,我们来看下面代码,比如要实现不同类型的交换函数,如下:
void Swap(int& a, int& b){ int c = a; a = b; b = c;}void Swap(char& a, char& b){ char c = a; a = b; b = c;}void Swap(double& a, double& b){ double c = a; a = b; b = c;}
这样每个类型交换都需要写一个函数重载,其中代码的重复率很高,只有类型不同而逻辑都一样,写起来也非常的繁琐,那我们能不能写一个通用的函数告诉编译器一个模版让编译器根据不同的类型利用该模版来生成代码呢?这就是本章的主题——模板。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
模板分为函数模板和类模板,接下来我们分别来详细学习。
二、函数模版
函数模板的格式:
template<typename T1, typename T2 , ... ... ,typename Tn>
返回类型 函数名(参数列表){}
注意:这里typename可以换为calss,但不能换位struct。
代码示例:
template<typename T>void Swap(T& a, T& b){ T c = a; a = b; b = c;}
这样我们写一个模板就可以省了很多代码,还增加可读性。以上并不是一个函数只是一个模板,需要等到使用这个函数的时候编译器会具体的实例化出对应类型的函数,它和写多个函数重载并没有本质区别,只是说这个工作让编译器帮我们做了而已,在效率上并没有提升。
需要注意的是如果在这里传两个不同的参数的话会编译报错,因为这里模板参数只有一个,而传入两个不同的类型的实参的时候,编译器并不会帮你强制类型转换,因为它并不知道转成那个类型出了问题它可不背锅。那么我们硬要传两个不同的参数的话,可以显示的强制类型转换成两个相同类型,或者调用的时候在函数名后面加<>,尖括号里面加类型名,也就是显示实例化,
如下示例:
这里有人可能会试图去测试Swap模板,结构还是编不过,这是因为不符合引用的语法,我在之前讲过,如下链接:
C++入门基础-CSDN博客
对于这种情况,我们可以在外面进行强制类型转换存入新的变量里面然后再把新的变量代替原来变量做函数参数。
函数模板与模板函数的区别:通过这两个词就可以看出来函数模板的主语是模板,模板函数的主语是函数,那么接下来就好理解了,函数模板就是一个模板如刚才我们写的那些都是函数模板,而模板函数是函数模板经过实例化后生成的函数。
三、类模板
类模板的定义个格式:
template<typename T1, typename T2, ... ... ,typename Tn>
class 类模板名
{}
同样这里typename可以换为calss,但不能换位struct。
对于类模板与函数模板不同,类模板在使用的时候必须显示实例化,在类模板名后面加<>尖括号里面放入类型名。
注意:类模板不是类,要经过实例化后才是类。如下一个Stack类模板的部分:
// 类模版template<typename T>class Stack{public : Stack(size_t capacity = 4) { _array = new T[capacity]; _capacity = capacity; _size = 0; } void Push(const T& data); //... ...private: T* _array; size_t _capacity; size_t _size;};// 模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误template<class T>void Stack<T>::Push(const T& data){ // 扩容 //... ... _array[_size] = data; ++_size;} int main(){ Stack<int> st1; Stack<double> st2; return 0;}
四、模板参数的匹配原则
接下来讲的匹配原则对于类模板和函数模板都是相同的,我们就以函数模板为例。
一个非模板函数是可以和模板函数同名的,而怎么区分编译器会调用那个呢,如下:
template<typename T>T Add(T a, T b){ return a + b;}int Add(int a, int b){ return a + b;}int main(){ int a1 = 2, b1 = 4; double a2 = 2.5, b2 = 1.5; int ret1=Add(a1, b1); double ret2=Add(a2, b2); return 0;}
编译器在做调用(不仅指函数的调用)的时候有一个特点,有现成的就调现成的,没现成的就调用模板实例化一个。如这里第一个Add调用的是非模板函数,第二个Add调用的是模板函数。