模板(上)

97 阅读3分钟

泛型编程

泛型编程:针对广泛的类型进行编程.

例:在一个程序中,要分别交换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上的函数实现)

image.png

--函数模板原理

函数模板会经历两个过程:

1 模板参数推演

2 针对推演出来的具体类型,生成一个具体函数

image.png

所以针对不同的类型交换,会生成不同的函数 image.png

--函数模板的实例化

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;
}

image.png

无法推演

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;
}

image.png

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>用了同一个类模板,但它们不是同一个类
        //它们是编译器实例化出来(生成)的