C++标准库概述

版权声明:转载请注明出处 https://blog.csdn.net/guyu2019/article/details/88374194


在使用 C++ 编程中,最重要的库就是 C++ 标准库。C++ 标准库的核心是泛型容器和泛型算法,库中的这一子集通常称为标准模板库(Standard Template Library, STL),因为这一部分大量使用了模板。STL 的威力在于提供了泛型容器和泛型算法,使大部分算法可用于大部分容器,而无论容器中保存的数据类型是什么。性能是 STL 中非常重要的一部分。STL 的目标是要让 STL 容器和算法和手工编写的代码速度相当快。

编码原则

标准库大量使用了C++的模板特性和运算符重载特性,标准模板库也是这样。

C++标准库概述

字符串

从技术角度,C++ string实际上是basic_string模板char实例化的typedef名称。然而,不需要关注这些细节。只要像非模板类那样使用string即可。

正则表达式

I/O流

I/O功能在几个头文件中定义:

<fstream>
<iomanip>
<ios>
<iosfwd>
<iostream>
<istream>
<ostream>
<sstream>
<streambuf>
<strstream>

智能指针

C++用智能指针unique_ptrshared_ptrweek_ptr解决内存管理问题。

shared_ptrweek_ptr是线程安全的,都在<memory>中定义。

异常

C++标准库提供了一个异常的类层次结构,在程序中可以使用这些类,也可以通过继承方式创建自己的异常类型。

异常在以下头文件中定义:

<exception>
<stdexcept>
<system_error>

数学工具

虽然这些类模板化了,可以用于任何类型,但是一般不认为这些类是标准模板库的一部分。

  • <complex>提供了一个复数类complex,提供了对实部和虚部的数的操作抽象。

  • <ratio>有理数运算库,可以精确地表示任何由分子和分母定义的有限有理数。

  • <valarray>提供了valarray,和vector类似,但是对高性能数值应用做了特别的优化,提供了表示矢量切片概念的相关类。通过这些构件,可以构建执行矩阵数学运算的类。不过,没有内建的矩阵类。Boost这样的第三方库提供了矩阵支持。

获取数值极限的标准方式。

<limts>中的numeric_limits模板。

cout << "Max int value: " << numeric_limits<int>::max() << endl;
cout << "Min int value: " << numeric_limits<int>::min() << endl;
cout << "Lowest int value: " << numeric_limits<int>::lowest() << endl;
cout << "Max double value: " << numeric_limits<double>::max() << endl;
cout << "Min double value: " << numeric_limits<double>::min() << endl;
cout << "Lowest double value: " << numeric_limits<double>::lowest() << endl;

时间工具

<chrono>简化了时间相关的操作,特定时间间隔的定时操作和定时相关的操作执行。

随机数

srand()rand()只提供了非常初级的随机数,也无法修改生成随机数的分布。

C++11添加了一个完善的随机数库<random>,带有随机数引擎、随机数引擎适配器以及随机数分布。

通过<random>可以生成特定问题域的随机数,例如正态分布、负指数分布。

初始化列表

<initializer_list>编写参数数目可变的函数。

Pair和Tuple

  • <utility>定义了pair模板,可以用于存储两种不同类型的元素。这称为存储异构元素。

  • <tuple>定义的tuplepair的一种泛化,是一个固定大小的序列,元组的元素可以是异构的。

函数对象

实现函数调用运算符的类称为函数对象。函数对象可以用作某些STL算法的谓词。

<functional>

多线程

单个线程可以用<thread>建立。

多线程代码,需要考虑:几个线程不能同时读写同一个数据。为了避免这种情形,可以使用<atomic>定义的原子性。

<condition_vaiable><mutex>提供了其他线程同步机制。

如果只需计算某个数据,得到结果,并具有相应的异常处理,使用<future>头文件定义的asyncfuture要比thread简单。

类型特质

