[C++]模板与STL简介

news2024/9/28 7:25:26

🥁作者: 华丞臧
📕​​​​专栏:【C++】
各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞+收藏+关注)。如果有错误的地方,欢迎在评论区指出。

推荐一款刷题网站 👉LeetCode


文章目录

  • 模板初阶
    • 泛型编程
    • 函数模板
      • 函数模板概念
      • 函数模板格式
      • 函数模板原理
      • 函数模板实例化
      • 多个参数
      • 模板参数的匹配原则
    • 类模板
      • 类模板定义格式
      • 类模板的实例化
    • 模板的声明和定义分离
  • []的重载
  • STL初识
    • 什么是STL
    • STL版本
    • STL的六大组件
    • STL重要性
    • STL的缺陷


模板初阶

泛型编程

首先来看下面一段代码:

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++中提供了一个模具,通过给这个模具中填充不同的类型,来获得不同类型的代码。

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

在这里插入图片描述

函数模板

函数模板概念

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

函数模板格式

C++中提供了一个新的关键字template,用来定义函数模板,template后面跟一个尖括号用来定义类型,这个类型不是具体的类型而是一个模板类型(虚拟类型)。

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

//也可以使用class
template<class T1, class T2..., class Tn>
返回值类型 函数名(参数列表)
{}


//例子
template<class T>
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);

	char e = 'a', f = 'b';
	swap(e, f);

	return 0;
}

注意:typename是用来定义模板参数关键字,也可以使用class,但是切记不能使用struct代替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;
}
  1. 显式实例化:在函数名后的<>中指定模板参数的实际类型
template<class T>
T Add(const T& left, const T& right)
{
 	return left + right;
}

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

多个参数

template<class T1, class T2>
T Add(const T1& left, const T2& right)
{
 	return left + right;
}

模板参数的匹配原则

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

类模板

我们知道typedef可以给类型重命名,typedef很好的解决了代码的可维护性,但不是所谓的泛型编程,它不能满足泛型编程的要求;假设有一个栈,如果我们要在栈中存放不同类型的数据就需要用到模板。

类模板定义格式

类模板的成员函数都是模板函数。

//例子
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)
	{
		// ....
		// 扩容
		_a[_top++] = x;
	}

private:
	T* _a;
	int _top;
	int _capacity;
};

int main()
{
	// 类模板一般没有推演时机,函数模板实参传递形参,推演模板参数
	// 类模板一般显式实例化
	// 他们是同一个模板实例化出来的
	// 但是模板参数不同,他们就是不同的类型
	Stack<int> st1;

	Stack<double> st2;

	return 0;
}

类模板的实例化

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

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

模板的声明和定义分离

注意模板不支持分离编译;这涉及到编译的过程,一个程序编译需要经过预编译、编译、汇编、链接四个过程,在链接之前的三个过程(预编译、编译、汇编)都是单线进行的。如果分离编译,那么用的地方在实例化但是只有声明,没有定义;而定义的地方无法完成实例化,只有定义,定义的地方只有模板而没有实例化的对象,因此链接时定义文件.cpp的符号表中并没有对应函数的地址。
代码地址👉git仓库地址
在这里插入图片描述
解决方案:

  1. 显式实例化:缺点很明显换个类型还得显式实例化
    在这里插入图片描述
    程序运行成功:
    在这里插入图片描述
  2. 声明和定义不分离
    文件后缀为.hpp是模板文件,.hpp表示是声明和定义放在一起。
//Stack.h
#pragma once
#include <iostream>
using namespace std;

template<typename T>
class Stack
{
public:
	Stack(int capacity = 4);
	~Stack();
	void Push(const T& x);

private:
	T* _a;
	int _top;
	int _capacity;
};

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

//test.cpp
#include "Stack.h"


int main()
{
	Stack<int> st1;
	st1.Push(1);
	return 0;
}

程序运行成功。
在这里插入图片描述

[]的重载

#define N  10

using namespace std;

namespace hcz
{
	template<class T>
	class array
	{
	public:
		T& operator[](size_t i)
		{
			assert(i < N); //断言检查是否越界
			return _a[i];
		}
	private:
		T _a[N];
	};
}

int main()
{
	//int a2[10];
	//a2[20] = 0; //编译器检查不出越界访问
	//a2[10];  //对数组边界抽查

	hcz::array<int> a1;
	for (size_t i = 0; i < N; ++i)
	{
		// a1.operator[](i)= i;

		a1[i] = i;
	}

	for (size_t i = 0; i < N; ++i)
	{
		// a1.operator[](i)
		cout << a1[i] << " ";
	}
	cout << endl;

	for (size_t i = 0; i < N; ++i)
	{
		a1[i]++;
	}

	for (size_t i = 0; i < N; ++i)
	{
		cout << a1[i] << " ";
	}
	cout << endl;

	//a1[20];
	//a1[10];


	return 0;
}

重载运算符[]可以让我们像使用数组一样使用某些自定义类型,如上述代码。
编译器对于数组越界的检查是不严格的,编译器只会对数组的边界进行抽查并且只有修改了数据才会报错,这种检查是很 不安全;而自定义类型中我们无疑可以可以对数组越界进行更加严格的检查,比如加上断言。

在这里插入图片描述
从上述图片可以看到,vs对于越界访问检查不出,并且对于越界访问并且修改了数据也可能检查不出,这是因为编译器对于数组的越界检查是抽查;编译器只会对于数组边界进行抽查,只有修改数组边界上的数据编译器才可能会报错。

在这里插入图片描述可以看到在VS2019中,越界访问程序能正常运行并结束,vs编译器检查不出数组越界访问的问题。

在这里插入图片描述

STL初识

什么是STL

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

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要阅读部分源代码,主要参考的就是这个版本。

STL的六大组件

在这里插入图片描述

STL重要性

STL是C++学习中非常重要的一部分,不管在笔试中还是面试中都是非重要的;网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。

STL的缺陷

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

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

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

相关文章

AspNetCore中的日志组件

介绍 本文写作年代比较久远&#xff0c;最新日志文档请查看&#xff1a; .NET Core 和 ASP.NET Core 中的日志记录 | Microsoft Learn了解如何使用由 Microsoft Extension.Logging NuGet 包提供的日志记录框架。https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/l…

选择-C++选择分支

引言 你们喜欢吃"只因"肉? a > 喜欢 b > 还行 c > 不喜欢 请说出你的答案: 证明 真ikun 和假ikun 关键点 我现在 a 因为我是ikun 我是 唯一玩梗的CSDN技术型博主 哈哈所以夹带点私货 你们dddd(懂的都懂) 关注我,让你看到更多的C/C 的技术点和技术以外的梗…

CTF之MISC题目-简单流量

CTF系列文章 第一篇 CTF之密码学题目-classical && coding 第二篇 CTF之MISC题目-西游记 第三篇 CTF之MISC题目-简单流量 文章目录CTF系列文章前言一、题目是什么&#xff1f;二、解题步骤1.下载文件、解压2.使用wireshark3.解压flag.zip总结前言 这是一道关于网络数…

ElasticSearch-倒排索引

文章目录一、mysql数据库存在的问题1.1 模糊查询索引失效1.2 不能分词查询二、倒排索引一、mysql数据库存在的问题 1.1 模糊查询索引失效 假设要查询上图中title中包含"手机"的信息&#xff0c;那么sql语句是这样的 SELECT * FROM goods WHERE title LIKE %手机%;如…

功能更新 | 身份认证增强安全配置

在开始本文前&#xff0c;先给大家出个解谜题&#xff0c;密码在下一段文字里&#xff0c;由 9 个字组成&#xff0c;开动你的脑筋吧&#xff0c;我们在本文结尾会揭晓答案&#xff1a; 2022 年马上就要结束了&#xff0c;机遇与挑战并存的一年。昨天&#xff0c;北京郊区一些地…

企业电子招标采购系统源码及功能清单

​ ​ 一、立项管理 1、招标立项申请 功能点&#xff1a;招标类项目立项申请入口&#xff0c;用户可以保存为草稿&#xff0c;提交。 2、非招标立项申请 功能点&#xff1a;非招标立项申请入口、用户可以保存为草稿、提交。 3、采购立项列表 功能点&#xff1a;对草稿进行编…

从卖货到广告,跨境电商解锁变现新模式

一、经济全球化背景下&#xff0c;跨境电商作为外贸发展的新模式&#xff0c;可谓势头正盛。而 2022 年&#xff0c;在汇率波动、欧美通胀等不可抗逆因素的影响下&#xff0c;跨境电商大环境也面临着诸多挑战。对于消费者而言&#xff0c;全球通货膨胀持续走高&#xff0c;物价…

List、List<Object>、List<?>

List、List、List<?>ListList<Object>List<?>demoList 1、声明的List集合对其 所指向的集合对象&#xff08;就是赋值的集合对象&#xff09;的限制:无泛型限制&#xff0c;并且无视指向的集合对象的泛型&#xff0c;直接当成List处理&#xff08;泛型擦除…

软件测试难吗?应该怎样学习?

软件测试是一份不错的职业&#xff0c;现在也有许多小伙伴想要学习软件测试技术&#xff0c;成为一名软件测试员。但是零经验的小白又担心不知道软件测试好不好学&#xff0c;应该如何学习软件测试能力&#xff0c;需要做哪些培训呢。下面就给大家推荐一些学习经验与技巧&#…

