模板和STL简介

模板和STL简介

  • 一、泛型编程
    • 1、通用交换函数的实现
      • (1)代码
      • (2)总结
    • 2、泛型编程的概念
    • 3、模板的概念
  • 二、函数模板
    • 1、概念
    • 2、格式
    • 3、代码
    • 4、原理
  • 三、函数模板实例化
    • 1、概念
    • 2、隐式实例化
      • (1)概念
      • (2)代码
      • (3)运行结果
      • (4)错误代码与编译器报错
      • (5)错误代码解释
    • 3、显式实例化
      • (1)概念
      • (2)代码
      • (3)运行结果
    • 4、模板参数的匹配原则
      • (1)原则
      • (2)示例代码1
      • (3)示例代码2
  • 四、类模板
    • 1、格式
    • 2、代码
    • 3、讲解
    • 4、运行结果
    • 5、错误代码与编译器报错
  • 五、STL
    • 1、概念
    • 2、六大组件
    • 3、缺陷

一、泛型编程

1、通用交换函数的实现

(1)代码

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;
}//…………

(2)总结

  • 使用函数重载虽然可以实现一个通用的交换函数,但是使用这种方法有几个不好的地方。
  • 重载的函数只有形参的类型不同,其他的都一样,这样的代码复用率比较低,当要进行操作的对象的类型不同时,就需要用户自己添加对应类型的函数。
  • 代码的可维护性比较低,一个函数出错可能所有的重载函数均会出错。

2、泛型编程的概念

  • 泛型:不使用具体的数据类型,而是使用一种通用类型 T 来进行程序设计;这里的T 只是一个占位符,实际在 T 的位置,它的真实数据类型取决于用户的需求;占位符的替换由编译器在编译阶段完成。
  • 泛型编程:为了避免因数据类型的不同,而被迫重复编写大量相同业务逻辑的代码,因此发展了泛型及泛型编程技术;即泛型编程就是独立于任何特定类型的方式去编写代码。
  • 编写与类型无关的通用代码,是代码复用的一种手段。

3、模板的概念

建立一个通用函数或类,其类内部的类型和函数的形参类型不具体指定,用一个虚拟的类型来代表。这种通用的方式称为模板。而模板是泛型编程的基础。
在这里插入图片描述

二、函数模板

1、概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,即编译器自动依据实参的类型推演函数形参的类型,再根据推演出来的类型生成具有特定类型的函数版本。

2、格式

  • template<typename T1, typename T2,…,typename Tn>
  • 返回值类型 函数名(参数列表){}
  • typename是用来定义模板参数的关键字,它可以使用class替换,但不能使用struct代替class。虽然class和struct都有类的定义,但在此处它们的作用是不相同的。
  • typename后面的类型名字T是随便取的,取T是因为采用type的缩写,还可以写为Ty、K、V等等,但一般都是大写字母或者单词首字母大写。
  • T 代表的是一个模板类型(虚拟类型)。

3、代码

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

4、原理

函数模板就像一个蓝图,它本身并不是一个函数,而是编译器用使用方式产生特定具体类型函数的模具。所以模板就是将本来应该我们做的重复事情交给编译器去做,即还是会生成特定的函数,而不是说调用函数时直接使用模板进行操作。
在这里插入图片描述

  • 例如,当用double类型的参数使用函数模板时,编译器通过对实参类型进行推演,将T确定为double类型,然后产生一个专门处理double类型的函数,对于其他类型的参数也是如此。

三、函数模板实例化

1、概念

用不同类型的参数使用函数模板时,称为函数模板的实例化。而实例化有两种,即隐式实例化和显式实例化。

2、隐式实例化

(1)概念

在发生函数调用的时候,如果没有找到相匹配的函数存在,编译器就会寻找同名函数模板,如果进行参数类型推演可以成功,就对函数模板进行实例化。

(2)代码

template<typename T>
T Add(const T& x, const T& y)
{return x + y;
}void Test1()
{int a1 = 10, a2 = 20;double d1 = 10.5, d2 = 20.5;cout << Add(a1, a2) << endl;cout << Add(d1, d2) << endl;//cout << Add(a1, d2) << endl;cout << Add((double)a1, d2) << endl;return 0;
}

(3)运行结果

在这里插入图片描述

(4)错误代码与编译器报错

在这里插入图片描述
在这里插入图片描述

