【C++】模板初阶STL简介

news2024/12/24 11:31:31

今天,你内卷了吗?
在这里插入图片描述

文章目录

  • 一、泛型编程
  • 二、函数模板(显示实例化和隐式实例化)
    • 1.函数模板格式
    • 2.单参数模板
    • 3.多参数模板
    • 4.模板参数的匹配原则
  • 三、类模板(没有推演的时机,统一显示实例化)
    • 1.类模板的使用
    • 2.类模板实现静态数组
    • 3.类模板能否声明和定义分离?
  • 四、STL简介
    • 1.什么是STL
    • 2.STL的版本
    • 3.STL的六大组件



一、泛型编程

1.
假设要交换两个变量的值,如果只是用普通函数来做这个工作的话,那么只要变量的类型发生变化,我们就需要重新写一份普通函数,如果是C语言,函数名还不可以相同,但是这样很显然非常的麻烦,代码复用率非常的低。
那么能否告诉编译器一个模板,让编译器通过模板来根据不同的类型产生对应的代码呢?答案是可以的。

2.
而上面这样利用模板来生成类型所对应的代码,这样的思想实际上就是泛型编程。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。
模板正是泛型编程的基础,模板又可以分为类模板和函数模板。

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

二、函数模板(显示实例化和隐式实例化)

1.函数模板格式

1.
函数模板格式:
template<typename T1, typename T2,…,typename Tn>
返回值类型 函数名(参数列表){ }

2.
typename和class是用来定义模板参数的关键字。

template<typename T>//typename和class这里都可以
void Swap(T& left, T& right)
{
	T tmp = left;
	left = right;
	right = tmp;
}
int main()
{
	int a = 1, b = 2;
	Swap(a, b);
	
	double c = 1.1, d = 2.2;
	Swap(c, d);
	
	return 0;
}

2.单参数模板

1.
模板的实例化有两种方式,一种是显示实例化,一种是隐式实例化,隐式实例化就是让编译器根据实参所传类型确定模板参数,然后推导出来函数,显式实例化是告诉编译器指定模板参数的类型。

2.
如果显示实例化后,实参与指定模板参数类型不同,则编译器会自动发生隐式类型转换。

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;
	//自动推演实例化,让编译器推导T的类型
	cout << Add(a1, a2) << endl;
	cout << Add(d1, d2) << endl;

	cout << Add((double)a1, d2) << endl;//强制类型转换,产生临时变量,临时变量具有常性
	cout << Add(a1, (int)d2) << endl;

	//显示实例化,直接告诉编译器T的类型
	cout << Add<double>(a1, d2) << endl;//隐式类型转换,产生临时变量,临时变量具有常性
	cout << Add<int>(a1, d2) << endl;
	
	return 0;
}

3.多参数模板

模板参数除单个外,也可以是多个,在使用上和单参数模板没什么区别,同样实例化的方式也可分为两种,一种是隐式实例化,一种是显示实例化。

template<class T1,class T2>
T1 Add(const T1& left, const T2& right)
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;
	//自动推演实例化,让编译器推导T的类型
	cout << Add(a1, a2) << endl;//T1和T2都推成int
	cout << Add(d1, d2) << endl;//都推成double

	cout << Add(a1, d2) << endl;//推成一个是int,一个是double
	cout << Add(d1, a2) << endl;

	return 0;
}

4.模板参数的匹配原则

1.
普通函数和模板函数若同名,是可以同时存在的,相当于两个函数构成重载,在调用上,一般会优先调用普通函数,因为编译器虽然勤快,但是它不傻,他知道调用模板函数需要进行推演实例化,如果有现成的普通函数,他肯定不会去推导模板实例化的。但是如果强行显式实例化模板参数,那编译器也没辙,就会显示调用模板推导出来的函数。

// 专门处理int的加法函数
int Add(int left, int right)// _Z3Addii
{
	return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)// _Z3TAddii(修饰规则不一定这样,只是假设而已)
{
	return left + right;
}
//一个具体的函数和模板函数能不能同时存在呢?答案是可以的,他们是可以同时存在的
int main()
{
	int a = 1, b = 2;
	Add(a, b);//会优先调用具体的函数,而不是调用模板进行推导。编译器很勤快,但它不傻。
	
	Add<int>(a, b);//显示调用模板推导出来的函数。

	//上面两行代码可以说明模板推导的int函数和具体的int函数可以同时存在,那么就可以证明这两个函数的函数名修饰规则是不一样的

	return 0;
}

三、类模板(没有推演的时机,统一显示实例化)

1.类模板的使用