【JavaEE】JVM(八股文!)

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录【JVM内存划分】【JVM类加载】【JVM垃圾回收机制GC】一&#xff09;GC是啥二&#xff09;GC回收哪部分内存三&#xff09;具体怎么回收四&#xff09;怎么找垃圾&#xff08;判定某个对象是否是垃圾&#xff09;五&a…

圆和椭圆外投影

1 圆外一点在缩放到圆上 圆方程: x2y222x^2y^2 2^2 x2y222 直线方程: ykxy kx ykx 圆外一点: A(3,3)A(3,3)A(3,3) 求点B. 方法1-解方程 圆外一点A(3,3)A(3,3)A(3,3),那么:直线k1k1k1,直线方程:yxyxyx 方程联立: x2x24x^2 x^2 4 \\ x2x24 x2y2x \sqrt{2} \\ y \sqrt…

点云 3D 分割 - SqueezeSeg(ICRA 2018)

点云 3D 分割 - SqueezeSeg&#xff08;ICRA 2018&#xff09;摘要1. 引言2. 相关工作A. 三维激光雷达点云的语义分割B. 用于3D点云的CNNC. 图像的语义分割D. 通过模拟收集数据3. 方法描述A. 点云变换B. 网络结构C. 条件随机场D. 数据收集4. 实验A. 评估指标B. 实验设置C. 实验…

2022电商行业重磅年度报告:八大年度关键词盘点

2022年终于过去&#xff0c;当网易云音乐推出“年度报告”霸屏朋友圈&#xff0c;它在试图唤起那些可能被遗忘的情绪和小心思。 这一年&#xff0c;有人悲观&#xff0c;有人积极&#xff0c;有人凭实力搭上了顺风车&#xff0c;也有人放弃了抵抗。这一年&#xff0c;作为电商人…

Mybatis学习笔记 | 动力节点老杜

目录 一、MyBatis概述 历史 MyBatis特点 ORM概述 二、Mybatis入门 1、SqlSessionFactory和SqlSession 2、核心配置文件的加载 3、mybatis事务管理机制 4、第一个mybatis程序 5、mybatis继承日志 三、mybatis增删改查 1、增加 通过Map传值 通过实体类传值 2、删除…

【uniapp】如何实现拖动文件直接上传

做uniapp项目发布H5有个后台管理&#xff0c;用户说上传文件的体验需要改进&#xff0c;那个弹出选择文件对话框然后去填文件路径选择文件上传&#xff0c;感觉操作太麻烦&#xff0c;于是就有了这么一个需求&#xff0c;需要实现拖动文件直接上传的&#xff0c;这样效率和体验…

九、动态组件与插槽

一、动态组件 1.1、什么是动态组件 动态组件指的是动态切换组件的显示与隐藏。 1.2、如何实现动态组件渲染 vue提供了一个内置的<component>组件&#xff0c;专门用来实现动态组件的渲染。示例代码如下&#xff1a; data() {// 1. 当前要渲染的组件名称return {comN…

学习笔记:混沌工程

个人理解&#xff1a; 混沌工程&#xff0c;chaos engineering&#xff0c;找出系统中的脆弱环节的方法学 混沌工程是软件测试和质量保证的一种方法&#xff0c;在黑客入侵之前或系统故障之前使用它来识别漏洞&#xff0c;由于混沌工程测试而做出的改变增加了人们对系统的信心。…

SpringBoot @SessionScope注解和Session的用法解释

参考资料 JSESSIONID是什么SessionScope 解决了不同session下如何生成不同服务实例 目录一. 前期准备二. 被SessionScope作用的类三. 使用被SessionScope作用类的Service四. 效果4.1 用Edge浏览器进入页面4.2 然后用Edge浏览器进入页面4.3 若将CacheHolder类上的SessionScope注…

nginx部署next项目访问日志去重小技巧,next项目资源不计入日志,网站日志统计去除资源请求

next项目访问日志去重小技巧需求提出具体解决方案配置代码需求提出 之前在跟SEO做网站日志分析的时候受到了一部分资源请求数据的影响&#xff0c;统计出来的ip访问次数远远大于实际值&#xff0c;从日志中或者网站控制台看到每个页面都会发送十几个请求&#xff0c;而这些请求…

Qt中使用QWebEngine加载百度离线地图,在特定地点加载个圣诞树

一、前言 2022年圣诞节到来啦&#xff0c;很高兴这次我们又能一起度过~ 遇上圣诞节&#xff0c;正好最近研究了QT中加载百度离线地图&#xff0c;用百度地图API加载个圣诞树&#xff08;主要我想要那个圣诞节特制勋章&#xff09;。 二、创意名 在百度离线地图加载个圣诞树。…