C++模版基础知识与STL基本介绍

news2024/11/24 19:18:09

目录

一. 泛型编程

二. 函数模板

1. 概念

 2. 函数模版格式

3. 函数模版的原理

4. 模版函数的实例化

(1). 隐式实例化

(2.) 显式实例化

5. 模版参数的匹配原则

三. 类模板

1. 类模板的定义格式

 2. 类模板的实例化

四. STL的介绍

1. 什么是STL?

 2. STL的版本

3. STL的六大组件


一. 泛型编程

我们之前实现一个交换函数,不同的类型就要用多个函数,我们学了函数重载比C语言时期方便了一点,不用想函数名了

如下代码

void Swap(int& xxxx, int& yyyy)
{
	int temp = xxxx;
	xxxx = yyyy;
	yyyy = temp;
}
void Swap(double& xxxx, double& yyyy)
{
	double temp = xxxx;
	xxxx = yyyy;
	yyyy = temp;
}
void Swap(char& xxxx, char& yyyy)
{
	char temp = xxxx;
	xxxx = yyyy;
	yyyy = temp;
}

但是呢,仍然有很多不好的地方:

1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数

2. 代码的可维护性比较低,一个出错可能所有的重载均出错

那么能不能告诉编译器一个模子,让编译器根据不同类型利用模子来生成代码呢?

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

二. 函数模板

1. 概念

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

 2. 函数模版格式

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

返回值类型  函数名 (参数列表) {}

如以下代码所示

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

typename 是用来定义模板参数关键字也可以使用class(尤其注意:不要用struct替代class)

如下

template <class T>
void Swap(T& xxxx, T& yyyy)
{
	T temp = xxxx;
	xxxx = yyyy;
	yyyy = temp;
}
3. 函数模版的原理

函数模版是一个蓝图,它本身不是函数,是编译器根据使用方式产生特定具体类型函数的模具。所以其实模版就是将原本应该我们做的重复的事情交给了编译器。

 在编译器编译阶段,对于模版函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用

假如主函数代码如下

int  main()
{
	int x1 = 1;
	int y1 = 32;
	Swap(x1, y1);

	return 0;
}

int 类型推演

处理代码为

void Swap(int& xxxx, int& yyyy)
{
	int temp = xxxx;
	xxxx = yyyy;
	yyyy = temp;
}

假如主函数内容如下

int  main()
{
	char x2 = 11;
	char y2 = 33;
	Swap(x2, y2);

	return 0;
}

char类型推演

处理代码为

void Swap(char& xxxx, char& yyyy)
{
	char temp = xxxx;
	xxxx = yyyy;
	yyyy = temp;
}

当用 double 类型使用函数模版时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码

4. 模版函数的实例化

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

(1). 隐式实例化

让编译器根据实参推演模板参数的实际类型

#include<iostream>
using namespace std;

template <typename T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 11;
	double d1 = 11.11, d2 = 7.007;

	Add(a1,d1 );
	Add(a2, d2);
return 0;
}

上述代码不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型

通过实参a1将T推演为int ,通过实参d1将T推演为double类型,但模版参数列表中只有一个T,编译器无法确定此处到底该将T确定为int或double类型而报错 

注意:在模版中,编译器一般不会进行类型转换操作,因为一旦转换出问题,编译器就需要背黑锅

此时有两种解决方法:1. 用户手动强制类型转换 2.  使用显式实例化

Add(a1,(int)d1);
Add((double)a2,d2);
(2.) 显式实例化

在函数名后的<>中指定模版参数的实际类型

int main()
{
	int a1 = 10, a2 = 11;
	double d1 = 11.11, d2 = 7.007;

	Add<int>(a1,d1 );
	Add<double>(a2, d2);
    return 0;
}

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

5. 模版参数的匹配原则

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

如以下代码

#include<iostream>
using namespace std;
template <typename T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int Add(const int& x, const int& y)
{
	return (x + y) * 10;
}

int main()
{
	int a1 = 10, a2 = 11;
	double d1 = 11.11, d2 = 7.007;

	cout << Add(1, 2) << endl;
	cout << Add<int>(2, 2) << endl;
}

其结果为

 我们发现,与若与非模板函数匹配,编译器不需要特化

