百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

C++ 标准模板库STL

liebian365 2024-11-18 14:21 18 浏览 0 评论

在分析和处理Tensorflow目标检测结果数据时,用到了STL相关的内容,如

//获取目标检测结果 类别,概率,位置

std::vector<std::tuple<string, float, Rect>> getObjDetectResult(Mat src);

//处理目标检测结果,每个类别输出概率最高的那个检测结果(筛选和过滤)

std::map<string, tuple<float, Rect>> processObjDetectResult(std::vector<std::tuple<string, float, Rect>> vecResult,std::set<string>& classobj, std::set<string>& classledobj);

这里将C++ STL 做个总结,以备后续查看参考。本文主要内容参考下图,


1.STL 是什么?

STL,英文全称 standard template library,中文可译为标准模板库或者泛型库,其包含有大量的模板类和模板函数,是 C++ 提供的一个基础模板的集合,用于完成诸如输入/输出、数学计算等功能。

如今 STL 已完全被内置到支持 C++ 的编译器中,无需额外安装。

STL 就位于各个 C++ 的头文件中,即它并非以二进制代码的形式提供,而是以源代码的形式提供。

从根本上说,STL 是一些容器、算法和其他一些组件的集合,所有容器和算法都是总结了几十年来算法和数据结构的研究成果,汇集了许多计算机专家学者经验的基础上实现的,因此可以说,STL 基本上达到了各种存储方法和相关算法的高度优化。

注意,这里提到的容器,本质上就是封装有数据结构的模板类,例如 list、vector、set、map 。

2.STL能干什么?

为了让读者清楚地了解 STL 是什么,使用 STL 编程有哪些优势,这里举一个使用 STL 的例子。

以 C++ 定义数组的操作为例,在 C++ 中如果定义一个数组,可以采用如下方式:

int a[n];

这种定义数组的方法需要事先确定好数组的长度,即 n 必须为常量,这意味着,如果在实际应用中无法确定数组长度,则一般会将数组长度设为可能的最大值,但这极有可能导致存储空间的浪费。所以除此之外,还可以采用在堆空间中动态申请内存的方法,此时长度可以是变量:

int *p = new int[n];

这种定义方式可根据变量 n 动态申请内存,不会出现存储空间浪费的问题。但是,如果程序执行过程中出现空间不足的情况时,则需要加大存储空间,此时需要进行如下操作:

1)新申请一个较大的内存空间,即执行int * temp = new int[m];

2)将原内存空间的数据全部复制到新申请的内存空间中,即执行memecpy(temp, p, sizeof(int)*n);

3)将原来的堆空间释放,即执行delete [] p; p = temp;

而完成相同的操作,如果采用 STL 标准库,则会简单很多,因为大多数操作细节将不需要程序员关心。下面是使用向量模板类 vector 实现以上功能的示例:

void  testVector()
{
	//定义 a 数组,当前数组长度为 0,但和普通数组不同的是,
	//此数组 a 可以根据存储数据的数量自动变长。
	vector <int> a; 
	//向数组 a 中添加 10 个元素
	for (int i = 0; i < 10; i++)
	{
		a.push_back(i);
	}		
	//for循环执行完成后,a有0到9 共10个元素

	//手动调整数组 a 的大小
	a.resize(100);//调整a的size为100,后面90个元素 默认补0
	a[90] = 100;//第90个元素 赋值
	//还可以直接删除数组 a 中所有的元素,此时 a 的长度变为 0
	a.clear();//清空a,没有元素了
	//重新调整 a 的大小为 20,并存储 20 个 -1 元素。
	a.resize(20, -1);
}

通过上面这个简单的例子,STL的优势很明显,简单,明了,灵活。。。

3.STL 组成(6大组件+13个头文件)

通常认为,STL 是由容器、算法、迭代器、函数对象、适配器、内存分配器这 6 部分构成,其中后面 4 部分是为前 2 部分服务的,它们各自的含义如表 1 所示。

另外,在惠普实验室最初发行的版本中,STL 被组织成 48 个头文件;但在 C++ 标准中,它们被重新组织为 13 个头文件,如表 2 所示。

