C++(四)——C++标准模板库

2023-11-20


STL(standard template library, 标准模板库)是C++标准库的核心,它深刻影响了标准库的整体结构。STL是一个泛型程序库,提供一些列软件方案,利用先进、高效的算法来管理数据。程序员无须了解STL的原理,便可享受数据结构和算法领域中的这一革新成果。

1. STL组件(Component)

若干精心勾画的组件共同合作,构筑起STL的基础。这些组件中最关键的是容器、迭代器和算法。

  • 容器(Container),用来管理某类对象的集合。每一种容器都有其优点和缺点,所以,为了应付不同的需求,STL准备了不同的容器类型。容器可以使array或Linked list,或者每个元素有一个特别的key。
  • 迭代器(Iterator),用来在一个对象集合内遍历元素。迭代器的主要好处是,为所有各式各样的容器提供了一组很小的共通接口。例如其中一个操作是行进至集合内的下一个元素。至于如何做到当然取决于集合的内部结构。不论这个集合是array或tree或hash table,此一行进动作都能成功,因为每一种容器都提供了自己的迭代器,而这些迭代器了解容器的内部结构,知道该做些什么。
  • 算法(Algorithm),用来处理集合内的元素。它们可以出于不同的目的而查找、排序、修改、使用元素。通过迭代器的协助,我们只需撰写一次算法,就可以将它应用于任意容器,因为所有容器的迭代器都提供一致的接口。

2. 容器(Container)

容器用来管理一大群元素。为了适应不同需要,STL提供了不同的容器。总的来说,容器可分为三大类:

  1. 序列式容器(Sequence container),这是一种有序(ordered)集合,其内每个元素均有确凿的位置——取决于插入时机和地点,与元素值无关。STL提供了5个定义好的序列式容器:array、vector、deque、list和forward_list。
  2. 关联式容器(Associative container),这是一种已排序(sorted)集合,元素位置取决于其value(或key——如果元素是个键值对)和给定的某个排序准则。STL提供了4个关联式容器:set、multiset、map和multimap。
  3. 无序容器(Unordered container),这是一种无序集合(unordered collection),其内每个元素的位置无关紧要,唯一重要的是某特定元素是否位于此集合内。STL内含4个预定义的无序容器:unordered_set、unordered_multiset、unordered_map和unordered_multimap。

下面各小节详细讨论各种容器类:包括容器的典型实现,及其好处和缺点。下一篇文章会谈到容器类的确切行为,描述它们共有和特有的能力,并详细分析其成员函数,并会详细讨论何时使用哪一种容器。

2.1 序列式容器(Sequence Container)

STL内部预先定义好了以下序列式容器:

  • Array(其类名为array)
  • Vector
  • Deque
  • List(单/双 链表)
    以下讨论从vector开始,因为array是TR1新引入的,进入C++标准库的时间比较短,而且它有一些特殊属性,与其他STL容器不共通。

Vector
Vector将其元素置于一个动态数组中管理。它允许随机访问。在数组尾部附加元素或移除元素都很快速,但是在数组的中段或起始段安插元素就比较费时。

以下例子针对整数类型定义了一个vector,插入6个元素,然后打印所有元素:

// stl/vector1.cpp
#include <vector>
#include <iostream>
using namespace std;

int main()
{
	vector<int> coll;   // vector container for integer elements
	// append elements with values 1 to 6
	for(int i=1; i<=6; ++i)
	{
		coll.push_back(i);
	}
	
	// print all elements followed by a space
	for(int i=0; i<coll.size(); ++i)
	{
		cout << coll[i] << ' ';
	}
	cout << endl;
}

// 程序输出: 1 2 3 4 5 6 

Deque
所谓deque,是“double-ended queue”的缩写,即双端队列。是一个动态数组,可以向两端发展,因此无论在尾部或头部插入元素都十分迅速。

以下例子声明了一个元素为浮点数的deque,并在容器头部插入1.1至6.6共6个元素,最后打印出所有元素。

// stl/deque1.cpp
#include <deque>
#include <iostream>
using namespace std;

int main()
{
	deque<float> coll; // deque container for floating-point elements

	// insert elements from 1.1 to 6.6 each at the front
	for(int i=1; i<=6; ++i)
	{
		coll.push_front(i*1.1); // insert at the front
	}

	// print all elements followed by a space
	for(int i=0; i<coll.size(); ++i)
	{
		cout << coll[i] << ' ';
	}
	cout << endl;

// 程序输出如下:6.6 5.5 4.4 3.3 2.2 1.1

}

