泛型编程
泛型编程:针对广泛的类型进行编程.
例:在一个程序中,要分别交换int类型、double类型、char类型的数据,
可以通过函数重载来实现。但这些函数的逻辑一致,只有类型不同,
接下来可能又需要写自定义类型的交换函数,这个过程无休无止,
于是引入了模板进行泛型编程 —— 模板分为 函数模板 和 类模板
void swap(int& a, int& b)
{
int c = a;
a = b;
b = c;
}
void swap(double& a, double& b)
{
double c = a;
a = b;
b = c;
}
void swap(char& a, char& b)
{
char c = a;
a = b;
b = c;
}
函数模板
--格式
template<typename T1, typename T2, ....,typename Tn>
例:
//template是关键字
//typename用于定义模板参数的关键字,这里也能用class
//T代表的是一个模板类型/虚拟类型
//typename后面的类型名字T是随便取的,一般是大写字母/大写字母开头
template<typename T>
void Swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
//调用位置
int a = 0, b = 1;
double c = 1.1, d = 2.2;
char e = 'A', f = 'B';
Swap(a, b);
Swap(c, d);
Swap(e, f);
之前的交换函数是针对具体的类型,而现在针对广泛的类型。
库里面也有一个swap函数:(cplusplus.com上的函数实现)
--函数模板原理
函数模板会经历两个过程:
1 模板参数推演
2 针对推演出来的具体类型,生成一个具体函数
所以针对不同的类型交换,会生成不同的函数
--函数模板的实例化
1 隐式实例化
让编译器根据实参推演模板参数的具体类型,若 推演出现矛盾 或 无法推演 报错
推演出现矛盾:
template<typename T>
void func(T& a1, T& a2){}
int main()
{
int a1 = 1;
double a2 = 1.1;
//此时无法判断模板参数T是int,还是double
func(a1, a2);
return 0;
}
无法推演:
template<typename T>
T func1(int a1, int a2)
{
return a1 + a2;
}
int main()
{
int a1 = 1;
int a2 = 1;
//T无法通过实参推出具体类型
func1(a1, a2);
return 0;
}
2 显式实例化
调用函数的地方,直接指定模板参数的类型.
在函数名和参数之间,加一个<>,里面指定模板参数的类型,例:
buyTSpace<int>(5)
template<class T>
T* buyTSpace(int n) //申请n个T类型大小空间
{
T* ret = new T[n];
return ret;
}
int main()
{
int* ret = buyTSpace<int>(5);
delete[] ret;
return 0;
}
类模板
--为什么要有类模板
typedef可以修改栈放的数据,但现在同时想要2个栈,
一个栈放int,另一个栈放double数据,typedef无法实现该功能.
typedef int StackDateType;
class Stack
{
public:
private:
StackDateType* _date;
int _capacity;
int _top;
};
Stack s1;//这个栈放int数据
Stack s2;//放double数据
--格式
与类似函数模板类似
template<typename T> //typenane也可以用class替换
class Stack
{
public:
private:
T* _date; //存T类型的数据
int _capacity;
int _top;
};
同时在定义类对象时,必须使用显式实例化,才能推出T
Stack<int> s1; //存int
Stack<double> s2;//存double
//虽然Stack<int> 、Stack<double>用了同一个类模板,但它们不是同一个类
//它们是编译器实例化出来(生成)的