4.STL 容器

4.1 STL 容器是什么?

简单的理解容器,它就是一些模板类的集合,但和普通模板类不同的是,容器中封装的是组织数据的方法(也就是数据结构)。

STL 提供有 3 类标准容器,分别是序列容器、排序容器和哈希容器,其中后两类容器有时也统称为关联容器。它们各自的含义如表 1 所示。

4.2 STL 序列式容器

序列式容器包括 array、vector、list、deque 和 forward_list,操作方法差不多,我们重点看vector和list。

4.2.1 vector

最常见的,参考前面的示例。

4.2.2 list

示例代码如下,

void testList()
{
		//创建空的 list 容器
		std::list<double> values;
		//向容器中添加元素
		values.push_back(3.1);
		values.push_back(2.2);
		values.push_back(2.9);
		//3
		cout << "values size:" << values.size() << endl;
		//对容器中的元素进行排序
		values.sort();
		//使用迭代器输出list容器中的元素
		for (std::list<double>::iterator it = values.begin(); it != values.end(); ++it) {
			std::cout << *it << " ";
		}	
		//输出结果2.2 2.9 3.1
}

其他容器 如 序列容器(STL array), 双端队列容器(deque)等使用方法打通小异,主要是调用一些容器提供的方法来操作访问容器的元素。具体的方法可参考下表,

4.3 STL 关联式容器

C++ STL关联式容器是什么

通过学习所有的序列式容器不难发现,无论是哪种序列式容器,其存储的都是 C++ 基本数据类型(诸如 int、double、float、string 等)或使用结构体自定义类型的元素。例如,如下是一个存储 int 类型元素的 vector 容器:

std::vector<int> primes {2, 3, 5, 7, 11, 13, 17, 19};

关联式容器则大不一样,此类容器在存储元素值的同时,还会为各元素额外再配备一个值(又称为“键”,其本质也是一个 C++ 基础数据类型或自定义类型的元素),它的功能是在使用关联式容器的过程中,如果已知目标元素的键的值,则直接通过该键就可以找到目标元素,而无需再通过遍历整个容器的方式。

弃用序列式容器,转而选用关联式容器存储元素,往往就是看中了关联式容器可以快速查找、读取或者删除所存储的元素,同时该类型容器插入元素的效率也比序列式容器高。

也就是说,使用关联式容器存储的元素,都是一个一个的“键值对”( <key,value> ),这是和序列式容器最大的不同。除此之外,序列式容器中存储的元素默认都是未经过排序的,而使用关联式容器存储的元素,默认会根据各元素的键值的大小做升序排序。先介绍了一下键值对,他们是关联容器的基本元素。

4.3.1 pair

考虑到“键值对”并不是普通类型数据,C++ STL 标准库提供了 pair 类模板,其专门用来将 2 个普通元素 first 和 second(可以是 C++ 基本数据类型、结构体、类自定的类型)创建成一个新元素<first, second>。通过其构成的元素格式不难看出,使用 pair 类模板来创建“键值对”形式的元素,再合适不过。

注意,pair 类模板定义在<utility>头文件中,所以在使用该类模板之前,需引入此头文件。

