【C++初阶】模板

2023-09-21 11:52:46

在这里插入图片描述

​👻内容专栏: C/C++编程
🐨本文概括: 泛型编程的认识、函数模板、类模板。
🐼本文作者: 阿四啊
🐸发布时间:2023.9.21

泛型编程

前面我们学过函数重载,我们可以使用同一个函数名Swap,实现多个重载函数,对intchardouble……类型进行交换,举例如下:

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

使用函数重载虽然可以实现,但是有一下几个不好的地方:
1.重载的函数仅仅是类型不同,代码比较冗余,只要有新类型出现时,就需要用户自己增加对应的函数
2.代码的可维护性比较低,一个出错可能所有的重载均出错。

那能否告诉编译器一个模具,让编译器根据不同的类型利用该模子来生成代码呢?
如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的物件(即生成具体类型的代码)。

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
在这里插入图片描述

函数模板

函数模板概念

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

函数模板格式

template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}

函数模板示例👇

template<typename T>
void Swap( T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}

在这个示例中,Swap 是一个函数模板,它接受两个参数 leftright,它们的类型由 T 指定(一般用T命名,它代表着typename的首字母)。T 是一个占位符类型,可以用任何有效的数据类型(如整数、浮点数、字符串等)来实例化它。
⚠️注意:template是模板关键字,可以定义多个模板类型T1、T2、……、Tntypename是用来定义模板参数的关键字,也可以使用class.

函数模板的原理

函数模板是一个蓝图,它本身并不是函数,是编译器用一种方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。
在这里插入图片描述
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。 比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。

函数模板的实例化

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

  1. 隐式实例化:让编译器根据实参推演模板参数的实际类型。

    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;
    	//隐式实例化
    	Add(a1, a2);
    	Add(d1, d2);
    	/*
    	该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
    	通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,
    	编译器无法确定此处到底该将T确定为int 或者 double类型而报错
    	注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅
    	Add(a1, d1);
    	*/
    	
    	// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化
    	Add(a, (int)d);
    	
    	return 0
    }
    
  2. 显式实例化:在函数名后的<>中指定模板参数的实际类型

    int main(void)
    {
    	int a = 10;
    	double b = 20.0;
    	// 显式实例化
    	Add<int>(a, b);
    	
    	return 0;
    }
    

如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

模板参数的匹配规则

  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。

    // 专门处理int的加法函数
    int Add(int left, int right)
    {
    	return left + right;
    }
    // 通用加法函数
    template<class T>
    T Add(T left, T right)
    {
    	return left + right;
    }
    void Test()
    {
    	Add(1, 2); // 与非模板函数匹配,编译器不需要特化
    	Add<int>(1, 2); // 调用编译器特化的Add版本
    }
    
  2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。

    // 专门处理int的加法函数
    int Add(int left, int right)
    {
    	return left + right;
    }
    // 通用加法函数
    template<class T1, class T2>
    T1 Add(T1 left, T2 right)
    {
    	return left + right;
    }
    void Test()
    {
    	Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
    	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
    }
    
  3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。

类模板

类模板的定义格式

template<class T1, class T2, ..., class Tn>
class classname
{
	// 类内成员定义
};

使用类模板,类里面的函数该如何进行声明与定义分离呢?下面用一个动态顺序表Vector做演示:

 // 动态顺序表
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template<class T>
class Vector
{
public :
	Vector(size_t capacity = 10)
	: _pData(new T[capacity])
	, _size(0)
	, _capacity(capacity)
	{}
	
	// 使用析构函数演示:在类中声明,在类外定义。
	~Vector();
	
	void PushBack(const T& data);
	void PopBack();
	
	// ...
	size_t Size() {return _size;}
	T& operator[](size_t pos)
	{
		assert(pos < _size);
		return _pData[pos];
	}
private:
	T* _pData;
	size_t _size;
	size_t _capacity;
};

// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表
//类名为Vector<T>,而不是Vector
template <class T>
Vector<T>::~Vector()
{
	if(_pData)
	delete[] _pData;
	_size = _capacity = 0;
}

int main()
{
	Vector<int> v;
}

注意⚠️:此时的Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具,是一个类模板。~Vetcor的声明与定义不可以放在两个文件中,即.cpp和.h文件中,这样是不可以的,具体原因在模板进阶中讲述!

那么我们对此前的认识:类名即是一种类型,但这里使用了类模板的VectorVector即为类名,类型为Vector<T>,而不是Vector,所以我们对析构函数~Vecor进行声明与定义分离时,声明的类域得是Vetcor<T>::,而且还得单独声明模板参数列表template <class T>!

类模板的实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

// Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

🤗模板的初阶就到这里,我们对模板有一个大体的认识,主要是应对STL的学习,后面C++进阶会再对模板进一步学习!