你也可以使用成员函数push_back()在deque尾端附加元素。Vector并未提供push_front(),因为其时间效率不佳。一般而言,STL容器只提供具备良好时间效率的成员函数,所谓“良好”通常意味着其复杂度为常量或对数,以免程序员调用性能很差的函数。

Array
一个array对象乃是在某个固定大小的array内管理元素。因此,只能改变元素值,不可以改变元素个数。你必须在建立时就指明其大小。

下面的例子定义出了一个array,元素是string:

// stl/array1.cpp
#include <array>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	// array container of 5 string elements:
	array<string, 5> coll = {"hello", "world" };
	
	// print each element with its index on a line
	for(int i=0; i<coll.size(); ++i)
	{
		cout << i << ": " << coll[i] << endl;
	}
}

// 整个程序输出如下:
0: hello
1: world
2: 
3: 
4: 

List
从历史角度看,我们只有一个list类。然而自C++11开始,STL竟提供了两个不同的list容器:class list<>和class forward_list<>。

list<>由双向链表实现而成。这意味着list内的每个元素都以一部分内存指示其前驱元素和后继元素。List不提供随机访问,因此如果你要访问第10个元素,你必须沿着链表依次走过前9个元素。

List的优势是:在任何位置上执行安插或删除动作都非常迅速,因为只需改变链接就好。这表示在list中段处移动元素比在vector和deque快得多。

以下例子产生一个空list,用以放置字符,然后将‘a’至‘z’的所以字符插入其中,利用循环每次打印并移除集合的第一个元素,从而打印出所有元素:

// stl/list1.cpp
#include <list>
#include <iostream>
using namespace std;

int main()
{
	list<char> coll; // list container for character elements

	// append elements from 'a' to 'z'
	for(char c='a'; c <= 'z'; ++c)
	{
		coll.push_back(c);
	}

	// print all elements:
	// -use range-based for loop
	for(auto elem : coll)
	{
		coll << elem << ' ';
	}
	cout << endl;
}

注意,elem永远是当前正被处理的元素的一个拷贝。虽然你可以改动它,但其影响只限于“针对此元素而调用的语句”,coll内部并没有任何东西被改动。如果你想改动传入的集合的元素,你必须将elem声明为一个非常量的reference:

for(auto& elem : coll)
{
	... // any modification of elem modifies the current element in coll
}

在C++11之前,打印所有元素的另一种做法(不使用迭代器)是逐一地打印而后移第一元素,直到此list中不再有任何元素:

// print all elements
// -while there are elements
// -print and remove the first element
while(! coll.empty()) {
	cout << coll.front() << ' ';
	coll.pop_front();
}
cout << endl;

Forward list
自C++11之后,C++标准库提供了另一个list容器:forward list。forward_list<>是一个由元素构成的单向链表。与list<>不同的是,每个元素有自己的一段内存,为了节省内存,它只指向下一元素。

因此,forward list原则上就是一个受限的list,不支持任何“后退移动”或“效率低下”的操作。基于这个原因,它不提供成员函数如push_back()乃至size()。

下面是forward list的一个小例子:

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