void testPair()
{
	// 调用构造函数 1,也就是默认构造函数
	pair <string, double> pair1;
	// 调用第 2 种构造函数
	pair <string, string> pair2("STL教程", "http://c.biancheng.net/stl/");
	// 调用拷贝构造函数
	pair <string, string> pair3(pair2);
	//调用移动构造函数
	pair <string, string> pair4(make_pair("C++教程", "http://c.biancheng.net/cplus/"));
	// 调用第 5 种构造函数
	pair <string, string> pair5(string("Python教程"), string("http://c.biancheng.net/python/"));

	cout << "pair1: " << pair1.first << " " << pair1.second << endl;
	cout << "pair2: " << pair2.first << " " << pair2.second << endl;
	cout << "pair3: " << pair3.first << " " << pair3.second << endl;
	cout << "pair4: " << pair4.first << " " << pair4.second << endl;
	cout << "pair5: " << pair5.first << " " << pair5.second << endl;

	//修改pair 键值对
	pair2.first = "Java教程";
	pair2.second = "http://c.biancheng.net/java/";
	cout << "修改后的pair2: " << pair2.first << " " << pair2.second << endl;

	pair4 = make_pair("C++教程2", "http://c.biancheng.net/cplus/2");
	cout << "pair4: " << pair4.first << " " << pair4.second << endl;

	//////////////比较
	pair <string, int> pair11("STL教程", 20);
	pair <string, int> pair22("C++教程", 20);
	pair <string, int> pair33("C++教程", 30);
	//pair1和pair2的key不同,value相同
	if (pair11 != pair22) {
		cout << "pair11 != pair22" << endl;
	}
	//pair22和pair33的key相同,value不同
	if (pair22 != pair33) {
		cout << "pair22 != pair33" << endl;
	}

	//交换 pair11 和 pair22 的键值对
	pair11.swap(pair22);
	cout << "pair11: " << pair11.first << " " << pair11.second << endl;
	cout << "pair22: " << pair22.first << " " << pair22.second << endl;

}

4.3.2 Tuple

pair 只能有2个元素,但tuple可以拥有任意数量的元素。来看tuple的使用方法。

void testTuple()
{
	tuple<string, int, int, complex<double>> t_tmp;
	tuple<int, float, string> t_tmp1(41, 6.3, "nico");
	//访问tuple 的第i个元素
	cout << get<0>(t_tmp1) << " ";
	cout << get<1>(t_tmp1) << " ";
	cout << get<2>(t_tmp1) << endl;

	auto t_tmp2 = make_tuple(22, 44, "nico");
	//更新 t_tmp1 的第一个元素 
	get<1>(t_tmp1) = get<1>(t_tmp2);

	//比较
	if (t_tmp1 < t_tmp2) {
		t_tmp1 = t_tmp2;
	}
	/////tie()是建立一个由引用构成的tuple。
	tuple<int, float, string> t_tmp11(1, 2.3, "yoyo");
	int i2=-1;
	float f2=0.01;
	string s2="";
	cout << "i2=" << i2 << " f2=" << f2 << " s2=" << s2 << endl;
	tie(i2, f2, s2) = t_tmp11;
	cout << "i2=" << i2 << " f2=" << f2 << " s2=" << s2 << endl;

	cout << "tuple_size" << endl;
	//tuple_size:可获取元素个数
	tuple<int, float, string> t2(1, 3.2, "aaa");
	cout << "t2 size=" << tuple_size<decltype(t2)>::value << endl;
	//tuple_element<idx, tupletype>::type:可获取第idx个元素的类型
	tuple_element<0, decltype(t2)>::type first = 1;
	cout << "t2's first element=" << first << endl;
	//tuple_cat:可以将多个tuple串成一个tuple
	auto t3 = tuple_cat(t_tmp11, t2);
	cout << "t3: ";
	cout << get<0>(t3) << " ";
	cout << get<1>(t3) << " ";
	cout << get<2>(t3) << " ";
	cout << get<3>(t3) << " ";
	cout << get<4>(t3) << " ";
	cout << get<5>(t3) << " " << endl;
}

C++ STL 标准库提供了 4 种关联式容器,分别为 map、set、multimap、multiset,其各自的特点如表 1 所示。

4.3.3 map/multimap

示例代码,map声明,添加元素,修改元素,访问元素的用法