1.
类模板和函数模板在使用上有些区别,函数模板可以隐式实例化,通过实参类型进行函数推演,而类模板是无法隐式实例化的,因为没有推演的时机,所以对于类模板,统一使用显示实例化,即在类后面加尖括号,尖括号中存放类型名,进行类模板的实例化。

2.
值得注意的是类模板不是具体的类,具体的类是需要进行实例化的,只有类名后面的尖括号跟上类型才算实例化出真正的类。
例如下面的栈,如果想让栈存储不同类型的数据,就需要显示实例化类模板。

template<typename T>
class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity = )" <<capacity<<endl;

		_a = (T*)malloc(sizeof(T)*capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}

		_top = 0;
		_capacity = capacity;
	}
	
	~Stack()
	{
		cout << "~Stack()" << endl;

		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}

	void Push(const T& x)//用引用效率高一点,因为类对象可能所占字节很大,如果不改变最好加const
	{
		// ....
		// 扩容
		_a[_top++] = x;
	}

private:
	T* _a;
	int _top;
	int _capacity;
};
int main()
{
	//Stack st1; 
	st1.push(1);
	//st1.Push(1.1);

	//Stack st2; 
	//st2.Push(1);

	//一个栈存int,一个栈存double这样的场景,用typedef无法解决,必须使用类模板来解决。

	//函数模板可以通过实参传递形参,推演模板参数。但是类模板一般没有推演的时机,统一使用显示实例化。
	Stack<int> st1;
	st1.Push(1);
	Stack<double>st2;
	st2.Push(1.1);

	//180和182行是两个不同的类类型,因为类的大小有可能不同,所以类模板相同,但模板参数不同,则类模板实例化出来的类是不同的。

	//st1 = st2;//这样的操作是不允许的,因为如果类不同,则实例化对象肯定也不相同,所以不可以赋值。

	return 0;
}

2.类模板实现静态数组

1.
std命名空间中的array可能和我们的array产生冲突,所以我们可以利用自己的命名空间将自己的类封装起来,以免产生冲突。

2.
利用运算符重载可以实现对静态数组中每一个元素进行操控。
与C语言不同的是,这种运算符重载可以不依赖于抽查行为,而是进行严格的越界访问检查,通过assert函数来进行严格检查。

#define N 10

namespace wyn
{
	template<class T>
	class array
	{
	public:
		inline T& operator[](size_t i)//内联,不会建立栈帧,提高效率,减少性能损耗。
		{
			assert(i < N);//不依赖于抽查行为
			return _a[i];
		}

	private:
		T _a[N];
	};
}

int main()
{
	int a2[10];
	//C语言对于越界访问是抽查的方式,访问近一点的位置,会查到报错,但访问远一点的位置,就有可能不会报错。
	//a2[10] = 10;
	//a2[20] = 20;//对于越界访问写,勉强会检查到。
	a2[10];
	a2[20] ;//但对于越界访问读,基本不会检查出来。
	
	wyn::array<int> a1;//array有可能和std命名空间里面的array冲突,所以我们自己定义一个命名空间
	
	for (size_t i = 0; i < N; i++)
	{
		a1[i] = i;
		//等价于 a1.operator[](i)=i;
	}
	for (size_t i = 0; i < N; i++)
	{
		a1[i]++;
	}
	for (size_t i = 0; i < N; i++)
	{
		cout << a1[i] << " ";
	}
	cout << endl;
	
	return 0;
}

3.类模板能否声明和定义分离?

1.
首先明确一点,类模板是不允许声明和定义分离的,因为这会发生链接错误。

2.
其实原因很简单,因为在用的地方类模板的确进行了实例化,可是用的地方只有声明没有定义,而定义的地方又没有进行实例化,所以就会发生链接错误。

3.
说白了就是Stack.cpp里面的类模板由于没有实例化,那就是没有真正的类,所以类中成员函数的地址无法进入符号表,那么在链接阶段,Test.cpp就无法链接到类成员函数的有效地址。

4.
解决方式有两种:
a.既然在Stack.cpp里面类模板没有实例化么,那我们就手动在Stack.cpp里面进行实例化就好了,但是这样也有一个弊端,只要类模板参数类型改变,我们手动实例化时就需要多加一行,这未免有些太繁琐了吧!

在这里插入图片描述
b.
所以最好的方式就是不要将类成员函数定义和声明分文件存放,而是将类模板中的成员函数直接放在.h文件里面。这样就不会出现找不到有效地址的问题了,因为一旦Test.cpp中进行了模板实例化,则.h文件中的那些方法也就会实例化,此时他们的地址就会进入符号表。也有人会将.h文件重命名为.hpp文件,这就是典型的模板类的声明和定义方式。