int main()
{
	// create forward-list container for some prime numbers
	forward_list<long> coll = {2, 3, 5, 7, 11, 13, 17};
	
	// resize two times
	// -note:poor performance
	coll.resize(9);
	coll.resize(10, 99);

	// print all elements
	for(auto elem : coll) 
	{
		cout << elem << ' ';
	}
	cout << endl;
2.2 关联式容器(Associative Container)

关联式容器依据特定的排序准则,自动为其元素排序。元素可以是任何类型的value,也可以是key/value对,其中农key可以是任何类型,映射至一个相关value,而value也可以是任意类型。排序准则以函数形式呈现,用来比较value,或比较key/value中的key。默认情况下所有容器都以操作符<进行比较,不过你也可以提供自己的比较函数,定义出不同的排序准则。

通常关联式容器由二叉排序树实现出来。优点是,能很快找出一个具有某特定value的元素,因为它具备对数复杂度。然而,它的一个缺点是,你不能直接改动元素的value,因为那会破坏元素的自动排序。

STL定义的关联式容器有:

  • Set 元素依据value自动排序,每个元素只能出现一次,不允许重复。
  • Multiset 和set的唯一差别是:元素可以重复。
  • Map 每个元素都是键值对(key-value pair),其中gkey是排序准则的基准。
  • Multimap 与map的唯一差别是:元素可以重复,也就是multimap允许其元素拥有相同的key。Multimap可被当做字典使用。

Set和Multiset实例
下面是一个使用multiset的例子:

#include <set>
#include <string>
#include <iostream>

using namespace std;

int main()
{
	multiset<string> cities {
		"Braunschweig", "Hanover", "Frankfurt", "New York",
		"Chicago", "Toronto", "Paris", "Frankfurt"
	};
	
	// print each element
	for(const auto & elem : cities) {
		cout << elem << " " ;
	}
	cout << endl;

	// insert additional values:
	cities.insert( {"London", "Munich", "Hanover", "Braunschweig"} );

	// print each element
	for(const auto & elem : cities) {
		cout << elem << " ";
	}
	cout << endl;
}

	// 第一次输出如下:
	Braunschweig Chicago Frankfurt Frankfurt Hanover New York Paris Toronto 

	// 第二次输出如下:
	Braunschweig Braunschweig Chicago Frankfurt Frankfurt Hanover Hanover London Munich New York Paris Toronto 
	
	// multiset允许元素重复

Map和Multimap实例
下面的例子示范了如何使用map和multimap:

#include <map>
#include <string>
#include <iostream>

using namespace std;

int main()
{
	multimap<int, string> coll;

	coll = { {5, "tagged"},
			 {2, "a"},
			 {1, "this"},
 			 {4, "of"},
			 {6, "strings"},
			 {1, "is"},
			 {3, "multimap"} };

	// print all element values
	for(auto elem : coll) {
		cout << elem.second << ' ';
	}
	cout << endl;
}

// 最终,程序输出如下:
// this is a multimap of tagged strings
2.3 无序容器(Unordered Container)

无序容器常以hash table实现出来,内部结构是一个“由linked list组成”的array。通过某个hash函数的运算,确定元素落于这个array的位置。Hash函数运算的目标是:让每个元素的落点(位置)有助于用户快速访问。
在这里插入图片描述
根据关联式容器的分类方法,STL定义出下面这些无序容器

  • Unordered set
  • Unordered multiset
  • Unordered map
  • Unordered multimap
    以上者四种容器与2.2节关联式容器的四个相比,唯一不同就是无序。

Unordered Set/Multiset实例

#include <unordered_set>
#include <string>
#include <iostream>

using namespace std;

int main()
{
	unordered_multiset<string> cities {
		"Braunschweig", "Hanover", "Frankfurt", "New York", "Chicago", "Toronto", "Paris", "Frankfurt"
	};

	// print each element
	for(const auto & elem : cities) {
		cout << elem << " ";
	}
	cout << endl;

	// insert additional values:
	cities.insert( {"London", "Munich", "Hanover", "Braunschweig"} );

	// print each element
	for(const auto & elem : cities) {
		cout << elem << " ";
	}
	cout << endl;
	
}

// 打印出来的次序可能不同于程序中所给的次序,因为其次序是不明确的。

次序究竟会不会变化,或变成怎样,取决于rehashing策略,而它在某种程度上可被程序员影像。例如你可以保留足够空间,使得直到出现一定量的元素才发生rehashing。此外,为确保你在处理所有元素的同时还可以删除元素,C++ standard保证,删除元素不会引发rehashing。但是删除之后的一次插入动作就有可能引发rehashing。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++(四)——C++标准模板库 的相关文章

随机推荐

  • Python实现二叉搜索树的删除功能

    Python实现二叉搜索树的删除功能 二叉搜索树 二叉查找树 Binary Search Tree 又称为排序二叉树 有序二叉树 二叉搜索树的实现可以参考 https blog csdn net weixin 43790276 articl
  • 标准正态分布变量的积累概率分布函数C\C++

    BS模型中用到的CDF函数实现 找到两种常见的实现方式 实现一 include
  • 图像处理——我理解的傅里叶变换

    1 傅里叶变换的理解 傅里叶变换的相关数学公式目前还没有搞懂 先不整那个东西 我们主要是研究傅里叶变换的一些思想和应用 这个思想起源于牛顿研究那个三棱镜 白光透过棱镜之后会被分解为七种颜色的光 这些光叠加又能形成白光 所以说可以把一种事物分
  • selenium自动向下滚动页面,并指定最大滑动距离

    需要selenium控制的chrome向下滑动 自动加载一些内容 核心代码是 browser execute script window scrollBy 0 300 这行可以向下滑动300个像素 需要的工具函数如下 def roll wi
  • unity实现鼠标右键控制视角

    主要实现的功能是相机跟随主角 鼠标右击移动后 相机的视角会旋转 思路 在主角里创建空的子物体 把相机绑在空物体上 通过旋转空物体来实现视角的旋转 要把相机调整到适当位置 代码如下 public float rotateSpeed 100 设
  • chatgpt赋能python:Python打包发布完整指南:从基础知识到实践操作

    Python打包发布完整指南 从基础知识到实践操作 作为一名有着十年python编程经验的工程师 我清楚地知道打包发布Python应用程序是非常重要的 它能帮助我们方便地分享和分发程序 并且能够让其他人通过使用我们的程序来提高自己的工作效率
  • 别人总结的一些git教程大全

    工作中 除了必备的基础知识 还要学会与人合作 如何将你开发的小功能整合到整个项目的大框架中 如何让你的实验性代码不影响到大框架中的代码性能 如何让你放下手中写到一半的代码去修改突然出现的bug 这些都是会出现的情况 为了应对这些情况 新入职
  • Qt QML多线程-WorkerScript的使用

    Qt QML多线程 WorkerScript的使用 在开发过程中 常常会遇到一些需要进行耗时计算的操作 如果这些操作都放在主线程中完成 就会导致UI界面被卡死 用户体验很不好 为了解决这个问题 我们可以将这些耗时计算操作放在一个单独的线程中
  • java综合(六)hibernate.current_session_context_class配置

    在前面一节 spring与Hibernate整合 事务 中 总是出现不存在激活事务的问题 结果去掉
  • 使用命令启动默认程序(例如启动系统默认浏览器打开指定网址)

    文章目录 目的 基础说明 代码示例 Golang 总结 目的 通过命令调用系统默认应用程序打开对应格式的文件是比较常用的功能 这篇文章将介绍下相关内容 基础说明 Windows windows下可以使用 start 指令来启动默认程序打开对
  • 数据结构——广度优先遍历(BFS)无向连通图

    以下是数据结构中关于广度优先遍历无向连通图的操作 编程风格参考严蔚敏版数据结构 其实深度优先遍历就是二叉树的层次遍历的推广 头文件及宏 include
  • Python----利用Threading和Queue实现多线程

    用来学习Threading Queue的组合使用 实现多线程编程 实现功能 利用 ping 来检测主机是否存活 代码如下 coding utf 8 from IPy import IP from subprocess import Pope
  • 2022年 大学生工程训练比赛[物料搬运]

    本人和团结参加了2022年大学生工程训练 简称工训赛 校赛选拔 准备了几个月的时间和花费了较多的资金 由于疫情等多种情况 很遗憾未能参加湖南省省赛 过了这么久还是写个博客记录参赛准备和调试过程 目录 一 比赛要求 二 整体思路 三 硬件模块
  • 所有OLE接口

    比较有用 记录下来供查阅 常规 函数 lUnknown 目的 控制的接口协商的对象生存期 普遍存在的任何组件 而不考虑实现 QueryInterface 公开传入的接口 函数 IEnum 目的 枚举的各种类型的列表 在许多情况下 整个 OL
  • 计算机扫描的文件保存在哪,电脑教程:文件扫描后自动保存哪里去了

    科技本身 支配宇宙的自然规律是充满魅力的 因此越来越多的人开始关注科技的相关动态 近来文件扫描后自动保存哪里去了的消息也是引起了很多人的关注 那么既然现在大家都想要知道文件扫描后自动保存哪里去了 小编今天就来给大家针对文件扫描后自动保存哪里
  • 关于 运算符号 &(与运算)、

    1 与运算 在二进制中 运算规则 0 0 0 0 1 0 1 0 0 1 1 1 类比到十进制 例如 3和4 首先化成二进制 就是 011 和 100 再进行相同位上的与运算 就是 000 最后就是0 因为是 运算符号 所以返回的是int
  • Ffmpeg视频开发教程(七)——基于ffmpeg4.0生成模拟yuv数据和模拟音频数据再合成为mp4文件

    本文主要实现使用最新版的ffmpeg生成模拟yuv数据和模拟音频数据再合成为mp4文件 重要代码都是来自官方 稳定性可靠 本文程序的环境搭建参考我的第一篇FFMPEG教程 https blog csdn net zhangamxqun ar
  • ENSP—NAT综合实验

    实验要求 1 IP地址的规划和拓扑搭建 2 IP地址的配置 AR1的代码如下 r1 interface g0 0 1 r1 GigabitEthernet0 0 1 ip add 12 1 1 1 24 r1 GigabitEthernet
  • 服务器虚拟化的优势

    1 提高硬件资源使用效率 一个服务器上可以开多个虚拟机 给不同应用使用 打破一个应用一台服务器的限制 因为某具体用户使用的时间 资源有限 多个用户 应用 就可以大大提高服务器的使用效率 减少服务器数量 可以 降低购买服务器的投资 降低服务器
  • C++(四)——C++标准模板库

    文章目录 1 STL组件 Component 2 容器 Container 2 1 序列式容器 Sequence Container 2 2 关联式容器 Associative Container 2 3 无序容器 Unordered Co