C++ 模板


没有模板的对于不同类型的函数需要特化出来各种不同的版本
比如:

void Swap(int& left, int& right)
{
 int temp = left;
 left = right;
 right = temp;
}
void Swap(double& left, double& right)
{
 double temp = left;
 left = right;
 right = temp;
}
void Swap(char& left, char& right)
{
 char temp = left;
 left = right;
 right = temp;
}

函数模板

有了这个模板,就再也不需要自己手动去写特定类型的交换
利用引用就可以交换自定义类型和内置类型,指针也不需要再用二级指针了

template<class T>// 模板参数 -- 类型
void Swap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	int a = 0, b = 1;
	double c = 1.1, d = 2.2;
	Swap(a, b);
	Swap(c, d);
	

	int* p1 = &a, *p2 = &b;
	Swap(p1, p2);



	//Date d1(2023,5,12), d2(2023, 1, 1);

	return 0;
}

函数模板的原理

编译器根据传入的实参类型推导生成对应类型的函数以供调用。
这件事本来是我们要做的,但是现在有了模板就不需要手动再写

在这里插入图片描述

而且在调试时,我们发现调用进入的是模板函数,但是实际上这只是调试想让你看见的,实际反汇编看到调用函数时模板还是要实例化生成具体类型函数,模板只是一个模子而已
在这里插入图片描述

在这里插入图片描述

函数模板格式

模板参数定义的T是类型
template <calss/typename T/K> T类型名 任取

定义多个模板参数 + 模板类型做返回值

template<class T1, class T2>
T1 func(const T1& x, const T2& y)
{
	return x + y;
}

cout << func(1, 2.3);

这里为什么要对形参x,y加上const?
因为1,2.3是常量,常量加上引用不加const会权限放大

函数模板的实例化

隐式实例化

template<class T>
T Add(const T& left, const T& right)
{
 return left + right;
}
int main()
{
 int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	//cout << Add(a1, a2) << endl;
	//cout << Add(d1, d2) << endl;

	//Add(a1, d1);//同一个T类型,传入不同类型参数,打架了
	//实参传递的类型,推演T的类型
	cout << Add(a1, (int)d1) << endl;//强制类型转换需要开辟临时空间,因为不能修改原本变量,所以形参要加const
	
	cout << Add((double)a1, d1) << endl;

无论是强制类型转换还是隐式类型转换,都会开辟临时空间,因为不能修改原本变量的值,所以形参要加上const,因为临时空间具有常性

显式实例化

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

//显示实例化,用指定类型实例化
	cout << Add<int>(a1, d1) << endl;
	cout << Add<double>(a1, d1) << endl;

某些函数只能显示实例化

某些函数传入的形参类型不一定是T类型,编译器就无法推演类型了,只能显示实例化
在这里插入图片描述

模板匹配规则

能有现成的就吃现成的(原生函数),没有现成的就吃最喜欢的(模板)
函数调用
在这里插入图片描述
如图所示size_t不符合胃口,显然(10,3)更加适合模板特化,所以调用了下面的模板
在这里插入图片描述

类模板

类模板都是没办法通过推演实例化,类模板都是显示实例化

//类模板
template<class T>
class stack
{
public:
	stack(size_t capacity = 3)//此时构造对象时传入的参数不一定是T就无法自动推演类型
	{
		/*_array = (T*)malloc(sizeof(T) * capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}*/
		_array = new T[capacity];
		_capacity = capacity;
		_size = 0;
	}

	void Push(const T& data)
	{
		// CheckCapacity();
		_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;
	stack<double> st2;
	stack<char> st3;

	return 0;
}

类模板的声明和定义分离有些许别致

预备知识:
普通类,类名和类型是一样
类模板,类名和类型不一样
类名: Stack
类型: Stack< T >

构造函数的函数名 = 类名

stack< T >是类型,指定类域用类型

声明和定义分离首先要加上template< class T >,不然定义时函数体中怎么认识T是谁
并且要指定好类域stack< T >::

template<class T>
class stack
{
public:
	stack(size_t capacity = 3);
	

	void Push(const T& data);
	

	// 其他方法...

	~stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}

private:
	T* _array;
	int _capacity;
	int _size;
};

template<class T>
stack<T>::stack(size_t capacity)
{
	_array = new T[capacity];
	_capacity = capacity;
	_size = 0;
}

template<class T>
void stack<T>::Push(const T& data)
{
	// CheckCapacity();
	_array[_size] = data;
	_size++;
}

int main()
{
	stack<int> st1;
	stack<double> st2;
	stack<char> st3;

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值