template<class T>
Stack<T>::Stack(int capacity)//声明处写了缺省值,定义处就不用写了。
{
	cout << "Stack(int capacity = )" << capacity << endl;

	_a = (T*)malloc(sizeof(T) * capacity);
	if (_a == nullptr)
	{
		perror("malloc fail");
		exit(-1);
	}

	_top = 0;
	_capacity = capacity;
}

template<class T>
Stack<T>::~Stack()
{
	cout << "~Stack()" << endl;

	free(_a);
	_a = nullptr;
	_top = _capacity = 0;
}

template<class T>
void Stack<T>::Push(const T& x)
{
	// ....
	// 
	_a[_top++] = x;
}

在这里插入图片描述

在这里插入图片描述

四、STL简介

1.什么是STL

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。

2.STL的版本

原始版本:
Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 HP 版本–所有STL实现版本的始祖。
P. J. 版本:
由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。
RW版本:
由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。
SGI版本:
由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。我们后面学习STL要阅读部分源代码,主要参考的就是这个版本。

3.STL的六大组件

在这里插入图片描述

STL的六大组件(转载自博客园博主WELEN的文章)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RTOS之二环境搭建初识RTOS

参考&#xff1a;https://blog.csdn.net/kouxi1/article/details/123650688RTOS本质就是切换线程栈&#xff0c;栈换了环境就换了&#xff0c;一个重要的结构tcb&#xff08;linux叫PCB或thread_info&#xff09;&#xff1a;struct tcb{int32_t *sp; // 重要的sp指针&#xff…

seata【SAGA模式】代码实践(细节未必完全符合saga的配置,仅参考)

seata SAGA模式&#xff1a; 代码仍然是上一篇AT模式的代码&#xff1a;AT模式 不需要undo_log表 下面开始&#xff1a; 首先&#xff0c;saga模式依靠状态机的json文件来执行整个流程&#xff0c;其中的开始节点的服务即TM&#xff0c;然后状态机需要依靠三张表&#xff0…

【大数据】Hadoop-HA-Federation-3.3.1集群高可用联邦安装部署文档(建议收藏哦)

背景概述 单 NameNode 的架构使得 HDFS 在集群扩展性和性能上都有潜在的问题&#xff0c;当集群大到一定程度后&#xff0c;NameNode 进程使用的内存可能会达到上百 G&#xff0c;NameNode 成为了性能的瓶颈。因而提出了 namenode 水平扩展方案-- Federation。 Federation 中…

C语言---字符串函数总结

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;夏目的C语言宝藏 &#x1f4ac;总结&#xff1a;希望你看完之…

ChatGPT国内使用方法全攻略(完整图文教程)

你好呀&#xff0c;我是月亮&#xff0c;一个90后的老程序员啦~ 最近ChatGPT完全火出圈了。 相关教程很多&#xff0c;我整理了一份网盘汇总&#xff0c;包含注册、谷歌浏览器插件使用、国内面注册平台&#xff0c;需要的小伙伴自取~ 网盘地址&#xff1a;使用方式汇总文档 …

数据库实践LAB大纲 06 INDEX

索引 索引是一个列表 —— 若干列集合和这些值的记录在数据表存储位置的物理地址 作用 加快检索速度唯一性索引 —— 保障数据唯一性加速表的连接分组和排序进行检索的时候 —— 减少时间消耗 一般建立原则 经常查询的数据主键外键连接字段排序字段少涉及、重复值多的字段…

分享114个JS菜单导航,总有一款适合您

分享114个JS菜单导航&#xff0c;总有一款适合您 114个JS菜单导航下载链接&#xff1a;https://pan.baidu.com/s/1t4_v0PipMjw3ULTLqkEiDQ?pwdgoi2 提取码&#xff1a;goi2 Python采集代码下载链接&#xff1a;https://wwgn.lanzoul.com/iKGwb0kye3wj $.ajax({type: &quo…

“万字“ Java I/O流讲解

Java I/O流讲解 每博一文案 谁让你读了这么多书&#xff0c;又知道了双水村以外还有一个大世界&#xff0c;如果从小你就在这个天地里&#xff0c;日出而作&#xff0c;日落而息。 那你现在就会和众乡亲抱同一理想&#xff1a;经过几年的辛劳&#xff0c;像大哥一样娶个满意的…

2023年中国各大城市薪酬报告出炉