更多推荐

Node.js

它们都是基于nodejs开发的WebpackViteBabelVSCodeFigmaPostman常用的命令行指令C:/D:切换盘符dir列出当前目录下的所有文件cd目录名进入到指定目录md目录名创建一个文件夹rd目录名删除一个文件夹.表示当前目录..表示上一级目录当我们在命令行窗口打开一个文件,或调用一个程序时,系统

JWT安全

文章目录JWT是什么?为什么要使用JWT?JWT的数据结构JWT的工作过程JWT是什么?JSONWebToken(JWT)是一个开放标准(RFC7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。JWT全称JSONWebToken,是一种传输信息的标准,用于JSON对象在各方之间

代码随想录算法训练营第58天 | ● 739. 每日温度 ● 496.下一个更大元素 I

文章目录前言一、739.每日温度二、496.下一个更大元素I总结前言单调栈;一、739.每日温度单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是整个数组只需要遍历一次。更直白来说,就是用一个栈来记录我们遍历过的元素,因为我们遍历数组的时候,我们不知道之前都遍历了哪些元素

前端Layui框架介绍

当涉及到前端UI框架时,Layui(简称layui)是一个备受欢迎的框架之一。在这篇博客中,我们将深入了解layui,包括其市场占有率、开发语言、使用场景、框架特点以及一些使用案例。1.市场占有率Layui是一款流行的前端UI框架,在开发者社区中非常受欢迎。它的市场占有率在前端开发领域相当高,许多企业和开发者选择使用l

FreeSWITCH 1.10.10 简单图形化界面11 - 简单封装一下JSSIP

FreeSWITCH1.10.10简单图形化界面11-简单封装一下JSSIP0、界面预览1、前言2、demo地址3、简单演示4、demo代码FreeSWITCH界面安装参考:https://blog.csdn.net/jia198810/article/details/1324793240、界面预览http://myf

【vue.js】路由使用与Node.js下载安装之环境配置

🎬艳艳耶✌️:个人主页🔥个人专栏:《Spring与Mybatis集成整合》《springMvc使用》⛺️生活的理想,为了不断更新自己!目录1、路由1.1什么是路由1.2案列实操1.2.1引入vue-router的js依赖1.2.2定义组件1.2.3定义路由1.2.4将路由加入路由器1.2.5将路由挂载根实例1.2.

状态管理艺术——借助Spring StateMachine驭服复杂应用逻辑

文章目录1.什么是状态2.有限状态机概述3.SpringStateMachine4.SpringStateMachine入门小案例4.1接口测试5.总结1.什么是状态在开发中,无时无刻离不开状态的一个概念,任何一条数据都有属于它的状态。比如一个电商平台,一个订单会有很多状态,比如待付款、待发货、待收货、完成订单。而这其

互联网摸鱼日报(2023-09-18)

互联网摸鱼日报(2023-09-18)36氪新闻最前线|号外电摩12.68万元起订,配16.9度一体压铸电池包本周双碳大事:CCER交易管理办法获生态环境部原则通过;明阳斥资100亿元加码光伏项目;“全路程”获2亿元D轮融资200亿,贝佐斯投出全球最大天使轮12种自由职业者管理时间的高效方法开彩票站,还是一门好生意吗:

阿曼市场最全开发攻略,看这一篇就够了

中东是一个充满外贸机遇的市场,已经成为很多外贸人重点开发的市场。阿曼的海岸南方和东方临阿拉伯海,东北方则抵阿曼湾。阿曼因为扼守着世界上最重要的石油输出通道——波斯湾和阿曼湾之间的霍尔木兹海峡,所以地理位置非常重要,商机也是很多的。作为外贸人,如何开发阿联酋市场客户呢?今天就来谈谈这个问题。文章略长,大家点赞收藏+关注,

【深度学习】 Python 和 NumPy 系列教程(十九):Matplotlib详解:2、3d绘图类型(5)3D等高线图(3D Contour Plot)

目录一、前言二、实验环境三、Matplotlib详解1、2d绘图类型2、3d绘图类型0.设置中文字体1.3D线框图(3DLinePlot)2.3D散点图(3DScatterPlot)3.3D条形图(3DBarPlot)4.3D曲面图(3DSurfacePlot)5.3D等高线图(3DContourPlot)一、前言Py

设计模式:简单工厂模式

目录代码实现总结简单工厂模式(SimpleFactoryPattern)是一种创建型设计模式,它提供了一种创建对象的最简单方式,通过一个工厂类根据传入的参数来决定创建哪种产品类的实例。在简单工厂模式中,有三个主要角色:产品(Product):定义了工厂创建的对象的接口。具体产品(ConcreteProduct):实现了

热文推荐