若显式实例化则调用编译器特化的Add版本

对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板中产生一个实例如果模板可以产生一个具有更好匹配的函数,那么将选择模板

#include<iostream>
using namespace std;

int Add(const int& x, const int& y)
{
	return (x + y) * 10;
}
template <typename T1,class T2>
T2 Add(const T1& left, const T2& right)
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 11;
	double d1 = 11.11, d2 = 7.007;

	cout << Add(1, 2) << endl;
	cout << Add(1, 2.1) << endl;
    return 0;
}

模板函数不允许自动类型转换,但普通函数可以进行自动类型转换 

template <typename T1>
T1 Add(const T1& left, const T1& right)
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 11;
	double d1 = 11.11, d2 = 7.007;
	char c1 = 'a';
	Add(1, 2.1);

	Add(d1, c1);
}

第一个Add函数 2.1不会自动转换为int类型,1也不会自动转换为double(或float)

第二个同理 d1不会自动转化为char .......

而普通函数可以

#include<iostream>
using namespace std;

int Add(const int& x, const int& y)
{
	return (x + y) * 10;
}

int main()
{
	float a=10.11;
	int a1 = 10, a2 = 11;
	double d1 = 11.11, d2 = 7.007;
	char c1 = 'a';
	Add(1, 2.1);

	Add(d1, c1);
}

 第一个Add函数2.1 自动进行了类型转换变为int类型

第二个Add函数的d1 自动类型转换变为了int类型,c1也转化为了int类型

三. 类模板

1. 类模板的定义格式

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

 如下代码

#include<iostream>
#include<cstring>
using namespace std;

template <typename T>
class Stack
{
public:
	Stack(size_t capacity = 4)
		:_arr(new T[capacity])
		,_capacity(capacity)
		,_size(0)
	{

	}

	void Push(const T& data)
	{
		if (_size == _capacity)
		{
			T* tmp = new T [_capacity*2];
			memcpy(tmp, _arr, sizeof(T)*_size);
			delete[] _arr;

			_arr = tmp;
			_capacity *= 2;
		}
		_arr[_size++] = data;
	}
	T Top()
	{
		return _arr[_size-1];
	}
	~Stack()
	{
		delete[] _arr;
		_arr = nullptr; 
		_size = _capacity = 0;
	}
private:
	T* _arr;
	size_t _capacity;
	size_t _size;
};

而类的成员函数要在类外实现时需要注意

再写一个模板,因为类上面的模版只针对类内

假如 将Push类内声明,类外定义,代码如下

class Stack
{
public:
	Stack(size_t capacity = 4)
		:_arr(new T[capacity])
		,_capacity(capacity)
		,_size(0)
	{

	}
	void Push(const T& data);
	T Top()
	{
		return _arr[_size-1];
	}
	~Stack()
	{
		delete[] _arr;
		_arr = nullptr; 
		_size = _capacity = 0;
	}
private:
	T* _arr;
	size_t _capacity;
	size_t _size;
};

template <typename T>
void Stack<T>::Push(const T& data)
{
	if (_size == _capacity)
	{
		T* tmp = new T[_capacity * 2];
		memcpy(tmp, _arr, sizeof(T) * _size);
		delete[] _arr;

		_arr = tmp;
		_capacity *= 2;
	}
	_arr[_size++] = data;
}
int main()
{
	Stack<int> s1;
	s1.Push(1);
	cout << s1.Top() << endl;
	s1.Push(5);
	cout << s1.Top() << endl;
	s1.Push(12);
	cout << s1.Top() << endl;
	s1.Push(11);
	cout << s1.Top() << endl;
	s1.Push(2);
	cout << s1.Top() << endl;
	s1.Push(3);
	cout << s1.Top() << endl;

	return 0;
}

下面的模板参数 也可以不叫T ,这两个T实际上就是代号,为什么是Stack<T>:: ,可以看下一条模板的实例化

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

 2. 类模板的实例化

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

	
//其中Stack是类名,Stack<int>、Stack<char>、Stack<double>才是类型
    Stack<int> s1;
	Stack<double> s2;
	Stack<char> s3;

四. 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)采用,可 移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。

3. STL的六大组件

 


这篇文章就写到这里啦,喜欢点一下赞吧~~

