# STL实用技术专题

STL实用技术专题

STL的从广义上讲分为三类: algorithm (算法)、container (容器)和iterator (迭代器),    

STL详细的说六大组件

– 容器( Container )
– 算法( Algorithm )
– 迭代器( Iterator )
– 仿函数( Function object )
– 适配器( Adaptor)
– 空间配制器( allocator )

1. string

string 和char* 的比较:

string 是一个类, char* 是一个指向字符的指针。
string 封装了char* ,管理这个字符串,是一个char* 型的容器。
string 不用考虑内存释放和越界。
string 封装相关函数。

相关函数

const char *c_str() const;  // 从string 取得const char* 的操作
string &operator+=(const string &s);    // 把字符串s 连接到当前字符串结尾
string &operator+=(const char *s);  // 把字符串s 连接到当前字符串结尾

int compare(const string &s) const; // 与字符串s 比较
int compare(const char *s) const; // 与字符串s 比较

string substr(int pos=0, int n=npos) const; // 返回由pos 开始的n 个字符组成的子字符串

find以及rfind函数。
replace函数
insert函数
erase函数 

相关算法:

transform(s2.begin(), s2.end(), s2.begin(), toupper);
transform(s3.begin(), s3.end(), s3.begin(), tolower);

2. Vector

向量是表示可以改变大小的数组的序列容器。

就像数组一样,向量使用连续的存储位置作为元素,这意味着它们的元素也可以使用常量指向其元素的偏移来访问,并且与数组一样有效。但与数组不同,它们的大小可以动态变化,其存储由容器自动处理。

在内部,向量使用动态分配的数组来存储它们的元素。可能需要重新分配此数组,以便在插入新元素时增大大小,这意味着分配新数组并将所有元素移动到该数组。就处理时间而言,这是相对昂贵的任务,因此,每次将元素添加到容器时,向量都不会重新分配。

相反,矢量容器可以分配一些额外的存储空间以适应可能的增长,因此容器的实际容量可能大于包含其元素所需的存储容量(即,其大小)。库可以实现不同的增长策略以在内存使用和重新分配之间取得平衡,但无论如何,重新分配只应以对数增长的大小间隔发生,以便在向量末尾插入单个元素可以提供摊销的常量时间复杂性(见push_back)。

因此,与数组相比,向量消耗更多内存,以换取管理存储和以有效方式动态增长的能力。

与其他动态序列容器(deques,lists和forward_lists)相比,向量非常有效地访问其元素(就像数组一样)并且相对有效地从其末尾添加或删除元素。对于涉及在末尾以外的位置插入或删除元素的操作,它们的性能比其他位置差,并且与列表和forward_lists相比具有更少的一致的迭代器和引用。

相关函数

vector.size(); // 返回容器中元素的个数
vector.capacity()   // 分配的内存大小
vector.empty(); // 判断容器是否为空

3. deque

双端队列
deque(通常发音为“deck”)是双端队列的不规则首字母缩写。双端队列是具有动态大小的序列容器,可以在两端(前端或后端)扩展或收缩。

特定库可以以不同方式实现deques,通常作为某种形式的动态数组。但无论如何,它们允许通过随机访问迭代器直接访问各个元素,并根据需要通过扩展和收缩容器来自动处理存储。

因此,它们提供类似于vector的功能,但是在序列的开头也有效地插入和删除元素,而不仅仅是在其结尾。但是,与向量不同,deques不能保证将其所有元素存储在连续的存储位置:通过将指针偏移到另一个元素来访问双端队列中的元素会导致未定义的行为。

vector和deque都提供了一个非常相似的接口,可以用于类似的目的,但内部都以完全不同的方式工作:虽然矢量使用需要偶尔重新分配以生长的单个数组,但是deque的元素可以分散在不同的存储块,容器在内部保存必要的信息,以便在恒定的时间内通过统一的顺序接口(通过迭代器)直接访问其任何元素。因此,deques在内部比矢量稍微复杂一些,但是这允许它们在某些情况下更有效地生长,特别是在非常长的序列中,其中重新分配变得更加昂贵。

对于涉及在开头或结尾以外的位置频繁插入或删除元素的操作,deques表现更差,并且与列表和转发列表相比具有更少的一致的迭代器和引用。

相关函数

