初识C++ · 模板初阶

目录

1 泛型编程

2 函数模板

3 类模板


1 泛型编程

模板是泛型编程的基础,泛型我们碰到过多次了,比如malloc函数返回的就是泛型指针,需要我们强转。

既然是泛型编程,也就是说我们可以通过一个样例来解决类似的问题,比如:

void swap(int& a,int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

就交换问题来说,我们可以交换整型,可以交换浮点型,但是我们可不可以在一个文件里面实现同时交换两种类型呢?实际上对于C语言来说是不可以的,因为C语言没有模板的概念。

可能会说用typedef ,但是用了也只能解决交换其他类型的原因,不能实现同时交换两种不同的类型,也可能说用auto,但是auto不能作为函数参数使用。

这里就需要用到模板了,模板使用到了两个关键字,一个是template ,一个是typename,template是模板的英文名,typename是类型名的英文,还是很好理解的。

使用如下:

template <typename T1, typename T2,…… typename Tn>

2 函数模板

相关的关键字只有两个,这里来谈一下使用的问题,具体使用如下:

template <typename T>
//模板
void Swap(T& a,T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}
int main()
{
	int a = 1, b = 2;
	Swap(a, b);
	double c = 1.1, d = 2.2;
	Swap(c, d);
	return 0;
}

可能看起来比较抽象?

模板实现的原理是模板实例化,背后靠的都是编译器,编译器会自动推演需要的类型,所以这里会有一个问题:两次交换调用的是同一个函数吗?

当然不是的,这里可以借助汇编看一下:

汇编层面可以看到调用的函数的地址不一样,所以调用的函数不是一个函数。

模板其实是编译器在负重前面,我们使用模板的时候,编译器自动推演出我们需要的函数,

那么这里就涉及效率问题了,某种意义上来说模板的调用效率确实没有直接调用来的快,但是省事儿,我们不用再去多写那么多行代码了,毕竟函数重载也要多写代码的

基本的使用我们了解了,以后我们使用交换函数也不用自己去实现了,因为库里面有,可以直接进行使用:

现在有一些问题:

使用模板的时候,我们想要交换类型不同的怎么办?

如果我们尝试直接交换a c的话,就会报错,即没有函数模板是可以使用的

为了方便演示使用加法函数:

T Add(T x,T y)
{
	return x + y;
}

解决方法1->强制转换:

int main()
{
	int a = 1;
	double b = 2.5;
	Add(a,(int)b);
	return 0;
}

但是这种处理方法丢失了原本我们期待的结果,它造成了精度的丢失。

解决方法2->显式实例化:

int main()
{
	int a = 1;
	double b = 2.5;
	double ret = Add<double>(a,b);
	cout << ret;
	return 0;
}
int main()
{
	int a = 1;
	double b = 2.5;
	cout << Add<int,double>(a, b) << endl;
	return 0;
}

 显式实例化可以让编译器不去自动推演参数类型。

我们知道最后的结果是double类型的,所以显式调用double类型的加法函数,调用的写法是函数后面加个<>,里面写要显式调用的类型,但是呢,都差点意思。

解决方法3->多个模板混用 + auto:

template <typename T1,typename T2>
auto Add(const T1& x,const T2& y)
{
	return x + y;
}
int main()
{
	int a = 1;
	double b = 2.5;
	cout << Add(a, b) << endl;
	return 0;
}

既然是多参数,那么我们使用不同的模板就行,但是返回值的话,就用auto即可,这里可以说auto很妙,不会存在丢失精度的问题,也不用显式实例化,也不用强转什么的。

但是有些情况,是必须要显式实例化的:

T* Func(int a)
{
	T* p = (T*)operator new(sizeof(T));
	new(p)T(a);
	return p;
}

返回的是一个指针,但是函数返回什么类型呢?如果没有显式实例化的话是没有办法返回的。

这里就必须要显式实例化了:

int main()
{
	int a = 1;
	Func<int>(a);
	return 0;
}

再补充一个小点,typename可以用class进行代替,二者完全等价的。

template <typename T>
template <class T>

两种写法均可。

这里在引入一个点,叫匹配原则,有合适的优先选择合适的:

//整型的加法函数
int Add(int x,int y)
{
	cout << "int Add(int x,int y)" << endl;
	return x + y;
}
//通用加法函数
template <typename T1,typename T2>
auto Add(const T1& a,const T2& b)
{
	cout << "auto Add(const T1& a,const T2& b)" << endl;
	return a + b;
}
//一般加法
template<typename T>
T Add(const T& left, const T& right)
{
	cout << "T Add(const T& left, const T& right)" << endl;

	return left + right;
}

这里提供了三个函数可以实现加法,一个是类型完全匹配的,一个是通用的加法函数,一个是需要编译器需要自己推演的:

int main()
{
	cout << Add(1, 3) << endl;
	cout << Add(1, 3.3) << endl;
	cout << Add(1.1, 3.1) << endl;
	return 0;
}

那么这里的调用,就会:

第一个,因为完全匹配非模板的函数,所以优先调用;

第二个,因为更匹配通用的加法函数,需要编译器自己推演的力度没有那么大,所以调用两个模板的参数。

第三个,完全需要编译器自己推理,那就只能它了。

所以函数模板的调用也要看谁更匹配,优先使用更匹配的,编译器也想休息~


3 类模板

类模板其实后面用的最多的,现在先做个了解,比如stack,我们要实现两种栈,一个是用来存储int数据,一个是用来存储数据double的,就需要用到模板,不然解决不了一个文件存在两个类型栈的情况:

template<typename T>
class Stack
{
public:
	Stack(size_t capacity = 4)
	{
		_array = (T*)malloc(sizeof(T) * capacity);
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}

	void Push(const T& data);
private:
	T* _array;
	size_t _capacity;
	size_t _size;
};

有了模板之后,我们实现相同的就很容易了,这里如果里面的函数定义和声明分离的话,如果不是同一个.h文件的话,造成的效果是很难想象的,会造成编译的时间大幅度的增加。

分离之后的写法:

template<typename T>
class Stack
{
public:
	Stack(size_t capacity = 4)
	{
		_array = (T*)malloc(sizeof(T) * capacity);
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}

	void Push(const T& data);
private:
	T* _array;
	size_t _capacity;
	size_t _size;
};
template<class T>
void Stack<T>::Push(const T& data)
{
	;
}

创建不同的Stack就显式实例化就可以了:

int main()
{
    stack<int> p1;
    stack<double> p2;
    return 0;
}

模板初阶还是好理解的,后面介绍高阶的。


感谢阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/604567.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pytorch基础: torch.unbind()

1. torch.unbind 作用 说明&#xff1a;移除指定维后&#xff0c;返回一个元组&#xff0c;包含了沿着指定维切片后的各个切片。 参数&#xff1a; tensor(Tensor) – 输入张量dim(int) – 删除的维度 2. 案例 案例1 x torch.rand(1,80,3,360,360)y x.unbind(dim2)print(&…

gitlab集群高可用架构拆分部署

目录 前言 负载均衡器准备 外部负载均衡器 内部负载均衡器 (可选)Consul服务 Postgresql拆分 1.准备postgresql集群 手动安装postgresql插件 2./etc/gitlab/gitlab.rb配置 3.生效配置文件 Redis拆分 1./etc/gitlab/gitlab.rb配置 2.生效配置文件 Gitaly拆分 1.…

BACnet转MQTT网关智联楼宇json格式自定义

智能建筑的BACnet协议作为楼宇自动化领域的通用语言&#xff0c;正逐步迈向更广阔的物联网世界。随着云计算和大数据技术的飞速发展&#xff0c;如何将BACnet设备无缝融入云端生态系统&#xff0c;成为众多楼宇管理者关注的焦点。本文将以一个实际案例&#xff0c;揭示BACnet网…

60、郑州大学附属肿瘤医院 :用于预测胃癌患者术后生存的深度学习模型的开发和验证[同学,我们的人生应当是旷野]

馒头老师要说的话&#xff1a; 我近期看了一下北京的脑机公司&#xff0c;大概是我之前对这一行业太过于乐观&#xff0c;北京的BCI公司和研究所&#xff0c;比上海、深圳、杭州甚至是重庆都要少&#xff0c;门槛也要高很多。也有我自己的原因&#xff0c;有时站的太高&#x…

92、动态规划-最小路径和

思路&#xff1a; 还是一样&#xff0c;先使用递归来接&#xff0c;无非是向右和向下&#xff0c;然后得到两种方式进行比较&#xff0c;代码如下&#xff1a; public int minPathSum(int[][] grid) {return calculate(grid, 0, 0);}private int calculate(int[][] grid, int …

ubuntu_Docker安装配置

什么是docker? Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xff0c;然后发布到任何流行的 Linux或Windows操作系统的机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间不会有…

为什么要梯度累积

文章目录 梯度累积什么是梯度累积如何理解理解梯度累积梯度累积的工作原理 梯度累积的数学原理梯度累积过程如何实现梯度累积 梯度累积的可视化 梯度累积 什么是梯度累积 随着深度学习模型变得越来越复杂&#xff0c;模型的训练通常需要更多的计算资源&#xff0c;特别是在训…

深度学习笔记_10YOLOv8系列自定义数据集实验

1、mydaya.yaml 配置方法 # 这里分别指向你训练、验证、测试的文件地址&#xff0c;只需要指向图片的文件夹即可。但是要注意图片和labels名称要对应 # 训练集、测试集、验证机文件路径&#xff0c;可以是分类好的TXT文件&#xff0c;也可以直接是图片文件夹路径 train: # t…

Litedram仿真验证(四):AXI接口完成板级DDR3读写测试(FPGA-Artix7)

目录 日常唠嗑一、仿真中遗留的问题二、板级测试三、工程获取及交流 日常唠嗑 接上一篇Litedram仿真验证&#xff08;三&#xff09;&#xff1a;AXI接口完成仿真&#xff08;FPGA/Modelsim&#xff09;之后&#xff0c;本篇对仿真后的工程进行板级验证。 本次板级验证用到的开…

学成在线 - 第3章任务补偿机制实现 + 分块文件清理

7.9 额外实现 7.9.1 任务补偿机制 问题&#xff1a;如果有线程抢占了某个视频的处理任务&#xff0c;如果线程处理过程中挂掉了&#xff0c;该视频的状态将会一直是处理中&#xff0c;其它线程将无法处理&#xff0c;这个问题需要用补偿机制。 单独启动一个任务找到待处理任…

Layer1 公链竞争破局者:Sui 生态的全面创新之路

随着 Sui 生态逐渐在全球范围内树立起声望&#xff0c;并通过与 Revolut 等前沿金融科技平台合作&#xff0c;推广区块链教育与应用&#xff0c;Sui 生态的未来发展方向已成为业界瞩目的焦点。如今&#xff0c;Sui 的总锁定价值已攀升至 5.93 亿美元&#xff0c;充分展示了其在…

分布式架构的演技进过程

最近看了一篇文章,觉得讲的挺不错,就借机给大家分享一下。 早期应用:早期的应用比较简单,访问人数有限,大部分的开发单机就能完成。 分离模型:在业务发展后,用户数量逐步上升,服务器的性能出现瓶颈;就需要将应用和数据分开存储,避免相互抢占资源。 缓存模式:随着系…

历代著名画家作品赏析-东晋顾恺之

中国历史朝代顺序为&#xff1a;夏朝、商朝、西周、东周、秦朝、西楚、西汉、新朝、玄汉、东汉、三国、曹魏、蜀汉、孙吴、西晋、东晋、十六国、南朝、刘宋、南齐、南梁、南陈、北朝、北魏、东魏、北齐、西魏、北周、隋&#xff0c;唐宋元明清&#xff0c;近代。 一、东晋著名…

现身说法暑期三下乡社会实践团一个好的投稿方法胜似千军万马

作为一名在校大学生,去年夏天我有幸参与了学院组织的暑期大学生三下乡社会实践活动,这段经历不仅让我深入基层,体验了不一样的生活,更是在新闻投稿的实践中,经历了一次从传统到智能的跨越。回忆起那段时光,从最初的邮箱投稿困境,到后来智慧软文发布系统的高效运用,每一步都刻印…

顺序栈的操作

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd;既然选择了远方&#xff0c;当不负青春…

什么是DDoS攻击?DDoS攻击的原理是什么?

一、DDoS攻击概念 DDoS攻击又叫“分布式拒绝服务”(Distributed DenialofService)攻击&#xff0c;它是一种通过控制大量计算机、物联网终端或网络僵尸&#xff08;Zombie&#xff09;来向目标网站发送大量请求&#xff0c;从而耗尽其服务器资源&#xff0c;导致正常用户无法访…

WEB基础--JDBC操作数据库

使用JDBC操作数据库 使用JDBC查询数据 五部曲&#xff1a;建立驱动&#xff0c;建立连接&#xff0c;获取SQL语句&#xff0c;执行SQL语句&#xff0c;释放资源 建立驱动 //1.加载驱动Class.forName("com.mysql.cj.jdbc.Driver"); 建立连接 //2.连接数据库 Stri…

【3dmax笔记】026:挤出和壳修改器的使用

文章目录 一、修改器二、挤出三、壳 一、修改器 3ds Max中的修改器是一种强大的工具&#xff0c;用于创建和修改复杂的几何形状。这些修改器可以改变对象的形状、大小、方向和位置&#xff0c;以生成所需的效果。以下是一些常见的3ds Max修改器及其功能&#xff1a; 挤出修改…

Google Earth Engine谷歌地球引擎计算遥感影像在每个8天间隔内的多年平均值

本文介绍在谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#xff09;中&#xff0c;求取多年时间中&#xff0c;遥感影像在每1个8天时间间隔内的多年平均值的方法。 本文是谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#xff09;系列教学文章…

神经网络案例实战

&#x1f50e;我们通过一个案例详细使用PyTorch实战 &#xff0c;案例背景&#xff1a;你创办了一家手机公司&#xff0c;不知道如何估算手机产品的价格。为了解决这个问题&#xff0c;收集了多家公司的手机销售数据&#xff1a;这些数据维度可以包括RAM、存储容量、屏幕尺寸、…
最新文章