泛型编程
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础
为了支持各种类型的两个数据的交换,我们不得不写出多个函数。比如之前写的swap函数:
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
void Swap(double& a, double& b)
{
double tmp = a;
a = b;
b = tmp;
}
void Swap(char& a, char& b)
{
char tmp = a;
a = b;
b = tmp;
}
它们构成函数重载,但是代码复用率低,一旦出现新的类型,就需要用户自己添加相应的函数。代码维护性低,一但发现错误,所有函数都要一起改。
我们能否只写一个函数模板,需要什么类型的函数直接让编译器自己往里面填好了生成出来呢?
答案是可以的,我们称这种方法叫泛型编程
函数模板
模板是泛型编程的基础,要用到的关键字:template
函数模板的定义
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表)
{
}
👆:typename
也可以写成class
,当前的角度,用这两个关键字没有区别,T
是模板参数,是我们定义函数模板要用的类型名,可以自己随别取
例1:
template<typename T>
void Swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
模板的声明必须带上模板参数:
//例1的声明:
template<typename T>
void Swap(T& a, T& b)
模板不支持声明和定义分离到两个文件。一般是要放到一个文件中,有些地方会把文件命名成xxx.hpp,寓意头文件和定义实现内容合并在一起。
函数模板的实例化
调用Swap
函数:
👆:传入不同类型的参数,编译器会按照模板生成不同的函数来调用。
另外,C++库里面有swap
函数,我们以后也不用自己实现了
函数模板显式实例化:
如果你写得函数模板无法通过传参来推导类型,就必须手动指定:
调用时在函数模板名后面跟<>
,<>
内是我们自己指定的类型。
template<typename T>
T* func(int c) //无法通过传参推导T的类型
{
return newT[n];
}
int main()
{
int* p1 = func<int>(10);
double* p2 = func<double>(10);
return 0;
}
再看下面的例子:
template<class T>
T Add(const T& a, const T& b)
{
return a + b;
}
int main()
{
int a = 10;
double d = 9.8;
Add(a, d);//此处报错:没有与参数列表匹配的 函数模板 "Add" 实例
return 0;
}
👆:传入的两个参数的类型不同,T的类型无法确认,此时也可以手动指定成Add<int>(a, d);
或Add<double>(a, d);
,这样程序就能运行了,传参的时候会进行强制类型转换。
模板参数的匹配原则
int Add(int a, int b) //专门处理int类型的函数
{
return a + b;
}
template<class T> //通用的函数模板
T Add(const T& a, const T& b)
{
return a + b;
}
int main()
{
int a = 1, b = 2;
Add(a, b);
return 0;
}
传入int类型的数据,会优先调用哪个函数呢?
通过调试发现,编译器调用的是专门处理int类型的函数,而没有去用模板实例化生成的。
如果手动指定类型Add<int>(a, b);
那么一定是调用函数模板实例化生成的。
类模板
类模板的定义
template<class T1, class T2, ..., class Tn>
class 类模板名
{
};
例:
template<class T>
class Stack
{
public:
Stack(int capacity = 0)
{
_a = new T[capacity];
_capacity = capacity;
_top = 0;
}
~Stack()
{
delete[] _a;
_capacity = 0;
_top = 0;
}
//...
private:
T* _a;
int _top;
int _capacity;
};
类模板是实例化
类不像函数,函数模板可以根据传参去推导要替换的类型,而类模板没有。
类模板的实例化需要在类模板名后面跟<>
,<>
内的是我们自己指定的类型。
int main()
{
Stack<int> st1;
Stack<double> st2;
return 0;
}
模板参数也支持给缺省参数,比如template<class T = char>
,这样如果不指定类型就默认是char
类型