(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤

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

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

相关文章

netty入门-7 ByteBuf

文章目录 前言ByteBuf结构池化与非池化创建&#xff08;直接内存/堆内存&#xff09;写入和读取释放零拷贝&#xff0c;slice&#xff0c;duplicate&#xff0c; copy&#xff0c;Composite 结语 前言 ByteBuf这部分视频讲的更为详细。 主要参考视频。 ByteBuf 结构 首先这…

C# Task.WaitAll 的用法

目录 简介 1.WaitAll(Task[], Int32, CancellationToken) 2.WaitAll(Task[]) 3.WaitAll(Task[], Int32) 4.WaitAll(Task[], CancellationToken) 5.WaitAll(Task[], TimeSpan) 结束 简介 Task.WaitAll 是 C# 中用于并行编程的一个的方法&#xff0c;它属于 System.Threa…

开始尝试从0写一个项目--后端(三)

器材管理 和员工管理基本一致&#xff0c;就不赘述&#xff0c;展示代码为主 新增器材 表设计&#xff1a; 字段名 数据类型 说明 备注 id bigint 主键 自增 name varchar(32) 器材名字 img varchar(255) 图片 number BIGINT 器材数量 comment VARC…

Elasticsearch 使用误区之三——分片设置不合理

Elasticsearch 是一个强大的搜索和分析引擎&#xff0c;它通过将数据分散到多个节点的分片中来进行分布式处理。 本文将探讨分片大小和策略的概念&#xff0c;以优化 Elasticsearch 的性能并防止过度分片或分片过大等问题。 先看个分片设置不合理的真实企业案例&#xff1a; 10…

陶晶驰串口屏使用记录与教程

首先把串口屏想象成和正点原子usmart调试程序一样的程序&#xff0c;串口屏主芯片有些是GD32 STM32都是主流单片机&#xff0c;里面下载了一些固件形成了现在的操作系统 其实我更喜欢把他们&#xff08;usmart&#xff0c;串口屏主程序&#xff0c;micropython&#xff0c;at指…

uniapp vue3 使用画布分享或者收藏功能

使用HBuilder X 开发小程序&#xff0c;大多数的画布插件很多都是vue2的写法&#xff0c;vue3的很少 我自己也试了很多个插件&#xff0c;但是有一些还是有问题&#xff0c;不好用 海报画板 - DCloud 插件市场 先将插件导入项目中 自己项目亲自用过&#xff0c;功能基本是完善…

GraphRAG:基于实体的本地搜索方法:知识图谱与非结构化数据的融合

GraphRAG&#xff1a;基于实体的本地搜索方法:知识图谱与非结构化数据的融合 在自然语言处理和信息检索领域,如何有效地结合结构化知识和非结构化文本数据一直是一个重要的研究方向。本文介绍一种基于实体的本地搜索方法,该方法巧妙地融合了知识图谱中的结构化数据和输入文档中…

优化冗余代码:提升前端项目开发效率的实用方法

目录 前言代码复用与组件化模块化开发与代码分割工具辅助与自动化结束语 前言 在前端开发中&#xff0c;我们常常会遇到代码冗余的问题&#xff0c;这不仅增加了代码量&#xff0c;还影响了项目的可维护性和开发效率。还有就是有时候会接到紧急业务需求&#xff0c;要求立马完…

打造一篇完美的【数学建模竞赛论文】:从准备到撰写的全面指南

目录 一、赛前准备 1.1 报名与纪律要求 1.2 MD5码上传 1.3 竞赛准备 1.4 时间分配 二、论文格式规范 2.1 摘要 2.2 参考文献 2.3 排版要求 三、建模过程与方法 3.1 问题分析与模型假设 3.2 模型构建与求解 3.3 结果分析与检验 四、论文撰写技巧 4.1 论文结构 4…

Redisson中分布式锁继承体系

直接上图 画了好久 关于非公平锁和公平锁中差异化函数如tryLockInnerAsyc 和unsubscribe还没有时间进行探索&#xff0c;这应该是公平锁和非公平锁之间的差异所在。 说一说Redisson中的类之间关系设计 参考抽象类实现接口_一个抽象之类 如果要实现某个接口怎么办-CSDN博客 众…

电脑文件误删除如何恢复?数据恢复第一步是什么?这五点要第一时间处理!

电脑文件误删除如何恢复&#xff1f;数据删除恢复的第一时间要做什么&#xff0c;你知道吗&#xff1f; 在使用电脑的过程中&#xff0c;误删除重要文件的情况时有发生。面对这种情况&#xff0c;不必过于慌张&#xff0c;因为有多种方法可以帮助你恢复误删除的文件。以下是恢复…

金字塔监督在人脸反欺骗中的应用

介绍 论文地址&#xff1a;https://arxiv.org/pdf/2011.12032.pdf 近年来&#xff0c;人脸识别技术越来越普及。在智能手机解锁和进出机场时&#xff0c;理所当然地会用到它。人脸识别也有望被用于管理今年奥运会的相关人员。但与此同时&#xff0c;人们对人脸欺骗的关注度也…

醒醒,别睡了...讲《数据分析pandas库》了—/—<3>

直接上知识点 一、 1、新建数据框时建立索引 所有的数据框默认都已经使用从 0 开始的自然数索引&#xff0c;因此这里的"建立”索引指的是自定 df pd.DataFrame( {varl : 1.0, var2 :[1,2,3,4], var3 :[test,python,test,hello] , var4 : cons} , index [0,1,2,3]) …

【ESP32 IDF SPI硬件驱动W25Q64】

目录 SPISPI介绍idf配置初始化配置通信 驱动代码 SPI SPI介绍 详细SPI介绍内容参考我之前写的内容【ESP32 IDF 软件模拟SPI驱动 W25Q64存储与读取数组】 idf配置 初始化配置 spi_bus_initialize() 参数1 &#xff1a;spi几&#xff0c;例如spi2,spi3 参数2&#xff1a;…

MySQL体系结构与查询执行流程详解

MySQL 体系结构与查询执行过程详解 MySQL 是一个采用单进程多线程架构模式的关系型数据库管理系统。本文将详细介绍 MySQL 的体系结构及其查询语句的执行过程,并探讨性能优化的关键点。 MySQL 体系结构 MySQL 的架构为 Client-Server 架构。总体上,我们可以将 MySQL 的体系…

python—pandas基础(2)

文章目录 列操作修改变量列筛选变量列使用.loc[]&#xff08;基于标签)使用.iloc[]&#xff08;基于整数位置&#xff09;使用.filter()方法 删除变量列添加变量列 变量类型的转换Pandas 支持的数据类型在不同数据类型间转换 建立索引新建数据框时建立索引读入数据时建立索引指…

如何在宝塔面板给域名配置 SSL 证书

首先需要有证书 这里以阿里云为例 1. 首先进入到 SSL 证书管理控制台 选择个人测试证书&#xff0c;并点击购买 免费的可以使用三个月。 购买完成之后回到控制台。 点击创建证书&#xff0c;将标红的地方填写&#xff0c;其他默认就好。 然后提交审核就行。 这里需要对域名…

JS逆向高级爬虫

JS逆向高级爬虫 JS逆向的目的是通过运行本地JS的文件或者代码,以实现脱离他的网站和浏览器,并且还能拿到和浏览器加密一样的效果。 10.1、编码算法 【1】摘要算法&#xff1a;一切从MD5开始 MD5是一个非常常见的摘要(hash)逻辑. 其特点就是小巧. 速度快. 极难被破解. 所以,…

图像生成中图像质量评估指标—FID介绍

文章目录 1. 背景介绍2. 实际应用3. 总结和讨论 1. 背景介绍 Frchet Inception Distance&#xff08;\textbf{FID}&#xff09;是一种衡量生成模型性能的指标&#xff0c;它基于Inception网络提取的特征来计算模型生成的图像与真实图像集合之间的距离。 FID利用了Inception模…

repo中的default.xml文件project name为什么一样?

文章目录 default.xml文件介绍为什么 name 是一样的&#xff0c;path 不一样&#xff1f;总结 default.xml文件介绍 在 repo 工具的 default.xml 文件中&#xff0c;定义了多个 project 元素&#xff0c;每个元素都代表一个 Git 仓库。 XML 定义了多个不同的 project 元素&…