C++系列3:C++11 STL
Posted IE06
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++系列3:C++11 STL相关的知识,希望对你有一定的参考价值。
首先,c++11之后基本相当于换了一门语言,用cout << __cplusplus << endl;
确认,如果出现的头六位数是大于等于201103的,则支持C++11。
这里首先讲STL,英文全称 standard template library,中文可译为标准模板库或者泛型库,其包含有大量的模板类和模板函数,是 C++ 提供的一个基础模板的集合,用于完成诸如输入/输出、数学计算等功能。
1. 远古方式
这里首先介绍一下传统方法如何建立数组:
- 静态 int array[100]; 定义了数组 array,并未对数组进行初始化
- 静态 int array[100] = {1,2}; 定义并初始化了数组 array
- 动态 int* array = new int[100]; delete []array; 分配了长度为 100 的数组 array
- 动态 int* array = new int[100]{1,2}; delete []array; 为长度为100的数组array初始化前两个元素
数组如果用了动态数组(使用指针),注意用完了之后要delete释放
如何获取数组长度:
char str[20]=“0123456789”;
int a=strlen(str); // a=10; >>>> strlen 计算字符串的长度,以结束符 0x00 为字符串结束。
int b=sizeof(str); // 而 b=20; >>>> sizeof 计算的则是分配的数组 str[20] 所占的内存空间的大小,不受里面存储的内容改变。
2. 序列式容器
2.1 array
静态数组可以使用stl中的array,需要引入 array 头文件
array和下面的vector一样,都可以用[]和at()两种方式随机访问元素。
2.2 vector
使用vector需要include ,首先是基本用法。
它和下面deque的最主要区别,莫过于它有一个insert和emplace的语法。emplace的效率更高:
其次是迭代器,核心是指针也可以用++运算符。
vector 容器还提供了 2 个成员函数,即 front() 和 back(),它们分别返回 vector 容器中第一个和最后一个元素的引用,通过利用这 2 个函数返回的引用,可以访问(甚至修改)容器中的首尾元素。
2.3 deque
deque 容器也擅长在序列尾部添加或删除元素(时间复杂度为O(1)),而不擅长在序列中间添加或删除元素。
deque 容器也可以根据需要修改自身的容量和大小。
2.4 list
双向链表,不能随机访问。实际场景中,如何需要对序列进行大量添加或删除元素的操作,而直接访问元素的需求却很少,这种情况建议使用 list 容器存储序列。
通过 front() 和 back() 成员函数,可以分别获得 list 容器中第一个元素和最后一个元素的引用形式。举个例子:
2.5 forward_list
单向链表,擅长在序列的任何位置进行插入元素或删除元素的操作,但对于访问存储的元素,没有其它容器(如 array、vector)的效率高。
另外,由于单链表没有双向链表那样灵活,因此相比 list 容器,forward_list 容器的功能受到了很多限制。比如,由于单链表只能从前向后遍历,而不支持反向遍历,因此 forward_list 容器只提供前向迭代器,而不是双向迭代器。这意味着,forward_list 容器不具有 rbegin()、rend() 之类的成员函数。
forward_list 容器中是不提供 size() 函数的,但如果想要获取 forward_list 容器中存储元素的个数,可以使用头文件 中的 distance() 函数。
3. 关联式容器
关联式容器在存储元素值的同时,还会为各元素额外再配备一个值(又称为“键”,其本质也是一个 C++ 基础数据类型或自定义类型的元素),它的功能是在使用关联式容器的过程中,如果已知目标元素的键的值,则直接通过该键就可以找到目标元素,而无需再通过遍历整个容器的方式。
弃用序列式容器,转而选用关联式容器存储元素,往往就是看中了关联式容器可以快速查找、读取或者删除所存储的元素,同时该类型容器插入元素的效率也比序列式容器高。
3.1 map和multimap
和 map 容器唯一的不同在于,multimap 容器中存储元素的键可以重复。下面是最简单的使用方法:
3.2 使用pair
pair 类模板定义在头文件中。
3.3 set和multiset
3.4 无序关联式容器
实际场景中如果涉及大量遍历容器的操作,建议首选关联式容器;反之,如果更多的操作是通过键获取对应的值,则应首选无序容器。
3. 容器适配器
容器适配器是一个封装了序列容器的类模板,它在一般序列容器的基础上提供了一些不同的功能。之所以称作适配器类,是因为它可以通过适配容器现有的接口来提供不同的功能。
介绍 3 种容器适配器,分别是 stack、queue、priority_queue
3.1 stack
stack 栈适配器是一种单端开口的容器(如图 1 所示),实际上该容器模拟的就是栈存储结构,即无论是向里存数据还是从中取数据,都只能从这一个开口实现操作。
3.2 queue
和 stack 栈容器适配器不同,queue 容器适配器有 2 个开口,其中一个开口专门用来输入数据,另一个专门用来输出数据,如图 1 所示。
3.3 priority_queue
底层采用堆结构存储数据,称为优先级队列。
priority_queue 容器适配器中元素的存和取,遵循的并不是 “First in,First out”(先入先出)原则,而是“First in,Largest out”原则。直白的翻译,指的就是先进队列的元素并不一定先出队列,而是优先级最大的元素最先出队列。
和 queue 一样,priority_queue 也没有迭代器,因此访问元素的唯一方式是遍历容器,通过不断移除访问过的元素,去访问下一个元素。
4. 算法
首先需要#include <algorithm>
4.1 sort
假设这样一种情境,有一个存有 100 万个元素的容器,但我们只想从中提取出值最小的 10 个元素,该如何实现呢?
通过前面的学习,读者可能会想到使用 sort() 或者 stable_sort() 排序函数,即通过对容器中存储的 100 万个元素进行排序,就可以成功筛选出最小的 10 个元素。但仅仅为了提取 10 个元素,却要先对 100 万个元素进行排序,可想而知这种实现方式的效率是非常低的。
对于解决类似的问题,C++ STL 标准库提供了更高效的解决方案,即使用 partial_sort() 或者 partial_sort_copy() 函数。
4.2 merge
merge() 函数用于将 2 个有序序列合并为 1 个有序序列,前提是这 2 个有序序列的排序规则相同(要么都是升序,要么都是降序)。并且最终借助该函数获得的新有序序列,其排序规则也和这 2 个有序序列相同。
举个例子,假设有 2 个序列,分别为5,10,15,20,25和7,14,21,28,35,42,显然它们不仅有序,而且都是升序序列。因此借助 merge() 函数,我们就可以轻松获得如下这个有序序列:
5 7 10 15 17 20 25 27 37 47 57
当 2 个有序序列存储在同一个数组或容器中时,如果想将它们合并为 1 个有序序列,除了使用 merge() 函数,更推荐使用 inplace_merge() 函数。
4.3 find
find() 函数本质上是一个模板函数,用于在指定范围内查找和目标元素值相等的第一个元素。
该函数会返回一个输入迭代器,当 find() 函数查找成功时,其指向的是在 [first, last) 区域内查找到的第一个目标元素;如果查找失败,则该迭代器的指向和 last 相同。
以上是关于C++系列3:C++11 STL的主要内容,如果未能解决你的问题,请参考以下文章
C++ STL应用与实现5: 如何使用std::array (since C++11)
C++ STL应用与实现64: 如何使用shuffle和random_shuffle : 洗牌 (since C++11)
C++ STL应用与实现22: 函数组合之1:如何使用std::bind (since C++11)
swig c++ to perl : 如何使用 c++11 字符串 STL 函数