欢乐C++ —— 7. 顺序容器概览
参考:
http://c.biancheng.net/view/6967.html 适配器
http://c.biancheng.net/cplus/80/ STL 和容器
简述
简单说一下C++的容器,这节主要介绍常见的顺序容器的操作。
STL 标准库
C++ 语言的核心优势之一就是便于代码的重用。C++ 中有两个方面体现重用:
- 一是面向对象的继承和多态机制;
- 二是通过模板的概念实现了对泛型程序设计的支持。
C++ 的标准模板库(Standard Template Library,STL)是泛型程序设计最成功应用的实例。STL 是一些常用数据结构(如链表、可变长数组、排序二叉树)和算法(如排序、查找)的模板的集合。有了 STL,程序员就不必编写大多数常用的数据结构和算法。而且 STL 是经过精心设计的,运行效率很高,比水平一般的程序员编写的同类代码速度更快。
容器
容器(container)用于存放数据的类模板。可变长数组、链表、平衡二叉树等数据结构在 STL 中都被实现为容器。
C++ 的容器总的可以分为两类:
-
顺序容器:容器中的元素是按照它们在容器中的位置顺序来保存和访问的。
-
关联容器:容器中的元素是按照关键字来保存和访问。
关联容器内的元素是排序的。插入元素时,容器会按一定的排序规则将元素放到适当的位置上,因此插入元素时不能指定位置。默认情况下,关联容器中的元素是从小到大排序的,而且用
<
运算符比较元素或关键字大小。因为是排好序的,所以关联容器在查找时具有非常好的性能。 -
容器适配器:通过对上面两类容器进行限制一部分操作
STL 在两类容器的基础上屏蔽一部分功能,突出或增加另一部分功能,实现了三种容器适配器:栈 stack、队列 queue、优先级队列 priority_queue。这些容器适配器底层没有自己的数据结构,依靠其它的容器。其本质上还是容器,只不过此容器模板类的实现,利用了大量其它基础容器模板类中已经写好的成员函数。
底层
我的是MSVC 14.16 版本。
- vector 动态扩容数组
- deque 底层为map 和多个小数组
- list 双向循环链表
- forward_list 单向链表
- array 固定大小数组
- string 动态扩容数组
- map set multimap multiset 红黑树
- unordered_map unordered_set unordered_mulitimap unordered_mulitiset 哈希链表
- stack queue 底层为 deque
- priority_queue 底层为 vector
顺序容器
![image-20200409172842242](https://img2020.cnblogs.com/blog/1512048/202006/1512048-20200618160550165-1921892250.png)
它们之所以被称为顺序容器,是因为元素在容器中的位置同元素的值无关,即容器不是排序的。将元素插入容器时,指定在什么位置(尾部、头部或中间某处)插入,元素就会位于什么位置。
该选择那种顺序容器?
以下是一些准则:
- 如果程序要求随机访问,那就 vector 或 deque
- 如果程序要求经常插入:
- 如果是头尾插入:deque
- 在中间插入:list 或 forward_list
- 如果既需要随机访问,又需要中间访问,测试vector deque 与 list forward_list 的相对性能。
- 如果程序中有很多小元素,且对空间要求较高,那就不要使用 list 或 forward_list
总之,结合各个容器的特性,按照使用使用场景进行筛选。
一些常见操作
添加元素
- push_back() … insert() … 拷贝构造元素
- emplace() … 新构造元素。
访问元素
- back() front() 返回容器中最后一个 / 第一个元素的引用。
- 重载运算符[] 和 at() 返回特定下标位置的元素的引用。
这些保证索引有效是调用者的责任。
删除元素
- pop_back() … erase() … clear()
删除时小心迭代器失效。
容器空间大小
-
reserve() resize()
两者区别在于前者只给元素分配空间,而后者是分配空间并构造元素。
empty() size() capacity() resize(10) false 10 10 reserve(10) true 0 10 在reserve 之后,不能使用下标访问vector ,因为此时vector 中没有元素。reserve 和resize 都可能导致容器扩容,元素搬家。
-
max_size() shrink_to_fit()
前者是返回当前环境最大存放元素的个数。后者使容器大小空间大小缩至和size() 相等。
赋值相关
-
assign()
给一个容器对象赋值。有多种重载版本,可以拷贝别的元素类型相同的容器的元素,或者使用列表,或者指定某个元素及其数量。都将会调用拷贝构造函数。
-
swap()
在绝大部分容器中,swap 只是交换底层的数据结构,所以开销与容器所含的元素个数无关。但在array 中 swap 会老老实实交换元素。
-
列表初始化与赋值
先将元素拷贝到 initializer_list 可变形参类中,然后再从这个类拷贝到容器中。
迭代器
四种类型begin() cbegin() rbegin() crbegin()
迭代器默认的范围是左闭右开 即 [begin , end)
迭代器底层是指针,所以在容器中添加,删除等操作可能使得迭代器失效。
string 容器的额外操作
-
substr() 返回子串的拷贝
-
额外支持下标版的insert 和 erase
-
append() 追加 replace() 替换子串
-
搜索:
如果成功,返回的是一个无符号整型,如果失败 返回 string::npos。
-
数值转换: