您现在的位置是:首页 >技术杂谈 >【C++】模板网站首页技术杂谈

【C++】模板

LinAlpaca 2023-05-13 08:00:04
简介【C++】模板

目录

前言

1.函数模板

1.1使用

1.2实现逻辑 

1.3实例化

1.4匹配规则

2.类模板

2.1使用

实例化


前言

🎗️照以前的想法,若我们想实现一个交换函数,需要这样写。

void swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 5, b = 6;
	swap(a, b);
	return 0;
}

🎗️若想写通用的交换函数呢?根本没完没了,换一个类型就要重新写一次就算有了函数重载也不能减少多少工作量

void swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

void swap(char& x, char& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

void swap(double& x, double& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

......

🎗️就像古代最早出现的印刷术那样,使用一个模板可以去除掉那些大量并重复的工作。落实到语言中则诞生了模板(泛型编程)

1.函数模板

1.1使用

🎗️想要将这个函数定义成模板只需要在函数上加上这句话:template<class T ...> 其中参数的个数取决于实际需要的个数

🎗️其中的 class 还可以使用 typename 替换,二者都是定义模板参数关键字,区别不大,但是不可以使用 struct 

🎗️这样子,我们便可以使用模板对上面交换函数进行修改

template<class T>
void swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

🎗️我们可以将这个 想象成一个抽象的数据类型,他具体是什么我们不知道,但之后这个 会自己进行推演并转化成传入的类型

1.2实现逻辑 

🎗️让我们思考一下,以下两次 swap 调用的是同一个函数吗?

template<class T>
void swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 5, b = 6;
	double c = 5.5, d = 6.6;
	swap(a, b);
	swap(c, d);
	return 0;
}

🎗️通过查看汇编,我们可以发现,其中调用的函数地址并不相同,即调用了两个不同的函数。 

 🎗️我们曾经将类比作蓝图,对象比作楼房。其实模板也是一样的,它会根据传入参数的不同类型进行推演,之后再进行实例化,生成不同版本的代码。因此不同类型的参数调用的函数并不是同一份

🎗️本质上不同类型的函数还是要实现一份的,但使用模板就将实例化这个重复的工作交给了编译器,由编译器代我们实现。

1.3实例化

🎗️用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化显式实例化

  • 隐式实例化:让编译器根据实参推演模板参数的实际类型。
  • 显式实例化:在函数名后的<>中指定模板参数的实际类型。

🎗️若模板中只有一个数据类型,用两个类型的参数进行调用,编译器不知道该推演成哪个类型,便会出现歧义

🎗️可以传参时强制类型转换,使其满足隐式实例化。也可以指定模板参数的实际类型,让参数发生隐式类型转换,使其满足显式类型转换

1.4匹配规则

🎗️一个非模板函数和一个同名的函数模板可以同时存在,那么在这种情况下会调用哪一个函数呢?

template<class T>
int add(const T& x,const T& y)
{
	return x + y;
}

int add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 5, b = 6;
	add(a, b);
	return 0;
}

🎗️为了节约程序开销,编译器会优先选择成本较低的参数更加匹配的的函数进行调用。

2.类模板

2.1使用

🎗️类模板的定义与函数模板类似,根据需要定义任意数量的模板参数,之后便可以使用模板参数作为抽象的数据类型到类中。

template<class T>
class A
{
public:
	A(T a = 1)
		:_a(a)
	{}
private:
	T _a;
};

int main()
{
	A<int> a1;
	return 0;
}

🎗️类模板中函数放在类外进行定义时,需要加模板参数列表

template<class T>
class A
{
public:
	A(T a = 1)
		:_a(a)
	{}
	~A();           //类中声明函数
private:
	T _a;
};

template<class T>   //需加上函数参数列表
A<T>::~A()          //类外定义函数
{ 
	_a = 0;
}

int main()
{
	A<int> a1; 
	return 0;
}

🎗️在实例化之前,当前模板类并不能算作一个真正的类,只是编译器根据被实例化的类型生成具体类的模具 。

实例化

🎗️与函数模板实例化不同,类模板实例化时需要在类模板名字后加上 <> ,<>中放的是实例化的类型。因为编译器无法推演出其中的类型应该是如何对应的,因此需要手动指定

A是类名,A<int>才是类型


🎗️好了,今天模板基础内容的讲解到这里就结束了,如果这篇文章对你有用的话还请留下你的三连加关注。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。