C++标准库
阅读数:200 评论数:0
跳转到新版页面分类
C/C++
正文
1、转换操作
(1)static_cast
将一个值以符合逻辑的方式转型。这个可以看作是“利用原值重建一个临时对象,并在设立初值时使用型别转换”。唯有当上述型别转换有所定义时,整个转换才会成功。
如:
float x;
cout<<static_cast<int>(x);
f(static_cast<string>("hello"));//call f() for string instead of char*
(2)dynamic_cast
将多态型别向下转型为其实际静态型别。
(3)const_cast
设定或去除型别的常数性,亦可去除volatile饰词。除此之外不允许任何转换。
(4)reinterpret_cast
此操作符的行为由实际编译器定义。可能重新解释bits意义,但也不一定如此。使用此一转型动作通常带来不可移植性。
2、一般概念
(1)头文件
标准头文件的#include
#include <iostream>
#include <string>
对于c标准头文件
#include <cstdlib>
#include <cstring>
在这些头文件中,每一个标识符都被声明于namespace std中。
为了向下兼容于C,旧式的C标准头文件仍然有效,如果需要,你还是可以使用它们,如:#include <stdlib.h>
3、通用工具
(1)pair
pair可以将两个值视为一个单元。
pair被定义为struct,而不是class,这么一来,所有成员都是public,我们因此可以直接存取pair中的个别值。
C++标准程序库大量运用了pair,例如map和multimpa容器的元素型别便是pair,也就是一组key/value。C++标准程序库中凡是“必须返回两个值”的函数,也都会利用pair。
(2)auto_ptr
auto_ptr是一种智能指针,是针对某个特定问题而设计的。由于智能型 指针本身就是区域变量,所以无论是正常退出,还是异常退出,只要函数即出,它就一定会被销毁。
注意,auto_ptr<>不允许你使用一般指针惯用的赋值初始化方式。你必须直接使用数值完成初始化。如下:
std:auto_ptr<ClassA> ptr1(new ClassA);//OK
std::auto_ptr<ClassA> ptr2 = new ClassA; //ERROR
auto_ptr拥有权的转移
auto_ptr的copy构造函数和assignment操作符将对象拥有权交出去。
注意事项:
A、auto_ptrs之间有能共享拥有权
一个auto_ptr不要指向另一个auto_ptr所拥有的对象。否则,当第一个指针删除该对象扣,另一个指针突然指向了一个已被销毁的对象。
B、并不存在针对array而设计的auto_ptrs
auto_ptr不可以指向array,因为auto_ptr是透过delete而不是delete[]来释放所拥有的对象。
C、auto_ptr不是一个四海通用的智能指针
它只是智能指针中的一种
D、auto_ptrs不满足STL容器对其元素的要求
因为在copy和assign动作后,原本的auto_ptr和新产生的auto_ptr并不相等。
(3)数值极限
数值型别的极值是一个与平台相关的特性,C++标准程序库通过template numeric_limits提供这些值,取代传统C语言所采用的预处理器常数。
C++ Standard规定了各种型别必须保证的最小精度
型别 | 最小长度 |
---|---|
char | 1byte |
short int | 2bytes |
int | 2bytes |
long int | 4bytes |
float | 4bytes |
double | 8bytes |
long double | 8byte |
(4)辅助函数
算法程序库(定义于头文件<algorithm>)内含三个辅助函数,一个用来在两值之中选择较大者,另一个用来在两值之中挑选较小者,第三个用来交换两值。
(5)<cstddef>和<cstdlib>
头文件<cstddef>和<cstdlib>和其C对应版本兼容,在C++程序中经常用到。它们是C头文件<stddef.h>和<stdlib.h>的较新版本,定义了一些常用的常数、宏、型别和函数。
4、Standard Template Library
STL是一个泛型程序库。STL组件中最关键的是容器、迭代器和算法。
(1)容器
可分为两类:序列式容器和关联式容器。序列式容器是每个元素与插入位置有关,包括vector,deque,list。关联容器中元素的位置与插入的位置无关,它对插入的元素进行自动排序,包括set,multiset,map,multimap。
严格说来,C++ Standard并未定义某一种容器的具体实现。然而C++Standard所规定的行为和其对复杂度的要求,让库作者没有太多变化余地。所以实际上各个实作版本之间只在细节上有所差异。
Vector
它是一个dynamic arrya,允许随机存取,在其尾部附加元素或移除元素均非常快速,但是在中部和头部安插元素就比较费时。push_back()函数可为容器附加元素,所有序列式容器者提供这个函数,也提供insert()这个成员函数。size()成员函数返回容器中元素个数,所有容器类别都提供这个函数。
deque
是“double-ended queue”的缩写。它是一个dynamic array,允许随机存储,可以向两端发展,因此不论在尾部或头部安插元素都十分迅速,而在中部部分安插元素则比较费时。除了可以使用push_back()函数外,还可使用push_front()函数从头部安插元素。
list
list不支持随机存取,其优势是在任何位置上安插元素和删除元素都非常快速,因为只须改变链接就行。成员函数empty()的返回值告诉我们容器中是否还有元素,front()函数会返回第一个元素,pop_front()会删除第一个元素。
set
其中每个元素只能出现一次,不允许重复。
multiset
与set相同,只不过它允许重复元素。
map
由键-值对组成元素,每一个键只能出现一次。
multimap
和map相同,但允许重复元素。
实际产品中,所有的这些关联容器通常都由二叉树实作而成。
stack
对元素采取后进先出管理策略
queue
对元素采用先进先出管理策略
priority queue
容器中的元素可以拥有不同的优先权,其效果相当于这样一个buffer:下一元素永远是queue中优先级最高的元素。如果同时有多个元素具备最高优先权,则其次序无明确定义。
(2)迭代器
迭代器是一个“可遍历STL容器内全部或部分元素”,所有容器类别提供有一些函数,使们们得以获取迭代:
A、begin(),返回一个迭代器,指向容器中第一个元素的位置(如果有的话)
B、end(),指向容器中最后一个元素之后。
采用这种半开区间的好处是不必对空区间采取特殊处理手法,空区间的begin()就等于end()。
任何一种容器都定义有两种迭代器型别:
A、container::iterator,“读写模式”遍历元素
B、container::const_iterator,“只读模式”遍历元素
(3)算法
算法并非容器类别的成员函数,而是一种搭配迭代器使用的全局函数。这么做有一个重要优势:所有算法只需实作出一份,就可以对所有容器动作,不必为每一种容器定制。这不是面向对象编程的思想,而是泛型编程的思想。
(4)iterator adpater
迭代器是一个纯粹的抽象概念:任何东西,只要其行为类似迭代器,它就是一个迭代器。C++标准库提供了数个预先定义的特殊迭代器,亦即迭代器适配器。
insert iterator
可以使算法以安插方式而非overwrite方式动作。back inserter内部调用push_back(),在容器尾端插入元素,当然只有在提供有push_back()成员函数的容器中,back inserter才能派上用场,这样的容器有vector,deque,list。front inserter内部调用push_front(),将元素安插于容器最前端,这样的容器有deque和list。general inserter的作用是将元素插入“初始化进接受第二参数”所指位置的前方。内部调用insert(),所有STL容器都提供有insert()函数。
stream iterator
这是一种用来读写stream的迭代器。istream_iterator<>(),ostream_iterator<>()
reverse iterator
它将increment运算转换为decrement运算,所有容器都可以通过成员函数rbegin()和rend()产生reverse iterators
(5)manupulating algorithms
算法remove自某个区间删除元素,它只是从逻辑上使某个元素不属于群集。如果想真正的删除元素,可以使用erase()函数。如:
coll.erase(remove(coll.begin,coll.end(),3),coll.end);
对于manipulating algorithms不能用于关联式容器,原因很简单:如果更易型算法用于关联式容器身上,会改变某位置上的值,进而破坏其已序特性,那就推翻了关联式容器的基本原则。但是可以通过它们的成员函数实现相同功能的操作。
(6)函数对象
只要其行为像函数,它就可以看做是一个函数。所谓函数行为,是指可以使用小括号传递参数,藉以调用 某个东西。那么函数对象就类内部定义了operator(),并给予合适的参数型别。
C++标准库包含了预选定义的函数对象,涵盖了许多基础的运算。如less<>、greater<>,negate<>,multiplies<>
(7)容器内的元素
STL容器元素必须满足以下三个基本条件:
A、必须可透过copy构造函数进行复制。
B、必须可以透过assiagnment操作符完成赋值操作。
C、必须可以透过析构函数完成销毁动作。
下面几个条件也应该满足:
A、对序列式容器而言,无素的default构造函数必须可用
B、对于某些动作,必须定义operator==以执行排序准则
C、在关联容器中,元素必须定义出排序准则
(8)错误处理
STL的设计原则是效率优先,安全次之。
5、STL容器详解
(1)vector
c.at(idx),返回idx所标示的元素。如果idx越界,抛出out_of_range.
c[idx],返回idx所标示的元素,不进行范围检查
c.front(),返回第一个元素,不检查第一个元素是否存在
c.back(),返回最后一个元素,不检查最后一个元素是否存在
C++标准程序库专门针对元素型别为bool的vector设计了一个特殊版本,目的是获取一个优化的vector。
(2)Deque
deque不提供容量操作(capacity()和reserve())
deque直接提供函数,用以完成头部元素的安插和删除(push_front()和pop_front())
(3)list
list对于异常有这样的处理方式:要么操作成功,要么什么也不发生。
由于不支持随机存取,list既不提供subscript操作符,也不提供at()
list并未提供容量、空间重新分配等操作函数,因为全无必要。每个元素都有自己的内存,在被删除之前一直有效
list提供了不少特殊的成员函数,专门用于移动元素。较之同名的STL通用算法,这些函数执行起来更快。
操作 | 效果 |
---|---|
c.unique() | 如果存在若干相邻而数值相等的元素,就移除重复元素,只留下一个 |
c.unique(op) | 如果存在若干相邻元素,都使op()的结果为true,则移除重复元素,只留下一个 |
c1.splice(pos,c2) | 将c2内的所有元素移到c1之内、迭代器pos之前 |
c1.splice(pos,c2,c2pos) | 将c2内的c2pos所指元素移到c1内的pos所指位置上(c1和c2可相同) |
c1.splice(pos,c2,c2beg,c2end) | 将c2内的[c2beg;c2end]区间内所有元素转移到c1内的pos之前(c1和c2可以相同) |
c.sort() | 以operator<为准则,对所有元素排序 |
c.sort(op) | 以op()为准则,对所有元素排序 |
c1.merge(c2) | 将c2的全部元素转移到c1,并保证合并后的list仍有序 |
c1.merge(c2,op) | |
c.reverse() | 反序 |
(4)set和multiset
二者通常以平衡二叉权完成。其自动排序的主要优点在于使二叉权于搜索元素进具有良好性能。但是如果要改变元素值,必须先删除旧元素,再插入新元素。它们都提供特殊的搜索函数
操作 | 效果 |
---|---|
count(elem) | 返回“元素为elem”的元素个数 |
find(elem) | 返回“元素值为elem”的第一个元素,如果找不到就返回end() |
lower_bound(elem) | 元素值>=elem的第一个元素位置 |
upper_bound(elem) | 元素值<elem最后一个元素位置 |
equal_range(elem) | 是lower_bound和upper_bound结果的pair |
(5)map和multimap
m[key],map可以通过这种方式直接对元素进行存取。
6、函数对象详解
预定义函数对象 | 效果 |
---|---|
negate<>() | -param |
plus<>() | param1+param2 |
minus<>() | param1-param2 |
multiplies<>() | param1*param2 |
divides<>() | param1/param2 |
modulus<>() | param1%param2 |
equal_to<>() | param1==param2 |
not_equal_to<>() | param1!=param2 |
less<>() | param1<param2 |
greater<>() | param1>param2 |
less_equal<>() | param1<=param2 |
greater_equal<>() | param1>=param2 |
logical_not<>() | !param |
logical_and<>() | param1&¶m2 |
logical_or<>() | param1||param2 |
所谓函数配接器是指能够将函数对象和另一个函数对象结合起来的函数对象。
表达式 | 效果 |
---|---|
bind1st(op,value) | op(value,param) |
bind2nd(op,value) | op(value,param) |
not1(op) | !op(param) |
not2(op) | !op(param1,param2) |
针对成员函数可以使用
mem_fun_ref(op),调用op,那是某对象的一个const成员函数
mem_fun(op),调用op,那是某对象指针的一个const成员函数
针对一函数,可以使用ptr_fun
7、STL算法
为了让人顾名思义,STL设计者为算法命名时,引入两个特别的尾词:
(1)_if
无尾词的那个要求传递数值,有尾词的那个要求传递函数或函数对象。如,find()用来搜索具有某个值的元素,而find_if()接收一个被当做寻找准则的函数或函数对象,并搜索第一个满足该准则的元素。
(2)_copy
元素不光被操作,还会被复制到目标区间。如,reverse()将区间中的元素颠倒次序,而reverse_copy()则是逆序后将元素复制到另一个区间。
(3)非变动性算法
非变动性算法既不改动元素次序,也不改动元素值。可以用于所有标准容器上。
名称 | 作用 |
---|---|
for_each() | 对每个元素执行操作 |
count() | 返回元素个数 |
count_if() | |
min_element() | 返回最小值元素(以一个迭代器表示) |
max_element() | |
find() | 搜索等于某值的第一个元素 |
find_if() | |
search_n() | 搜索具有某特性的第一段“n个连续元素” |
search() | 搜索某个子区间第一次出现位置 |
find_end() | 搜索某个子区间最后一次出现位置 |
find_first_of() | 搜索等于“某数个值之一”的第一个元素 |
adjacent_find() | 搜索连续两个相等的元素 |
equal() | 判断两区间是否相等 |
mismatch() | 返回两个序列的各组对应元素中,第一对不相等元素 |
lexicographical_compare() | 判断某一序列在“字典序”下是否小于另一个序列 |
(4)变动性算法
名称 | 效果 |
---|---|
for_each() | 针对每个元素执行某项操作 |
copy() | 从第一个元素开始,复制某段区间 |
copy_backward() | 从最后一个元素开始,复制某段区间 |
transform() | 变动(并复制)元素,将两个区间的元素合并 |
merge() | 合并两个区间 |
swap_rangers() | 交换两区间内的元素 |
fill() | 以给定值替换每一个元素 |
fill_n() | 以给定值替换n个元素 |
generate() | 以某些操作的结果替换每一个元素 |
generate_n() | |
replace() | |
replace_if() | |
replace_copy() | |
replace_copy_if() |
(5)排序算法
名称 | 效果 |
---|---|
sort() | 对所有元素排序,内部使用quicksort,复杂度nlogn,但最差情况下也可能具有 非常差的性能(二次复杂度) |
stable_sort() | 对所有元素排序,并操持相等元素间的相对次序。内部采用mergesort,在内存 足够情况下复杂度是nlogn,否则其复杂度是nlognlogn |
partial_sort() | 直到前n个元素就位。内部采用heapsort,复杂度是nlogn,但大多数情况下,heapsort 比quicksort慢2~5倍 |
partial_sort_copy() | |
nth_element() | 作用为求第n小的元素 |
partition() | 根据参数进行分组,quicksort中有使用 |
stable_partition() | |
make_heap() | |
push_heap() | |
pop_heap() | |
sort_heap() |
和变动性算法的情况一样,关联式容器不可作为排序算法的目标。因为关联式容器的元素被视为常数,不可改动。
由于list没有提供随机存取迭代器,所以不可以对它使用排序算法。然而list自身提供了一个成员函数sort(),可用来对其元素排序。
(6)已序区间算法
名称 | 效果 |
---|---|
binary_search() | |
include() | 判断某区间内的每一个元素是否都涵盖于另一区间中 |
lower_bound() | 搜索第一个“大于等于给定值”的元素 |
upper_bound() | 搜索第一个“大于给定值”的元素 |
equal_range() | 返回“等于给定值”的所有元素构成的区间 |
merge() | 将两个区间的元素合并 |
set_union() | 求两个区间的并集 |
set_intersection() | 求两个区间的交集 |
set_difference() | 求位于第一个区间但不位于第二区间的所有元素 |
set_symmetric_difference() | 找出只出现于两个区间之一的所有元素 |
inplace_merge() |
8、特殊容器
(1)stack
内部存放元素所用的实际容器,缺省采用deque。之所以选择deque而非vector,是因为deque移除元素时会释放内存,并且不必在重新分配时复制全部元素。
三个核心成员函数:
A、push() 元素入栈
B、top()返回下一个元素,但是并不移除它
C、pop()元素出栈
如果stack内没有元素,则执行top()和pop()会导制未定义的行为,你可以采用成员函数size()和empty()来检验容器是否为空。
(2)queue
核心成员函数:
A、push()会将一个元素置于queue中
B、front()会返回queue内的下一个元素
C、back()会返回queue中最后一个元素
D、pop()会从queue中移除一个元素
(3)priority queue
由于priority queue需要用到STL heap算法,所以其内部容器必须支持随机存取迭代器。
核心成员函数:
A、push()会将一个元素置入priority queue中
B、top()会返回priority queue中的“下一个元素”
C、pop()会从priority queue中移除一个元素
(4)bitset
内含bit或bool值且大小固定的array。如果你需要一个可变长度的位容器,可考虑使用vector<bool>
9、数值
C++标准程序库提供了一个template class complex<>用于操作复数。运用辅助函数polar(),可以采用极坐标(距原点距离和弧度相位角)来对一个复数进行初始化。
表达式 | 效果 |
---|---|
c.real() | 返回实部值 |
real(c) | |
c.imag() | 返回虚部值 |
imag() | |
abs(c) | 返回c的绝对值 |
norm(c) | 返回c绝对值的平方 |
arg(c) | 返回c的极坐标相位角(相当于atan2(c.imag(),c.real())) |
valarray可作为向量和矩阵运算的基础。
10、国际化
国际化问题的主要思路是以locale对象来代表一个可扩展的面貌集合,以此进行地区转换工作。
(1)不同的字符编码
这个问题主要出现在亚洲,例如汉字至少有Big5和GB两种编码。面对多于256个字符集,有两种不同的解决方案:多字节表示法和宽字节表示法,在多字符表示法中,字符所用的bytes个数是可变的;宽字符表示法中所用的bytes数目恒定的。
(2)locales
在国际化背景中,一个locale就是一个“参数和函数的集合”,用以进行国文化间的转换。不同的浮点数、日期、货币格式则根据这个locale确定。
定义一个locale,需采用以下字符串格式:
language[_area[.code]]
C++ standard并未强制要求支持其它locale,不过通常各个c++实作版本都会支持其它locales.