C++——模板初阶(收录在专栏C++入门到精通)

泛型编程

指的是编写与类型无关 的通用代码,是代码复用的一种手段,而模板是泛型编程的基础

模板分为函数模板类模板

函数模板

介绍

函数模板代表了一个函数家族,与类型无关,在使用时会被参数化,根据实参类型生成函数的特定类型版本。

原理示意图

函数模板的格式

template<typename T1,typename T2...Typename Tn>。template 是模板的意思,这里的typename写成class 也行。

< >里面的一长串是模板参数列表 ,代表不同的类型 ,我们可以类比一下函数的参数列表,函数参数列表:类型 变量名模板参数列表:typename 类型名或者class 类型名。

函数模板的实例化

用不同类型的参数使用函数模板,称为函数模板的实例化。其中有隐式实例化和显示实例化之分。

隐式实例化

指的就是:编译器根据实参类型推导出模板参数类型。

所以当实参类型多于模板参数类型会报错,比如如下场景。我们实现了一个Add函数,分别传了int 和 double实参。

cpp 复制代码
template <class T1>
T1 Add(const T1& x, const T1& y)
{
	return x + y;
}
int main()
{
	int c = 18;
	double d = 10.89;
	std::cout << Add(c, d) << std::endl;
	return 0;
}

在VS上编译会报错

那么我们想实现浮点数相加该怎么办呢?可以使用隐式类型转换,如下图

显示实例化

在函数名后面加上< >指定要实例化的参数类型。如果不匹配就会尝试隐式类型转换,如果无法转换就会报错。

就像上面的Add()函数,我们就可以这样实力化

一般地,在由实参无法推导出模板参数地类型 时,我们就需要显示实例化。

同名的非模板函数和模板是可以同时存在的,并且当其他条件相同时会优先调用非模板函数,因为这样就不用自己生成了,省了功夫。

类模板

类模板的定义格式

一个示例(以栈为例)

cpp 复制代码
template < class T1>
class Stack
{
public:
	Stack(int n=4)
		:_array(new T1[n])
		,_size(0)
		,_capacity(n)
	{ }
	Stack(const Stack& s)
		:_size(s._size )
		,_capacity(s._capacity )
	{
		_array = new T1[s.capacity];
		memcpy(_array, s._array, s._size*sizeof(T1));
	}
	~Stack()
	{
		delete[]_array;
		_size = _capacity = 0;
	}
	void Push(const T1& t)
	{

		if (_size == _capacity)
		{
			T1* tmp = new T1[2 * _capacity];
			memcpy(tmp, _array, _size * sizeof(T1));
			delete[]_array;
			_array = tmp;
			_capacity *= 2;
		}
		_array[_size++] = t;
	}
	void Print()
	{
		for (int i = 0; i < _size; i++)
		{
			std::cout << _array[i] << " ";
		}
	}
private:
	T1* _array;
	int _size;
	int _capacity;
};

实例化

都是显示实例化,因为无法向函数模板那样根据实参去推演。

如图

和typedef的区别

有些初学者可能会这样认为,函数模板也没什么了不起的嘛,我在C语言中用typedef不一样能根据需求改变类型吗?那我问你,typedef能允许int 和double的栈同时出现吗?显然不能,用typedef只能同时存在一个栈,他终究不是模板,不可以无限复制。

注意

模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误,具体原因后面会讲

类模板成员函数在外定义

这时要重新提供函数模板参数,

如下代码`

cpp 复制代码
template <class T1>
 void Stack<T1>::Push (const T1&t)
{
	 if (_size == _capacity)
	 {
		 T1* tmp = new T1[2 * _capacity];
		 memcpy(tmp, _array, _size * sizeof(T1));
		 delete[]_array;
		 _array = tmp;
		 _capacity *= 2;
	 }
	 _array[_size++] = t;
};

这里的函数模板参数只是一个代号,真正决定它是什么的是类模板实例化成什么。

相关推荐
彧azz5 小时前
图的存储结构详解:邻接矩阵的原理、实现与应用
开发语言·数据结构·学习·php
hansang_IR5 小时前
【题解】P4460 [CQOI2018] 解锁屏幕
c++·算法
嵌入式学习菌5 小时前
Modbus‑RTU 数据类型分析
开发语言·单片机·bug
ChampaignWolf6 小时前
在 SAP S/4HANA 内部基于 ICF Handler 从零实现 ABAP MCP Server:架构、代码与踩坑实录
c++·架构·sap·abap·mcp
matlab代码6 小时前
基于matlab数字图像处理的指纹识别系统【源码68期】
开发语言·matlab
matlab代码6 小时前
基于matlab的水果识别系统(苹果香蕉菠萝梨桃子)【源码65期】
开发语言·matlab
wuyk5557 小时前
从零吃透 MQTT 通信|第 8 章 FreeRTOS 多任务架构下 MQTT 工程架构,任务拆分、队列解耦、临界区保护
c语言·开发语言·stm32·学习·架构
weixin199701080167 小时前
[特殊字符]《跨境二手ERP对接Back Market:标准化API + 7~10工作日技术合规审核实录》(附Python源码)
开发语言·python·pandas
尘客-追梦7 小时前
Day 01:插件化之前,先看清 ABI 这堵墙
开发语言·c++·qt
持敬chijing7 小时前
Python-数据类型-列表
开发语言·python·青少年编程