模版初阶(更新)

发布于:2024-07-11 ⋅ 阅读:(24) ⋅ 点赞:(0)

模版介绍

函数模版分为两个类型:

  1. 函数模版
  2. 类模版

函数模版

语法格式: t e m p l a t e < t y p n a m e T 1 , t y p n a m e T 2... > template<typname T1,typname T2...> template<typnameT1,typnameT2...>

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

int main()
{
    int a = 1, b = 2;

    double c = 1.1, d = 2.2;

    cout << a << " " << b << endl;
    swap(a, b);
    cout << a << " " << b << endl;
    
    cout << "----------------" << endl;
    
    cout << c << " " << d << endl;
    swap(c, d);
    cout << c << " " << d << endl;

    return 0;
}

在C语言中,如果我们需要分别交换 i n t int int d o u b l e double double 的数据,那么我们需要定义两个swap函数并且还不能重名。(C语言并不支持函数重载)。

而在C嘎嘎中:

在这里插入图片描述
我们只需要写一个模版就可以了。这里的T在编译的时候会默认替换为我们的 i n t int int d o u b l e double double

在这里插入图片描述
同时通过调试发现,这两个swap都进入我们写的函数模版,那么这两个Swap调用的是同一个函数吗?
在这里插入图片描述
通过反汇编发现,此时我们两个调用的并不是同一个函数。

在这里插入图片描述
当我们使用我们的 S w a p ( ) Swap() Swap()模版的时候,编译器会去默认推演我们此时的参数类型,然后编译器自动生成一个 S w a p ( ) Swap() Swap()函数,中国有句古话:死道友不死贫道[doeg]。咱们只管写一个模版,其他的交给编译器。

在这里插入图片描述
但是如果我们只有一个模版参数的话,传入两个不同类型编译器推导不出来。

此时我们有以下几种方法:
在这里插入图片描述

  1. 添加模版参数
  2. 进行强制类型转化
  3. 直接给定模版参数类型,不让编译器推演

A d d < i n t > Add<int> Add<int> 这个叫做显式实例化。它指定了参数的类型,编译器就不会推演了,T默认会 i n t int int 类型。

模版匹配规则

在这里插入图片描述
在这里插入图片描述

如上图:当我们调用 A d d ( a , b ) Add(a,b) Add(a,b) 的时候,我们写的单参数模版函数其实可以实现的,但是编译器回去默认调用最匹配的 i n t A d d ( i n t l e f t , i n t r i g h t ) int Add(int left, int right) intAdd(intleft,intright)

而当我们调用 A d d ( a , c ) Add(a,c) Add(a,c) 的时候不同参数类型我们的单参数模版函数就不行了,第一张图中我们定义了两个参数的模版参数,所以编译器会去调用我们定义的这个模版函数,而在第二张图中,编译器由于没得选了,只能被迫去调用
i n t A d d ( i n t l e f t , i n t r i g h t ) int Add(int left, int right) intAdd(intleft,intright),只不过会有 精度的丢失。

总结一下:

  • 函数调用会优先调用 (参数匹配+口味好)例如: A d d ( a , b ) Add(a,b) Add(a,b)调用 i n t A d d ( i n t l e f t , i n t r i g h t ) int Add(int left, int right) intAdd(intleft,intright)
  • 当口味不对的时候,会优先调用参数匹配 例如: A d d ( a , c ) Add(a,c) Add(a,c)调用 t e m p l a t e < t y p e n a m e T 1 , t y p e n a m e T 2 > template<typename T1, typename T2> template<typenameT1,typenameT2>
  • 当两个都没有的时候,会将就一样。例如: A d d ( a , c ) Add(a,c) Add(a,c)调用 i n t A d d ( i n t L e f t , i n t R i g h t ) int Add(int Left, int Right) intAdd(intLeft,intRight)

当然模版函数只是单参数的时候,我们传入两个不同类型的参数时,就会报错,编译器不知道推演哪一个?

类模版

typedef int DataType;
class Stack
{
public:
	Stack()
	{
		_array = (DataType*)malloc(sizeof(DataType) * 3);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = 3;
		_size = 0;
	}
	void Push(DataType data)
	{
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};

如上述栈,在C语言中,如果我们想要栈中存不同数据。

int main()
{
	Stack st1; //int
	Stack st2; //double
	return 0;
}

例如我们想要一个存 i n t int int,一个存 d o u b l e double double,那我们就需要写两个栈了,由于这两个栈的代码都类似,那么在C++中,我们可以定义模版,栈的数据类型,有模版来代替,这样想要存什么类型,直接示例化什么类型的即可。

template<typename T>
class Stack
{
public:
	Stack()
	{
		_array = (T*)malloc(sizeof(T) * 3);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = 3;
		_size = 0;
	}
	void Push(T data)
	{
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	T* _array;
	int _capacity;
	int _size;
};

int main()
{
	Stack<int> st1; //int
	Stack<double> st2; //double
	return 0;
}

当然我们的模版也可以把声明和定义分开,但是注意: 模版的定义和声明要分离也分离在 .h 文件中,最好不要一个在 .h 文件 一个在 .cpp文件,这样会让编译的时间大大增加。
在这里插入图片描述

在这里插入图片描述
同时我们的T还要另外声明一次,不然编译器不知道T是哪来的。类域声明的时候我们要 S t a c k < i n t > Stack<int> Stack<int>这样写。因为此时我们的 S t a c k < i n t > Stack<int> Stack<int>才是我们的模版类型。

结言

本文只是简单介绍了模版的基本概念,后期学了更深的内容会持续更新…