(5)错误代码解释

  • 该语句之所以不能通过编译,是因为在编译期间,当编译器执行到该实例化语句时,需要推演其形参类型,而通过实参a1将T推演为int类型,通过实参d1将T推演为double类型,但模板参数列表中只有一个T。
  • 因此,编译器无法确定此处到底该将T确定为int类型还是double类型而报错。
  • 在模板中,编译器一般不会进行类型转换的操作,因为一旦转化出问题,编译器就需要背黑锅。
  • 此时有两种处理方式,第一种为用户自己将实参进行强制类型转化,使传递的实参只有一个类型。如代码中的最后一条语句;第二种为使用显式实例化。

3、显式实例化

(1)概念

在函数名后的<>中指定模板参数的实际类型,使函数模板生成特定类型的函数,从而避免在使用模板时重复编译相同的模板代码,或者模板T只有一个类型,而传递的实参的类型却不止有一个时,即编译器推演的形参类型与模板的参数类型不匹配时,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

(2)代码

int main()
{int a = 10;double d = 10.5;cout << Add<int>(a, d) << endl;cout << Add<double>(a, d) << endl;return 0;
}

(3)运行结果

在这里插入图片描述

4、模板参数的匹配原则

(1)原则

  • 一个非模板函数和一个同名的函数模板可以同时存在,而且该函数模板还可以被实例化为这个非模板函数。
  • 对于非模板函数和同名函数模板,如果其他条件都相同,在调用时会优先调用非模板函数而不会从函数模板中实例化一个函数。如果模板函数可以产生一个参数匹配更好的函数,那么将选择函数模板去实例化一个函数。
  • 模板函数不允许自动类型转换,但普通函数却可以进行自动类型转换。

(2)示例代码1

int Add(int x, int y)
{return x + y;
}template<typename T>
T Add(const T& x, const T& y)
{return x + y;
}
int main()
{int a1 = 10, a2 = 20;cout << Add(a1, a2) << endl;cout << Add<int>(a1, a2) << endl;return 0;
}
  • 第一个Add函数调用时,将调用非模板函数,第二个因为使用显式实例化,所以它将用函数模板实例化出一个非模板函数,而这个实例化出来的函数和已存在的非模板函数一样。

(3)示例代码2

int Add(int x, int y)
{return x + y;
}template<class T1,class T2>
T1 Add(const T1& x, const T2& y)
{return x + y;
}int main()
{cout << Add(10, 20) << endl;cout << Add(10, 20.5) << endl;return 0;
}
  • 第一个Add函数调用时,它的实参与非模板函数的形参类型完全匹配,所以不需要使用函数模板去实例化一个函数。
  • 第二个Add函数调用时,模板函数可以生成更加匹配的函数,则编译器会根据实参使用模板函数去生成一个更加匹配的Add函数。

四、类模板

1、格式

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

2、代码