void testMap()
{
	//创建一个空的 map 关联式容器,该容器中存储的键值对,其中键为 string 字符串,值也为 string 字符串类型
	map<string, string> mymap;
	//向 mymap 容器中添加数据
	mymap["http://c.biancheng.net/c/"] = "C语言教程";
	mymap["http://c.biancheng.net/python/"] = "Python教程";
	mymap["http://c.biancheng.net/java/"] = "Java教程";

	//使用 map 容器的迭代器,遍历 mymap 容器,并输出其中存储的各个键值对
	for (map<string, string>::iterator it = mymap.begin(); it != mymap.end(); ++it) {
		//输出各个元素中的键和值
		cout << it->first << " => " << it->second << '\n';
	}
	//修改元素
	mymap["http://c.biancheng.net/c/"] = "C语言教程2";
	//根据Key值 来取Value
	string str1 = mymap["http://c.biancheng.net/c/"];
	cout << str1 << endl;
	//下标访问方式
	string str2 = mymap.at("http://c.biancheng.net/c/");
	cout << str2 << endl;

	////////////////////////////////另外一种初始化方式
	//创建并初始化 map 容器
	std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
											 {"C语言教程","http://c.biancheng.net/c/"},
											 {"Java教程","http://c.biancheng.net/java/"} };
	map< std::string, std::string >::iterator myIter = myMap.find("C语言教程");
	cout << myIter->first << " " << myIter->second << endl;
	for (auto iter = myMap.begin(); iter != myMap.end(); ++iter) {
		//调用 string 类的 compare() 方法,找到一个键和指定字符串相同的键值对
		if (!iter->first.compare("C语言教程")) {
			cout << iter->first << " " << iter->second << endl;
		}
	}
}

4.3.4 set/multiset

和 map、multimap 容器不同,使用 set 容器存储的各个键值对,要求键 key 和值 value 必须相等。基于 set 容器的这种特性,当使用 set 容器存储键值对时,只需要为其提供各键值对中的 value 值(也就是 key 的值)即可。

通过前面的学习我们知道,map、multimap 容器都会自行根据键的大小对存储的键值对进行排序,set 容器也会如此,只不过 set 容器中各键值对的键 key 和值 value 是相等的,根据 key 排序,也就等价为根据 value 排序。

另外,使用 set 容器存储的各个元素的值必须各不相同。更重要的是,从语法上讲 set 容器并没有强制对存储元素的类型做 const 修饰,即 set 容器中存储的元素的值是可以修改的。但是,C++ 标准为了防止用户修改容器中元素的值,对所有可能会实现此操作的行为做了限制,使得在正常情况下,用户是无法做到修改 set 容器中元素的值的。

void testSet()
{
	//创建空set容器
	std::set<std::string> myset;
	//空set容器不存储任何元素
	cout << "1、myset size = " << myset.size() << endl;
	//向myset容器中插入新元素
	myset.insert("http://c.biancheng.net/java/");
	myset.insert("http://c.biancheng.net/stl/");
	myset.insert("http://c.biancheng.net/python/");
	cout << "2、myset size = " << myset.size() << endl;
	//利用双向迭代器,遍历myset
	for (auto iter = myset.begin(); iter != myset.end(); ++iter) {
		cout << *iter << endl;
	}
	////////////
	string str = *myset.find("http://c.biancheng.net/java/");
	cout <<"查找"<< str << endl;
}

小结:

本文介绍了C++ STL 相关的一些知识点,重点是常用的 序列容器,键值对(pair,tuple)和关联式容器的基本操作和数据读取,添加,删除等方法。

序列式容器包括 array、vector、list、deque 和 forward_list

键值对,pair和tuple

关联式容器,分别为 map、set、multimap、multiset

相关推荐

4万多吨豪华游轮遇险 竟是因为这个原因……

(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...

“菜鸟黑客”必用兵器之“渗透测试篇二”

"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...

科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白

作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...

麦子陪你做作业(二):KEGG通路数据库的正确打开姿势

作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...

知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势

智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...

每日新闻播报(September 14)_每日新闻播报英文

AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...

香港新巴城巴开放实时到站数据 供科技界研发使用

中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...

5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper

本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...

Qt动画效果展示_qt显示图片

今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...

如何从0到1设计实现一门自己的脚本语言

作者:dong...

三年级语文上册 仿写句子 需要的直接下载打印吧

描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...

C++|那些一看就很简洁、优雅、经典的小代码段

目录0等概率随机洗牌:1大小写转换2字符串复制...

二年级上册语文必考句子仿写,家长打印,孩子照着练

二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...

一年级语文上 句子专项练习(可打印)

...

亲自上阵!C++ 大佬深度“剧透”:C++26 将如何在代码生成上对抗 Rust?

...

取消回复欢迎 发表评论: