今天我们来学习c++入门到入土第十二节课——不花钱
上一节课,我们讲到了文件操作,这节课我们来讲STL容器介绍和迭代器
我们先来解答一下问题:
1.没有问题
您如有问题的话,可以回复我哦!
我会取前三个问题回答哦
STL容器分类
序列式容器
- 动态数组(
vector
) 后端可高效增加元素的顺序表。 - 数组(
array
)C++11,定长的顺序表,C 风格数组的简单包装。 - 双端队列(
deque
) 双端都可高效增加元素的顺序表。 - 列表(
list
) 可以沿双向遍历的链表。 - 单向列表(
forward_list
) 只能沿一个方向遍历的链表。
关联式容器
- 集合(
set
) 用以有序地存储 互异 元素的容器。其实现是由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种比较元素大小的谓词进行排列。 - 多重集合(
multiset
) 用以有序地存储元素的容器。允许存在相等的元素。 - 映射(
map
) 由 {键,值} 对组成的集合,以某种比较键大小关系的谓词进行排列。 - 多重映射(
multimap
) 由 {键,值} 对组成的多重集合,亦即允许键有相等情况的映射。 -
无序(关联式)容器
- 无序(多重)集合(
unordered_set
/unordered_multiset
)C++11,与set
/multiset
的区别在于元素无序,只关心「元素是否存在」,使用哈希实现。 - 无序(多重)映射(
unordered_map
/unordered_multimap
)C++11,与map
/multimap
的区别在于键 (key) 无序,只关心 “键与值的对应关系”,使用哈希实现。 -
容器适配器
容器适配器其实并不是容器。它们不具有容器的某些特点(如:有迭代器、有 clear()
函数……)。
「适配器是使一种事物的行为类似于另外一种事物行为的一种机制」,适配器对容器进行包装,使其表现出另外一种行为。
- 栈(
stack
) 后进先出 (last in first out
)(LIFO) 的容器,默认是对双端队列(deque
)的包装。 - 队列(
queue
) 先进先出 (first in first out
) 的容器,默认是对双端队列(deque
)的包装。 - 优先队列(
priority_queue
) 元素的次序是由作用于所存储的值对上的某种谓词决定的的一种队列,默认是对动态数组(vector
)的包装。
共有函数
=
:有赋值运算符以及复制构造函数。
begin()
:返回指向开头元素的迭代器。
end()
:返回指向末尾的下一个元素的迭代器。end()
不指向某个元素,但它是末尾元素的后继。
size()
:返回容器内的元素个数。
max_size()
:返回容器 理论上 能存储的最大元素个数。依容器类型和所存储变量的类型而变。
empty()
:返回容器是否为空。
swap()
:交换两个容器。
clear()
:清空容器。
==
/!=
/<
/>
/<=
/>=
:按 字典序 比较两个容器的大小。(比较元素大小时 map
的每个元素相当于 set < pair < key, value > >
,无序容器不支持 <
/>
/<=
/>=
。)
迭代器
在 STL 中,迭代器(Iterator)用来访问和检查 STL 容器中元素的对象,它的行为模式和指针类似,但是它封装了一些有效性检查,并且提供了统一的访问格式。类似的概念在其他很多高级语言中都存在,如 Python 的 __iter__
函数,C# 的 IEnumerator
。
基础使用
迭代器听起来比较晦涩,其实迭代器本身可以看作一个数据指针。迭代器主要支持两个运算符:自增 (++
) 和解引用(单目 *
运算符),其中自增用来移动迭代器,解引用可以获取或修改它指向的元素。
指向某个STL容器container
中元素的迭代器的类型一般为 container::iterator
。
迭代器可以用来遍历容器,例如,下面两个 for 循环的效果是一样的:
vector<int> a(10);
for (int i = 0; i < a.size(); i++)
{
cout << a[i] << "\n";
}
for (vector<int>::iterator it = a.begin(); it != a.end(); it++)
{
cout << *it << "\n";
}
`auto` 在竞赛中的使用
大部分选手都喜欢使用 auto
来代替繁琐的迭代器声明。根据 2021 年 9 月发布的 关于 NOI 系列活动中编程语言使用限制的补充说明,NOI 系列比赛(包括 CSP J/S)在评测时将使用 C++14,这个版本已经支持了 auto
关键字。
分类
在 STL 的定义中,迭代器根据其支持的操作依次分为以下几类:
- InputIterator(输入迭代器):只要求支持拷贝、自增和解引访问。
- OutputIterator(输出迭代器):只要求支持拷贝、自增和解引赋值。
- ForwardIterator(向前迭代器):同时满足 InputIterator 和 OutputIterator 的要求。
- BidirectionalIterator(双向迭代器):在 ForwardIterator 的基础上支持自减(即反向访问)。
- RandomAccessIterator(随机访问迭代器):在 BidirectionalIterator 的基础上支持加减运算和比较运算(即随机访问)。
- ContiguousIterator(连续迭代器):在 RandomAccessIterator 的基础上要求对可解引用的迭代器
a + n
满足*(a + n)
与*(std::address_of(*a) + n)
等价(即连续存储,其中a
为连续迭代器、n
为整型值)。
ContiguousIterator 于 C++17 中正式引入。
为什么输入迭代器叫输入迭代器?
「输入」指的是「可以从迭代器中获取输入」,而「输出」指的是「可以输出到迭代器」。
「输入」和「输出」的施动者是程序的其它部分,而不是迭代器自身。
其实这个「分类」并不互斥——一个「类别」是可以包含另一个「类别」的。例如,在要求使用向前迭代器的地方,同样可以使用双向迭代器。
不同的 STL 容器 支持的迭代器类型不同,在使用时需要留意。
指针满足随机访问迭代器的所有要求,可以当作随机访问迭代器使用。
相关函数
很多STL 函数都使用迭代器作为参数。
可以使用 std::advance(it, n)
将迭代器 it
向后移动 n
步;若 n
为负数,则对应向前移动,此时迭代器必须满足双向迭代器,否则行为未定义。
在 C++11 以后可以使用 std::next(it)
获得向前迭代器 it
的后继(此时迭代器 it
不变),std::next(it, n)
获得向前迭代器 it
的第 n
个后继。
在 C++11 以后可以使用 std::prev(it)
获得双向迭代器 it
的前驱(此时迭代器 it
不变),std::prev(it, n)
获得双向迭代器 it
的第 n
个前驱。
STL 容器一般支持从一端或两端开始的访问,以及对 const 修饰符的支持。例如容器的 begin()
函数可以获得指向容器第一个元素的迭代器,rbegin()
函数可以获得指向容器最后一个元素的反向迭代器,cbegin()
函数可以获得指向容器第一个元素的 const 迭代器,end()
函数可以获得指向容器尾端(「尾端」并不是最后一个元素,可以看作是最后一个元素的后继;「尾端」的前驱是容器里的最后一个元素,其本身不指向任何一个元素)的迭代器。
可在 Iterator library - cppreference.com 查看更多用法。
下一节课—— 序列式容器简介
点赞,点赞,点赞!!!!!!!
球球了!!!!
求求了!!!!
QwQ QwQ