template<typename T>
class Stack
{
public:Stack(size_t capacity = 0){if (capacity > 0){_a = new T[capacity];_capacity = capacity;_top = 0;}}~Stack(){delete[] _a;_a = nullptr;_capacity = _top = 0;}void Push(const T& x);void Pop(){assert(_top > 0);--_top;}bool Empty(){return _top == 0;}const T& Top(){assert(_top > 0);return _a[_top - 1];}
private:T* _a = nullptr;size_t _top = 0;size_t _capacity = 0;
};template<class T>
void Stack<T>::Push(const T& x)
{if (_top == _capacity){size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;// 1、开辟新空间 2、拷贝数据 3、释放旧空间T* tmp = new T[newCapacity];if (_a){memcpy(tmp, _a, sizeof(T)*_top);delete[] _a;}_a = tmp;_capacity = newCapacity;}_a[_top] = x;++_top;
}int main()
{try{/*Stack<> st1;Stack st2;*/Stack<int> st3;Stack<char> st4;Stack<int> st5(10);st3.Push(1);st3.Push(2);st3.Push(3);st3.Push(4);st3.Push(5);/*st3.Top()++;st3.Top() *= 2;*/while (!st3.Empty()){cout << st3.Top() << " ";st3.Pop();}cout << endl;}catch (const exception& e){cout << e.what() << endl;}return 0;
}

3、讲解

  • 上方代码是一个Stack的类模板,但它不是具体的类,而是编译器根据被实例化的类型而生成具体类的模板。
  • 虽然模板不支持分离编译,即不支持声明放在.h头文件,定义放在.cpp源文件中。但当模板在同一个文件中时,是可以声明和定义分离的。即如上方代码中的Push成员函数。而当类模板中的成员函数放在类外进行定义时,需要加模板参数列表(template< class T>)。
  • 类模板都需要显示实例化,虽然实例化出来的类都用了同一个类模板,但它们并不是同一个类型。
  • 类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将欲实例化的类型放在<>中,如上方代码中的st3和st4。但需要注意的是类模板名字不是真正的类,而实例化后的结果才是真正的类,即Stack是类名,而Stack< int >才是类型。
  • 当知道对象需要存储数据的数量时,可以如上方代码中的st5一样,在()中输入欲存储数据的数量,这样的操作能避免在插入数据时进行扩容的消耗。
  • try和catch的作用是捕获异常,即当new开辟空间失败抛出异常时,会对被抛出的异常进行捕获。
  • 类模板中的Top成员函数是用const修饰的,当调用Top函数时,不能对其进行修改,如要对其进行修改则需要去掉const。

4、运行结果

在这里插入图片描述

5、错误代码与编译器报错

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五、STL

1、概念

  • STL(Standard Template Library),即标准模板库,它是一个高效的C++程序库,包含了诸多常用的基本数据结构和基本算法。为广大C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可复用性。
  • 从逻辑层次来看,在STL中体现了泛型化程序设计的思想(generic programming)。在这种思想里,大部分基本算法被抽象,被泛化,独立于与之对应的数据结构,用于以相同或相近的方式处理各种不同的情形。
  • 从实现层次来看,整个STL是以一种类型参数化(type parameterized)的方式实现的,即是基于模板(template)的。

2、六大组件

在这里插入图片描述

3、缺陷

  • STL库的更新太慢了。它的上一个靠谱的版本是C++98,中间的C++03添加了一些修订。到C++11出来时已经过了13年,STL才得到了进一步的更新。
  • STL现在都没有支持线程安全。并发环境下需要我们自己加锁,且锁的粒度比较大。
  • STL极度地追求效率,导致内部比较复杂。比如,类型萃取,迭代器萃取。
  • STL的使用会有代码膨胀的问题,比如使用vector/vector/vector这样会生成多份代码。当然,这是模板语法本身导致的。

本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
创作不易,如果觉得博主写得不错,请务必点赞、收藏加关注💕💕💕

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[unity]三角形顶点顺序

序 详见官方文档&#xff1a;Unity - Manual: Mesh data (unity3d.com) Topology&#xff1a;拓扑结构 翻译&#xff1a; 拓扑描述网格具有的面类型。 网格的拓扑定义了索引缓冲区的结构&#xff0c;索引缓冲区又描述了顶点位置如何组合成面。每种类型的拓扑都使用索引数组中…

医院安全(不良)事件上报系统源码 不良事件报告平台源码 前后端分离,支持二开

医院安全&#xff08;不良&#xff09;事件上报系统源码 系统定义&#xff1a; 规范医院安全&#xff08;不良&#xff09;事件的主动报告&#xff0c;增强风险防范意识&#xff0c;及时发现医院不良事件和安全隐患&#xff0c;将获取的医院安全信息进行分析反馈&#xff0c;…

Nacos 开源版的使用测评

文章目录 一、Nacos的使用二、Nacos和Eureka在性能、功能、控制台体验、上下游生态和社区体验的对比&#xff1a;三、记使使用Nacos中容易犯的错误四、对Nacos开源提出的一些需求 一、Nacos的使用 这里配置mysql的连接方式&#xff0c;spring.datasource.platformmysql是老版本…

Web前端开发概述

Web&#xff08;World Wide Web&#xff0c;全球广域网&#xff09;是指一种基于互联网的信息系统&#xff0c;通过超文本链接将全球各地的文档、图像、视频等资源相互关联起来&#xff0c;并通过Web浏览器进行交互浏览和访问。Web的发展使得人们可以方便地获取和共享各种类型的…

规避【虚拟专线技术】使用风险实现业务系统安全

本文为作者学习文章&#xff0c;按作者习惯写成&#xff0c;如有错误或需要追加内容请留言&#xff08;不喜勿喷&#xff09; 本文为追加文章&#xff0c;后期慢慢追加 一、技战法描述 VPN是利用Internet等公共网络基础设施&#xff0c;通过隧道加密通信技 术&#xff0c;为用…

js+vue,前端关于页面滚动让头部菜单淡入淡出实现原理

今天遇到个需求&#xff1a;我这里借用小米商城的详情页做个比喻吧。 刚开始其商品详情页是这样的&#xff1a; 当滚动到一定高度时&#xff0c;是这样的&#xff1a; 可以看到当滚动到轮播图底下的时候&#xff0c;详情页的菜单完全显现出来。 以下上代码&#xff1a; HTML…

大数据组件-Flink环境搭建

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

电脑硬盘数据恢复一般需要收费多少钱

随着电子信息时代的发展&#xff0c;个人和企业对电脑硬盘中存储的数据越发重视。然而&#xff0c;由于各种原因&#xff0c;硬盘数据丢失的情况屡见不鲜。如果您正陷入这样的困境&#xff0c;您可能会好奇恢复失去的数据需要花费多少钱。本文将为您介绍电脑硬盘数据恢复的一般…

【内网穿透】使用Nodejs搭建简单的HTTP服务器 ,并实现公网远程访问

目录 前言 1.安装Node.js环境 2.创建node.js服务 3. 访问node.js 服务 4.内网穿透 4.1 安装配置cpolar内网穿透 4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation…

hadoop3.3.1单机版环境搭建详细流程记录

1、在centos7中创建必要的目录&#xff1b; 2、上传JDK安装包到tools目录&#xff1b; 3、解压JDK到/opt/server/目录&#xff1b; tar -zxvf jdk-8u221-linux-x64.tar.gz -C /opt/server/ 4、“vim&#xff1a;未找到命令”的解决办法&#xff1b; 安装vim即可&#xff1b; …

Vue-router路由

配置路由 相当于SpringMVC的Controller 路径然后&#xff0c;跳转到对应的组件 一键生成前端项目文档

手写Mybatis:第7章-SQL执行器的定义和实现

文章目录 一、目标&#xff1a;SQL执行的定义和实现二、设计&#xff1a;SQL执行的定义和实现三、实现&#xff1a;SQL执行的定义和实现3.1 工程结构3.2 SQL执行实现的关系图3.3 执行器的定义和实现3.3.1 Executor 接口3.3.2 BaseExecutor 抽象基类3.3.3 SimpleExecutor 简单执…

机器学习---预剪枝、后剪枝(REP、CCP、PEP、)

1. 为什么要进行剪枝 横轴表示在决策树创建过程中树的结点总数&#xff0c;纵轴表示决策树的预测精度。 实线显示的是决策树 在训练集上的精度&#xff0c;虚线显示的则是在⼀个独⽴的测试集上测量出来的精度。 随着树的增⻓&#xff0c;在 训练样集上的精度是单调上升的&…

【前端demo】动态赋值CSS

文章目录 效果过程html实现oninput与onchange事件统一配置CSS 代码HTMLCSSJS 其他demo 效果 动态显示CSS样式&#xff0c;由:root统一配置。 效果预览&#xff1a;https://codepen.io/karshey/pen/BavLrwy 参考&#xff1a; Dynamic CSS Variables(codepen.io) 漫谈document…

Vue 2 nextTick方法|异步更新|事件循环

1 nextTick的用处 vm.$netTick的作用是将回调延迟到下次DOM更新周期之后执行。 它接受一个回调函数作为参数。 其实&#xff0c;在我们更新数据状态后&#xff0c;是不会立马渲染的&#xff0c;你不能即刻获取到新的DOM&#xff1a; <!DOCTYPE html> <html><…

NPM 常用命令(三)

目录 1、npm compltion 1.1 描述 2、npm config 2.1 常用命令 2.2 描述 set get list delete edit fix 2.3 配置 json global editor location long 3、npm dedupe 3.1 描述 3.2 配置 4、npm deprecate 4.1 命令使用 4.2 描述 4.3 配置 registry ot…

CentOS7 Hadoop3.3.0 安装与配置

一、安装JDK 1、创建文件夹tools和training用于存放压缩包和解压使用&#xff0c;tools存放压缩包&#xff0c;training用于解压后安装jdk和hadoop的路径。 1&#xff09;回到路径为 / 的位置 cd /2) 创建 tools 和 training mkdir toolsmkdir training3) 进入tools文件夹 …

RHCA之路---EX280(4)

RHCA之路—EX280(4) 1. 题目 Use the S2I functionality of your OpenShift instance to build an application in the rome project Use the Git repository at http://services.lab.example.com/php-helloworld for the application source Use the Docker image labeled re…

Three.js开发中遇到的常见问题总结和性能优化

关于Three.js开发中遇到的一些问题总结 1.加载外部模型文件无法在场景中显示: (1) 确保当前文件内容是否能被读取&#xff0c;在Javascript的console中查找错误&#xff0c;并确定当你调用.load()的时候&#xff0c;使用了onError回调函数来输出结果, 如果err 输出则表示当前…

一加11/Ace2/10Pro手机如何实现全局120HZ高刷-游戏超级流畅效果

已经成功root啦。安卓13目前也一样支持LSPosed框架&#xff0c;如果你对LSP框架有需求&#xff0c;也可以使 自测120HZ刷新率诞生以后&#xff0c;很多小伙伴用上了就很难回来啦&#xff0c;一加11/Ace2/10Pro/9pro手 机厂商也对新机做了很多的适配&#xff0c;让我们日常使用起…