C++初阶——模板

目录

一、什么是模板?

二、函数模板的格式

三、函数模板的原理

四、函数模板实例化

1、什么是函数模板实例化?

2、隐式实例化

3、显式实例化

五、类模板

1、类模板的格式

2、类名和类型名


一、什么是模板?

先让我举下面的例子:

void Swap(int& a, int& b)
{
	int t = a;
	a = b;
	b = t;
}

void Swap(char& a, char& b)
{
	char t = a;
	a = b;
	b = t;
}

void Swap(double& a, double& b)
{
	double t = a;
	a = b;
	b = t;
}

//...

在碰到模板之前,如果我们遇到交换函数,那么就不可避免地要对每种类型都要写一遍,那这样就会显得代码冗余。而对于这种只是参数类型不一样,但函数的逻辑一样的情况,我们现在就可以用“模板”这一概念解决了,那样我们就只用写一遍这个函数,并且这个函数还适用于任何类型。

因此,模板的概念很简单。就是函数的通式,调用时只要把类型代进去就行了。 

二、函数模板的格式

template <typename T> /* <typename T>:模板参数列表 */
void Swap(T& a, T& b)
{
	T t = a;
	a = b;
	b = t;
}

//调用:
int main()
{
	int a = 1;
	int b = 10;
	Swap(a, b);
	return 0;
}

三、函数模板的原理

先举个例子:

template <typename T> /* <typename T>:模板参数列表 */
void Swap(T& a, T& b)
{
	T t = a;
	a = b;
	b = t;
}

int main()
{
	int a = 1;
	int b = 10;
	Swap(a, b);// B

	double c = 0.0f;
	double d = 4.5f;
	Swap(c, d);// A
	return 0;
}

 所以请问函数A和函数B是同一个函数吗?很遗憾地告诉你——不是。
 

虽然我们只写了一个函数模板,但当我们调用这个函数模板时,编译器就会自动识别实参的类型,然后自动把模板的类型名改成一个具体的类型(如int、float、char等等)。因此,虽然函数名相同,但由于参数类型不同,构成函数重载,因此其实是两个不同的函数。

四、函数模板实例化

1、什么是函数模板实例化?

简单来说,其实就是把函数形参的模板类型名变成一个具体的类型,让编译器知道应该把函数模板转换成哪种类型的函数。

2、隐式实例化

template <typename T>
T Add(const T& a, const T& b)
{
	return a + b;
}

int main()
{
	int a = 1;
	char b = '\n';

	Add(a, b);
    /*error:没有与参数列表匹配的Add函数模板实例  参数类型为:(int, char)*/
	return 0;
}

由于只有一种类型的模板,因此参数列表只能是一种类型,所以编译器就不知道应该转化成哪种类型了。而隐式类型转换就是通过强制类型转化来实现:

template <typename T>
T Add(const T& a, const T& b)
{
	return a + b;
}

int main()
{
	int a = 1;
	char b = '\n';

	Add(a, (int)b);
    /*注:这里由于隐式类型转化产生的临时变量具有常性,而引用赋值要考虑权限,因此形参列表只能用 const T& !*/
	return 0;
}

3、显式实例化

如果说隐式实例化是通过强制类型转换来让编译器识别参数类型,那么显式类型转换则是直接人为地把参数模板类型改成你想要的类型,即可以直接跳过编译器的识别。

template <typename T>
T* Alloc(int num)
{
	return new T[num];
}

int main()
{

	int* pi = Alloc<int>(5); /* 显式实例化的语法就是直接在函数名和圆括号之间加上 <类型名> 就行了 */
	
	return 0;
}

以上面的例子为例,由于函数的参数列表中没有模板类型的参数,所以编译器无法识别具体的类型是什么,但对于一个函数模板来说它总是要实例化的,因此我们可以用显式实例化来人为地把模板类型改成自己想要的类型。

但要注意的是,无论是隐式实例化还是显式实例化,实参的数据类型都有可能被改变,因此一定要注意临时变量的产生。如果函数的参数列表有引用和指针变量的话那就要注意权限问题了。 

五、类模板

1、类模板的格式

template<typename T1, typename T2, ..., typename Tn>
class 类模板名
{
 // 类内成员定义
}; 

2、类名和类型名

在普通类中,类名和类型名是一个东西。为什么呢?其实看对象实例化就可以看出:

 但是在模板中,类名和类型名是两个不同的东西。

 原因其实也很简单。因为我们可以看到编译器是无法自动识别具体的数据类型,因此在对象实例化时我们要用显式实例化来实例化对象。因为只有用显式实例化才可以直接跳过编译器识别,人为地把模板参数改成具体的类型。

因此,当我们想在类模板里面声明函数,然后在类外定义这个函数时,我们只能这样定义:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胎粉仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值