类型特质在<type_traits>中定义,提供了编译期间的类型信息。编写高级模板的时候可以使用它。

标准模板库

C++ STL(Standard Template Library, STL)容器是同构的,每种容器只允许一种类型的元素。

注意:C++标准定义了每个容器和算法的接口(interface),没有定义实现。因此,不同的供应商可以自由提供不同的实现。不过,作为接口的一部分,标准定义了性能需求。

STL容器

vector

如果在程序中需要快速访问元素,但是不会频繁在中间添加或删除元素,则应该使用vector

在任何可能的情况下使用vector而不是C风格的数组

vector<bool>有特定优化过的模板,不过标准没有规定如何实现优化空间。

list

双向链表

forward_list(C++11)

只支持单向遍历的单链表。内存需求比list小。

deque

双端队列(double-ended queue)。不过行为更像vector,而不是queue

提供了快速的元素访问(常量时间),在序列两端还提供了快速的插入和删除(摊还常量时间),但是在序列中间插入和删除的速度较慢(线性时间)。

如果需要在两头快速插入或删除元素,还要求快速访问所有元素,那么应该使用deque而不是vector

大部分场景并不满足这种需求,因此大部分情况下,vectorlist足以满足需求。

array

标准C风格数组的替代品。特别适用于大小固定的集合,而且没有vector的开销。

使用array比C风格数组有几点好处:

  • 总能知道自己的大小;

  • 不会自动转换为指针类型,避免某些bug;

  • 大小固定,允许堆栈上分配内存,不需要像vector一样需要堆访问权限。

vectorlistdequearrayforward_list容器都称为顺序容器(sequential container),因为它们保存的是元素的序列。

queue

先进先出的队列

priority_queue

优先级队列。插入和删除比简单的queue插入和删除慢。

stack

先入后出

从技术角度,queuepriority_queuestack容器都是容器适配器(adapter)。它们只是构建在某种标准顺序容器(vector、list或deque)上的简单接口。

set和multiset

类似数学上的集合。不过set中按照一定的顺序保存。排序的原因是当客户枚举元素时,能够以operator<或用户自定义的比较器的顺序出现。

set提供了对数时间的插入、删除和查找。如果要存储重复元素,就必须使用 <set> 头文件定义的 multiset

map和multimap

map 保存的是键/值对。map 按照排好的顺序保存元素,排序的依据是键值而非对象值。在其他所有方面,mapset 是一致的。如果需要关联键和值,就应该使用 map

multimap 也在 <map> 中定义,它和 map 的关系等同于 multisetset 的关系。确切地讲,multimap 是一个允许重复键的 map

setmap容器都是关联容器,因为它们关联了键和值。在set中,键本身就是值。这些容器会对元素进行排序,因此这些容器称为排序或者有序关联容器。

无序关联容器/哈希表

哈希表也称为无序(unordered associative container)。有4个无序关联容器:

  • unordered_map
  • unordered_set
  • unordered_multimap
  • unordered_multiset

在C++11之前,哈希表不属于C++标准库的一部分,因此很多第三方库都用hash作为前缀。因此C++标准委员会决定使用unordered而不是hash,避免名称冲突。

bitset

bitset不是常规意义的容器,不能插入和删除元素。固定大小,不支持迭代器。可以想象成一个可以读写的布尔值序列。

bitset不局限于int或者其他原始类型的大小。可以在声明时指定:bitset<N>

vector应该是默认使用的容器。实际上,vector中的插入和删除常常快于list或者forward_list。这是现代CPU上内存和缓存的工作方式,而listforward_list需要先移动到要插入或删除元素的位置上。list或者forward_list的内存可能是碎片化的,所以迭代慢于vector

STL算法

STL采取了分离数据(容器)和功能(算法)的方式。看上去有点违背了面向对象编程的思想,但是为了在STL中支持泛型编程,有必要这么做。

正交性的指导原则使算法和容器分离开,(几乎)所有算法都可以用于(几乎)所有容器。

有些容器以类方法提供了某些算法,因为泛型算法在特定容器上表现不出色。**比如,set提供了自己的find()算法,比泛型的find()算法快。**如果类提供了特定的算法实现,应该使用容器特定方法的形式。

泛型算法并不是直接对容器操作。泛型算法使用迭代器(iterator)作为中介。

函数名 概要
begin(), end() 第一个元素和最后一个元素后面的元素,返回非const迭代器
以下是C++14新增的迭代器
cbegin(), cend() 第一个元素和最后一个元素后面的元素,返回const迭代器
rbegin(), rend() 非const的反向迭代器
crbegin(), crend() const的反向迭代器

非修改顺序算法

有了这些算法后,几乎不再需要编写for循环来迭代值序列了。【就是暴力枚举的封装形式】

算法名称 算法概要 复杂度
adjacent_find() 查找第一个两个连续元素相等或匹配谓词的实例 线性
find()、find_if() 查找第一个匹配值或使谓词返回true的元素 线性
find_first_of() find类似,只是同时搜索多个元素中的一个 二次
find_if_not() 查找第一个使谓词返回false的元素 线性
search()、find_end() 在序列中查找第一个(search())或最后一个(find_end())匹配另一个序列的子序列,或者这个子序列的元素和谓词指定的一致 线性

搜索算法
不需要元素有序

比较算法

不要求有序,最差复杂度为线性复杂度

算法名称 算法概要
equal() 检查相应元素是否相等或匹配谓词,以此判断两个序列是否相等
mismatch() 返回每个序列第一个出现的和其他序列同一位置元素不匹配的元素
lexicographical_compare() 比较两个序列,判断这两个序列的“词典顺序”。将第一个序列中的每一个元素和第二个序列中对应的元素进行比较。如果一个元素小于另一个元素,那么这个序列按照词典顺序在前面。如果两个元素相等,则按顺序比较下一个元素

工具算法

算法名称 算法概要
all_of() 序列为空或对所有谓词返回true,则返回true,否则返回false
any_of() 序列为空或至少谓词判断有一次true,则返回true,否则返回false
none_of() 序列为空或者谓词对所有元素返回false,则返回true,否则返回false
count()、count_if() 计算匹配一个值或者使谓词返回true的元素个数

修改序列算法

算法名称 算法概要
copy()、copy_backward() 将一个序列的元素复制到另一个序列
copy_if() 将谓词返回true的元素复制到另一个序列
copy_n() 复制n个元素到另一个序列
fill() 所有元素设置为一个新值
fill_n() 前n个元素设置为一个新值
generate() 调用指定函数,为序列中每一个元素生成新值
generate_n() 调用指定函数,为序列中前n个元素生成一个新值
move(),move_backward() 将一个序列的元素移到另一个序列。使用了高效的移动语义
remove()、remove_if()、remove_copy()、remove_copy_if() 删除匹配给定值或使谓词返回true的元素,就地删除或将结果复制到另一个不同的序列
replace()、replace_if()、replace_copy()、replace_copy_if() 将匹配特定值或导致谓词返回true的所有元素替换为新元素,就地替换或将结果复制到新序列
reverse()、reverse_copy() 掉转序列中的元素,原地操作或者复制到新序列
rotate()、rotate_copy() 交换序列中前半部分和后半部分,原地操作或者复制到新序列。两个要交换的子序列不一定要一样大
shuffle()、random_shuffle() 打乱元素的顺序,random_shuffle()在C++14之后被废弃
transform() 对序列中的每个元素调用一元函数,或对两个队列中的对应元素调用二元函数
unique()、unique_copy() 在序列中删除连续出现的重复元素,原地删除或复制到新序列

操作算法
for_each:对每个元素执行函数

交换算法

算法名称 算法概要
iter_swap()swap_ranges() 交换两个元素,或交换两个元素的序列
swap() 交换两个值,在<utility>头文件中定义

分区算法

排序算法

算法名称 算法概要 复杂度
is_sorted()、is_sorted_until() 检查一个序列是否排序,或者哪个子序列已经排序 线性
nth_element() 重定位序列中的第n个元素,使第n个位置的元素就是排好序之后第n个位置的元素。该函数会重新安排所有元素,使第n个元素前面的所有元素都小于新的第n个元素 线性
partial_sort()、partial_sort_copy() 只排序序列中的一部分元素:只有前n个元素(由迭代器指定)排序,其余元素不排序。在原位置排序,或者复制到新的序列 线性对数
sort()、stable_sort() 在原位置排序,保留重复元素的顺序或不保留 线性对数

二叉搜索树算法

算法名称 算法概要 复杂度
lower_bound()、upper_bound()、equal_range() 查找包含给定元素的范围的头(lower_bound())、尾(upper_bound())或两端(equal_range()) 对数
binary_search() 在序列中查找一个值 对数

集合算法

这些算法最适合set,但是也能操作大部分容器排序后的序列。【归并排序】

算法名称 算法概要 复杂度
inplace_merge() 在原位置将两个排好序的序列合并 线性对数
merge() 合并两个排好序的序列,将两个序列复制到新的序列 线性
includes() 确定是否序列中的每一个元素都在另一个序列中 线性
set_union()、set_intersection()、set_difference()、set_symmetric_difference()[并、交、差、补] 在两个排序的序列上执行特定的集合操作,将结果复制到第三个排序的序列中 线性

堆算法

堆(heap)是一个标准的数据结构,数组或序列中的元素在其中以半排序的方式排序,因此能够快速找到“顶部”的元素。通过使用6个算法可以对序列进行堆排序。

算法名称 算法概要 复杂度
is_heap() 检查某个范围的元素是否是一个堆 线性
is_heap_until() 在给定范围内的元素中查找最大的子范围 线性
make_heap() 从一个范围的元素中创建堆 线性
push_heap()pop_heap() 在堆中添加或删除元素 对数
sort_heap() 把堆转换为升序排列的元素范围 线性对数

最大/最小算法

算法名称 算法概要
min()max() 返回两个值中最小值或最大值
minmax() pair 方式返回两个或多个值中的最小值和最大值
min_element()max_element() 返回序列中最小或最大元素
minmax_element 找到序列中最小和最大元素,把结果返回为 pair

数值处理算法
<numeric>,所有的算法都是线性复杂度,不要求排序源序列。

算法名称 算法概要
accumulate() “累加”一个序列中所有的元素的值,默认行为是计算元素的和,但调用者可以提供不同的二元函数
adjacent_difference() 生成一个新的序列,其中每一个元素都是对应元素和源序列中之前元素的差(或其它二元操作)
inner_product() accumulate() 类似,但对两个序列操作。对序列中的并行元素调用二元函数(默认做乘法),通过另一个二元函数(默认加法)累加结果值。如果序列表示数学矢量,那么这个算法计算矢量的点积

置换算法

算法名称 算法概要 复杂度
is_permutation() 如果一个范围中的元素是另一个范围中的元素的转换,就返回true 二次
next_permutation()、prev_permutation() 修改序列,将序列转换为下一个或前一个排列。如果从正确排序的序列开始,连续调用可以获得所有可能的排列。如果没有更多排列,则返回false 线性

STL中还缺什么

  • STL不能保证任何线程安全

  • STL没有提供任何泛型的树结构或图结构(不过大部分语言都没有提供),如果编写解析器,就需要自己实现或者寻找其他库。

最后

STL 是可扩展的,可以编写适用于现有算法和容器的容器和算法。如果 STL 没有提供需要的内容,可以考虑编写兼容 STL 的代码。

猜你喜欢

转载自blog.csdn.net/guyu2019/article/details/88374194