全国地区&#xff1a;https://download.csdn.net/download/std86021/87322224北京&#xff1a;https://download.csdn.net/download/std86021/87273488上海&#xff1a;https://download.csdn.net/download/std86021/87322226广州&#xff1a;https://download.csdn.net/downlo…

Linux之文本搜索命令

文本搜索命令学习目标能够知道文本搜索使用的命令1. grep命令的使用命令说明grep文本搜索grep命令效果图:2. grep命令选项的使用命令选项说明-i忽略大小写-n显示匹配行号-v显示不包含匹配文本的所有行-i命令选项效果图:-n命令选项效果图:-v命令选项效果图:3. grep命令结合正则表…

linux基本功系列之hostname实战

文章目录前言一. hostname命令介绍二. 语法格式及常用选项三. 参考案例3.1 显示本机的主机名3.2 临时修改主机名3.3 显示短格式的主机名3.4 显示主机的ip地址四. 永久修改主机名4.1 centos6 修改主机名的方式4.2 centos7中修改主机名永久生效总结前言 大家好&#xff0c;又见面…

Java、JSP企业快信系统的设计与实现

技术&#xff1a;Java、JSP等摘要&#xff1a;计算机网络的出现到现在已经经历了翻天覆地的重大改变。因特网也从最早的供科学家交流心得的简单的文本浏览器发展成为了商务和信息的中心。到了今天&#xff0c;互联网已经成为了大量应用的首选平台&#xff0c;人们已经渐渐习惯了…

02- 天池工业蒸汽量项目实战 (项目二)

忽略警告: warnings.filterwarnings("ignore") import warnings warnings.filterwarnings("ignore") 读取文件格式: pd.read_csv(train_data_file, sep\t) # 注意sep 是 , , 还是\ttrain_data.info() # 查看是否存在空数据及数据类型train_data.desc…

线程池框架

这是之前有做的一个可以接受用户传入任意类型的任务函数和任意参数&#xff0c;并且能拿到任务对应返回值的一个线程池框架&#xff0c;可以链接成动态库&#xff0c;用在相关项目里面。一共实现了两版&#xff0c;都是支持fixed和cached模式的&#xff0c;半同步半异步的&…

全局向量的词嵌入(GloVe)

诸如词-词共现计数的全局语料库统计可以来解释跳元模型。 交叉熵损失可能不是衡量两种概率分布差异的好选择&#xff0c;特别是对于大型语料库。GloVe使用平方损失来拟合预先计算的全局语料库统计数据。 对于GloVe中的任意词&#xff0c;中心词向量和上下文词向量在数学上是等…

分享113个JS菜单导航,总有一款适合您

分享113个JS菜单导航&#xff0c;总有一款适合您 113个JS菜单导航下载链接&#xff1a;https://pan.baidu.com/s/1d4nnh-UAxNnSp9kfMBmPAw?pwdcw23 提取码&#xff1a;cw23 Python采集代码下载链接&#xff1a;https://wwgn.lanzoul.com/iKGwb0kye3wj base_url "http…

MySQL 4:MySQL函数

为了提高代码的复用性和隐藏实现细节&#xff0c;MySQL提供了很多函数。函数可以理解为别人封装好的模板代码。 在MySQL中&#xff0c;函数有很多&#xff0c;主要可以分为以下几类&#xff1a;聚合函数、数学函数、字符串函数、日期函数、控制流函数、窗口函数。 一、聚合函…

研一寒假C++复习笔记--深拷贝和浅拷贝代码实例

目录 1--深拷贝和浅拷贝的基础概念 2--浅拷贝的代码实例 3--深拷贝代码实例 4--参考 1--深拷贝和浅拷贝的基础概念 ① 浅拷贝&#xff1a;简单的赋值拷贝操作&#xff1b; ② 深拷贝&#xff1a;在堆区重新申请空间&#xff0c;进行拷贝操作&#xff1b; 2--浅拷贝的代码…

CUDA中的统一内存

文章目录1. Unified Memory Introduction1.1. System Requirements1.2. Simplifying GPU Programming1.3. Data Migration and Coherency1.4. GPU Memory Oversubscription1.5. Multi-GPU1.6. System Allocator1.7. Hardware Coherency1.8. Access Counters2. Programming Mode…

格子玻尔兹曼法介绍

1 LBM简介格子玻尔兹曼法&#xff08;Lattice Boltzmann Method&#xff09;简称LBM&#xff0c;是一种CFD算法&#xff0c;可求解流动、传热等常见CFD问题。LBM基于格子玻尔兹曼方程&#xff08;LBE&#xff09;&#xff0c;从介观尺度&#xff08;mesoscope&#xff09;描述了…