deque.push_back(elem); // 在容器尾部添加一个数据
deque.push_front(elem); // 在容器头部插入一个数据
deque.pop_back(); // 删除容器最后一个数据
deque.pop_front(); // 删除容器第一个数据
deque.at(idx); // 返回索引idx 所指的数据,如果idx 越界,抛出out_of_range 。
deque[idx]; // 返回索引idx 所指的数据,如果idx 越界,不抛出异常,直接出错。
deque.front(); // 返回第一个数据。
deque.back(); // 返回最后一个数据

4. stack

stack 是简单地装饰deque 容器而成为另外的一种容器。

LIFO堆栈
堆栈是一种容器适配器,专门设计用于在LIFO上下文中操作(后进先出),其中元素仅从容器的一端插入和提取。

堆栈实现为容器适配器,它是使用特定容器类的封装对象作为其底层容器的类,提供一组特定的成员函数来访问其元素。 元素从特定容器的“后面”推出/弹出,该容器称为堆栈顶部。

底层容器可以是任何标准容器类模板或一些其他专门设计的容器类。

相关函数

stack.push(elem); // 往栈头添加元素
stack.pop(); // 从栈头移除第一个元素
stack.empty(); // 判断堆栈是否为空
stack.size(); // 返回堆栈的大小

5.1 queue

FIFO队列
队列是一种容器适配器,专门设计用于在FIFO上下文中操作(先进先出),其中元素插入容器的一端并从另一端提取。

队列实现为容器适配器,它是使用特定容器类的封装对象作为其底层容器的类,提供一组特定的成员函数来访问其元素。 元素被推入特定容器的“后面”并从其“前面”弹出。

底层容器可以是标准容器类模板之一或其他一些专门设计的容器类。

相关函数

queue.push(elem); // 往队尾添加元素
queue.pop(); // 从队头移除第一个元素
queue.empty(); // 判断队列是否为空
queue.size(); // 返回队列的大小
queue.back(); // 返回最后一个元素
queue.front(); // 返回第一个元素

5.2 priority_queue

优先队列
优先级队列是一种容器适配器,根据一些严格的弱排序标准,专门设计使其第一个元素始终是它包含的最大元素。

此容器类似于堆,其中可以随时插入元素,并且只能检索最大堆元素(优先级队列中顶部的元素)。

优先级队列实现为容器适配器,它是使用特定容器类的封装对象作为其底层容器的类,提供一组特定的成员函数来访问其元素。元素从特定容器的“后面”弹出,该容器称为优先级队列的顶部。

底层容器可以是任何标准容器类模板或一些其他专门设计的容器类。容器应通过随机访问迭代器访问。

相关函数

priority_queue<int> p1; // 默认是最大值优先级队列
//priority_queue<int, vector<int>, less<int>> p1; // 相当于这样写
priority_queue<int, vector<int>, greater<int>> p2; // 最小值优先级队列

6. list

列表是序列容器,允许在序列中的任何位置进行恒定时间插入和擦除操作,并在两个方向上进行迭代。

列表容器实现为双向链表;双向链表可以将它们包含的每个元素存储在不同且不相关的存储位置中。排序由内部保留,链接到前面元素的链接的每个元素,以及到它后面的元素的链接。

它们与forward_list非常相似:主要区别在于forward_list对象是单链接列表,因此它们只能向前迭代,以换取更小和更高效。

与其他基本标准序列容器(数组,向量和双端队列)相比,列表在插入,提取和移动已经获得迭代器的容器内的任何位置中的元素时通常表现更好,因此也在使用密集型的算法中其中,就像排序算法一样。

与其他序列容器相比,list和forward_lists的主要缺点是它们无法通过位置直接访问元素;例如,要访问列表中的第六个元素,必须从已知位置(如开头或结尾)迭代到该位置,这将在这些位置之间的距离中采用线性时间。它们还消耗一些额外的内存来保持与每个元素相关联的链接信息(这可能是大型小元素列表的重要因素)。

相关函数

list.push_back(elem); // 在容器尾部加入一个元素
list.pop_back(); // 删除容器中最后一个元素
list.push_front(elem); // 在容器开头插入一个元素
list.pop_front(); // 从容器开头移除第一个元素

7.1 set

集合是按特定顺序存储唯一元素的容器。

在集合中,元素的值也标识它(值本身是类型T的键),并且每个值必须是唯一的。 集合中元素的值不能在容器中修改一次(元素总是const),但可以在容器中插入或删除它们。

在内部,集合中的元素总是按照其内部比较对象(类型比较)指示的特定严格弱排序标准进行排序。

set容器通常比unordered_set容器慢,以便通过键访问单个元素,但它们允许根据子集的顺序直接迭代子集。

集合通常实现为二叉搜索树。

7.2 multiset

multiset是按特定顺序存储元素的容器,多个元素可以具有等效值。

在multiset中,元素的值也标识它(值本身是T类型的键)。 多个集合中元素的值不能在容器中修改一次(元素总是const),但可以在容器中插入或删除它们。

在内部,多集合中的元素总是按照其内部比较对象(类型比较)指示的特定严格弱排序标准进行排序。

multiset容器通常比unordered_multiset容器慢,以便通过其键访问单个元素,但它们允许根据子集的顺序直接迭代子集。

multiset通常实现为二叉搜索树。

8.1 map

map是关联容器,它按照特定顺序存储由键值和映射值的组合形成的元素。

在映射中,键值通常用于排序和唯一标识元素,而映射值存储与此键关联的内容。键和映射值的类型可能不同,并在成员类型value_type中组合在一起,这是一种结合两者的对类型:

typedef pair <const Key,T> value_type;

在内部,map中的元素总是按照其内部比较对象(类型比较)指示的特定严格弱排序标准按其键排序。

map容器通常比unordered_map容器慢,以便通过键访问单个元素,但它们允许根据子集的顺序直接迭代子集。

可以使用括号运算符((operator [])通过相应的键直接访问映射中的映射值。

map通常实现为二叉搜索树。

8.2 multimap

Multimaps是关联容器,用于存储由键值和映射值的组合形成的元素,遵循特定顺序,并且多个元素可以具有等效键。

在multimap中,键值通常用于排序和唯一标识元素,而映射值存储与此键关联的内容。 键和映射值的类型可能不同,并在成员类型value_type中组合在一起,这是一种结合两者的对类型:

typedef pair <const Key,T> value_type;

在内部,多图中的元素总是按照其内部比较对象(类型比较)指示的特定严格弱排序标准按其键排序。

multimap容器通常比unordered_multimap容器慢,以通过其键访问单个元素,但它们允许根据子集的顺序直接迭代子集。

multimap通常实现为二叉搜索树。

以下是 C++11 新增的

9. array

数组是固定大小的序列容器:它们包含以严格线性序列排序的特定数量的元素。

在内部,数组不保留除其包含的元素之外的任何数据(甚至不是它的大小,这是一个模板参数,在编译时固定)。它在存储大小方面与使用语言括号语法([])声明的普通数组一样高效。这个类只是为它添加了一层成员函数和全局函数,因此数组可以用作标准容器。

与其他标准容器不同,数组具有固定大小,并且不通过分配器管理其元素的分配:它们是封装固定大小元素数组的聚合类型。因此,它们不能动态扩展或收缩(参见可扩展的类似容器的向量)。

零大小的数组是有效的,但不应该取消引用它们(成员前面,后面和数据)。

与标准库中的其他容器不同,交换两个数组容器是一种线性操作,涉及单独交换范围中的所有元素,这通常是效率相当低的操作。另一方面,这允许两个容器中的元素的迭代器保持其原始容器关联。

数组容器的另一个独特功能是它们可以被视为元组对象:<array>头重载get函数以访问数组的元素,就像它是一个元组一样,以及专门的tuple_size和tuple_element类型。

10. forward_list

前向列表是序列容器,允许在序列中的任何位置进行恒定时间插入和擦除操作。

forward_list实现为单链表;单个链接列表可以将它们包含的每个元素存储在不同且不相关的存储位置中。通过关联到序列中下一个元素的链接的每个元素来保持排序。

forward_list容器和list容器之间的主要设计区别在于,第一个容器内部仅保留指向下一个元素的链接,而后者每个元素保留两个链接:一个指向下一个元素,一个指向前一个元素,从而有效在两个方向上迭代,但每个元素消耗额外的存储空间,并且插入和移除元素的时间略微增加。因此,forward_list对象比列表对象更有效,尽管它们只能向前迭代。

与其他基本标准序列容器(数组,向量和双端队列)相比,forward_list通常在容器内的任何位置插入,提取和移动元素时执行得更好,因此也可以在大量使用这些元素的算法中执行,例如排序算法。

与其他序列容器相比,forward_lists和list的主要缺点是它们无法通过位置直接访问元素;例如,要访问forward_list中的第六个元素,必须从开头到该位置进行迭代,这需要在它们之间的距离内采用线性时间。它们还消耗一些额外的内存来保持与每个元素相关联的链接信息(这可能是大型小元素列表的重要因素)。

forward_list类模板的设计考虑了效率:通过设计,它与简单的手写C风格单链表一样高效,实际上是唯一一个为了提高效率而故意缺乏大小成员函数的标准容器:由于其作为链接列表的性质,具有持续时间的大小成员将要求它保持其大小的内部计数器(如列表所示)。这将消耗一些额外的存储空间,并使插入和移除操作的效率略低。要获取forward_list对象的大小,可以使用距离算法及其开始和结束,这是一个需要线性时间的操作。

11.1 unordered_set

unordered_set是不按特定顺序存储唯一元素的容器,它允许根据单个元素的值快速检索单个元素。

在unordered_set中,元素的值同时是其键,它唯一地标识它。 键是不可变的,因此,unordered_set中的元素不能在容器中修改一次 - 但是可以插入和删除它们。

在内部,unordered_set中的元素不按任何特定顺序排序,而是根据其哈希值组织到存储桶中,以允许直接通过其值快速访问各个元素(平均时间复杂度恒定)。

unordered_set容器比通过其键访问单个元素的set容器更快,尽管它们通过其元素子集的范围迭代通常效率较低。

容器中的迭代器至少是前向迭代器。

11.2 unordered_multiset

unordered_multiset是不按特定顺序存储元素的容器,允许基于其值快速检索单个元素,非常类似于unordered_set容器,但允许不同元素具有等效值。

在unordered_multiset中,元素的值同时是其键,用于标识它。键是不可变的,因此,unordered_multiset中的元素不能在容器中修改一次 - 但是可以插入和删除它们。

在内部,unordered_multiset中的元素没有按任何特定方式排序,而是根据其哈希值组织到存储桶中,以允许直接通过其值快速访问各个元素(平均时间复杂度恒定)。

具有等效值的元素在同一个存储桶中组合在一起,并且迭代器(请参阅equal_range)可以迭代所有这些元素。

容器中的迭代器至少是前向迭代器。

请注意,此容器未在其自己的标头中定义,但使用unordered_set共享标头<unordered_set>。

12.1 unordered_map

unordered_map是关联容器,其存储由键值和映射值的组合形成的元素,并且允许基于其键快速检索各个元素。

在unordered_map中,键值通常用于唯一标识元素,而映射值是具有与此键关联的内容的对象。键和映射值的类型可能不同。

在内部,unordered_map中的元素没有按照其键值或映射值以任何特定顺序排序,而是根据其哈希值组织到桶中,以允许通过键值直接快速访问各个元素(使用常量)平均时间复杂度)。

unordered_map容器比映射容器更快地通过其键访问单个元素,尽管它们通过其元素子集的范围迭代通常效率较低。

unordered_map实现直接访问运算符(operator []),允许使用其键值作为参数直接访问映射值。

容器中的迭代器至少是前向迭代器。

12.2 unordered_multimap

unordered_multimap是关联容器,它存储由键值和映射值组合形成的元素,非常类似于unordered_map容器,但允许不同的元素具有等效键。

在unordered_multimap中,键值通常用于唯一标识元素,而映射值是具有与此键关联的内容的对象。键和映射值的类型可能不同。

在内部,unordered_multimap中的元素没有按照其键值或映射值以任何特定顺序排序,而是根据其哈希值组织到桶中,以允许通过其键值直接快速访问各个元素(使用常量)平均时间复杂度)。

具有等效键的元素在同一个桶中组合在一起,并且迭代器(请参阅equal_range)可以遍历所有这些元素。

容器中的迭代器至少是前向迭代器。

请注意,此容器未在其自己的标头中定义,但与unordered_map共享标头<unordered_map>。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,667评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,361评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,700评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,027评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,988评论 5 361
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,230评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,705评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,366评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,496评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,405评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,453评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,725评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,803评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,015评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,514评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,111评论 2 341

推荐阅读更多精彩内容