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

C++ 标准模板库STL

liebian365 2024-11-18 14:21 3 浏览 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

相关推荐

快递查询教程,批量查询物流,一键管理快递

作为商家,每天需要查询许许多多的快递单号,面对不同的快递公司,有没有简单一点的物流查询方法呢?小编的回答当然是有的,下面随小编一起来试试这个新技巧。需要哪些工具?安装一个快递批量查询高手快递单号怎么快...

一键自动查询所有快递的物流信息 支持圆通、韵达等多家快递

对于各位商家来说拥有一个好的快递软件,能够有效的提高自己的工作效率,在管理快递单号的时候都需要对单号进行表格整理,那怎么样能够快速的查询所有单号信息,并自动生成表格呢?1、其实方法很简单,我们不需要一...

快递查询单号查询,怎么查物流到哪了

输入单号怎么查快递到哪里去了呢?今天小编给大家分享一个新的技巧,它支持多家快递,一次能查询多个单号物流,还可对查询到的物流进行分析、筛选以及导出,下面一起来试试。需要哪些工具?安装一个快递批量查询高手...

3分钟查询物流,教你一键批量查询全部物流信息

很多朋友在问,如何在短时间内把单号的物流信息查询出来,查询完成后筛选已签收件、筛选未签收件,今天小编就分享一款物流查询神器,感兴趣的朋友接着往下看。第一步,运行【快递批量查询高手】在主界面中点击【添...

快递单号查询,一次性查询全部物流信息

现在各种快递的查询方式,各有各的好,各有各的劣,总的来说,还是有比较方便的。今天小编就给大家分享一个新的技巧,支持多家快递,一次能查询多个单号的物流,还能对查询到的物流进行分析、筛选以及导出,下面一起...

快递查询工具,批量查询多个快递快递单号的物流状态、签收时间

最近有朋友在问,怎么快速查询单号的物流信息呢?除了官网,还有没有更简单的方法呢?小编的回答当然是有的,下面一起来看看。需要哪些工具?安装一个快递批量查询高手多个京东的快递单号怎么快速查询?进入快递批量...

快递查询软件,自动识别查询快递单号查询方法

当你拥有多个快递单号的时候,该如何快速查询物流信息?比如单号没有快递公司时,又该如何自动识别再去查询呢?不知道如何操作的宝贝们,下面随小编一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号若干...

教你怎样查询快递查询单号并保存物流信息

商家发货,快递揽收后,一般会直接手动复制到官网上一个个查询物流,那么久而久之,就会觉得查询变得特别繁琐,今天小编给大家分享一个新的技巧,下面一起来试试。教程之前,我们来预览一下用快递批量查询高手...

简单几步骤查询所有快递物流信息

在高峰期订单量大的时候,可能需要一双手当十双手去查询快递物流,但是由于逐一去查询,效率极低,追踪困难。那么今天小编给大家分享一个新的技巧,一次能查询多个快递单号的物流,下面一起来学习一下,希望能给大家...

物流单号查询,如何查询快递信息,按最后更新时间搜索需要的单号

最近有很多朋友在问,如何通过快递单号查询物流信息,并按最后更新时间搜索出需要的单号呢?下面随小编一起来试试吧。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?运行【快递批量查询高手】...

连续保存新单号功能解析,导入单号查询并自动识别批量查快递信息

快递查询已经成为我们日常生活中不可或缺的一部分。然而,面对海量的快递单号,如何高效、准确地查询每一个快递的物流信息,成为了许多人头疼的问题。幸运的是,随着科技的进步,一款名为“快递批量查询高手”的软件...

快递查询教程,快递单号查询,筛选更新量为1的单号

最近有很多朋友在问,怎么快速查询快递单号的物流,并筛选出更新量为1的单号呢?今天小编给大家分享一个新方法,一起来试试吧。需要哪些工具?安装一个快递批量查询高手多个快递单号怎么快速查询?运行【快递批量查...

掌握批量查询快递动态的技巧,一键查找无信息记录的两种方法解析

在快节奏的商业环境中,高效的物流查询是确保业务顺畅运行的关键。作为快递查询达人,我深知时间的宝贵,因此,今天我将向大家介绍一款强大的工具——快递批量查询高手软件。这款软件能够帮助你批量查询快递动态,一...

从复杂到简单的单号查询,一键清除单号中的符号并批量查快递信息

在繁忙的商务与日常生活中,快递查询已成为不可或缺的一环。然而,面对海量的单号,逐一查询不仅耗时费力,还容易出错。现在,有了快递批量查询高手软件,一切变得简单明了。只需一键,即可搞定单号查询,一键处理单...

物流单号查询,在哪里查询快递

如果在快递单号多的情况,你还在一个个复制粘贴到官网上手动查询,是一件非常麻烦的事情。于是乎今天小编给大家分享一个新的技巧,下面一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号怎么快速查询?...

取消回复欢迎 发表评论: