IO库与STL.
IO库
- istream:输入流类型,提供输入操作。
 - ostream:输出流类型,提供输出操作
 - cin:一个
istream对象,从标准输入读取数据。 - cout:一个
ostream对象,向标准输出写入数据。 - cerr:一个
ostream对象,向标准错误写入消息。 >>运算符:用来从一个istream对象中读取输入数据。<<运算符:用来向一个ostream对象中写入输出数据。- getline函数:从一个给定的
istream对象中读取一行数据,存入到一个给定的string对象中。 
IO类
- iostream头文件:从标准流中读写数据,
istream,ostream,iostream等。 - fstream头文件:从文件中读写数据,
ifstream,ofstream,fstream等。 - sstream头文件:从字符串中读写数据,
istringstream,ostringstream,stringstream等。 
IO对象无拷贝或赋值
- 1.IO对象不能存在容器里.
 - 2.形参和返回类型也不能是流类型。
 - 3.形参和返回类型一般是流的引用。
 - 4.读写一个IO对象会改变其状态,因此传递和返回的引用不能是
const的。 
条件状态
| 状态 | 解释 | 
|---|---|
| strm:iostate | 是一种机器无关的类型,提供了表达条件状态的完整功能 | 
| strm:badbit | 用来指出流已经崩溃 | 
| strm:failbit | 用来指出一个IO操作失败了 | 
| strm:eofbit | 用来指出流到达了文件结束 | 
| strm:goodbit | 用来指出流未处于错误状态,此值保证为零 | 
| s.eof() | 若流s的eofbit置位,则返回true | 
| s.fail() | 若流s的failbit置位,则返回true | 
| s.bad() | 若流s的badbit置位,则返回true | 
| s.good() | 若流s处于有效状态,则返回true | 
| s.clear() | 将流s中所有条件状态位复位,将流的状态设置成有效,返回void | 
| s.clear(flags) | 将流s中指定的条件状态位复位,返回void | 
| s.setstate(flags) | 根据给定的标志位,将流s中对应的条件状态位置位,返回void | 
| s.rdstate() | 返回流s的当前条件状态,返回值类型为strm::iostate | 
上表中,strm是一种IO类型,(如istream), s是一个流对象。
管理输出缓冲
每个输出流都管理一个缓冲区,执行输出的代码,文本串可能立即打印出来,也可能被操作系统保存在缓冲区内,随后再打印。
刷新缓冲区,可以使用如下IO操纵符:
- endl:输出一个换行符并刷新缓冲区。
 - flush:刷新流,单不添加任何字符。
 - ends:在缓冲区插入空字符
null,然后刷新。 - unitbuf:告诉流接下来每次操作之后都要进行一次
flush操作。 - nounitbuf:回到正常的缓冲方式。
 
1 | cout << unitbuf;    //所有输出操作后都会立即刷新缓冲区。 | 
2 | //... 任何输出操作都立即刷新,无缓冲。 | 
3 | cout << nounitbuf;  //回到正常的缓冲方式。 | 
文件输入输出
- ifstream从一个给定文件读取数据。
 - ofstream向一个给定文件写入数据。
 - fstream可以读写给定文件。
 
文件流:需要读写文件时,必须定义自己的文件流对象,并绑定在需要的文件上。
fstream特有的操作
| 操作 | 解释 | 
|---|---|
| fstream fstrm; | 创建一个未绑定的文件流。 | 
| fstream fstrm(s); | 创建一个文件流,并打开名为s的文件,s可以是string也可以是char指针 | 
| fstream fstrm(s, mode); | 与前一个构造函数类似,但按指定mode打开文件 | 
| fstrm.open(s) | 打开名为s的文件,并和fstrm绑定 | 
| fstrm.close() | 关闭和fstrm绑定的文件 | 
| fstrm.is_open() | 返回一个bool值,指出与fstrm关联的文件是否成功打开且尚未关闭 | 
当一个fstream对象被销毁时,close会自动被调用。
文件模式
| 文件模式 | 解释 | 
|---|---|
| in | 以读的方式打开 | 
| out | 以写的方式打开 | 
| app | 每次写操作前均定位到文件末尾 | 
| ate | 打开文件后立即定位到文件末尾 | 
| trunc | 截断文件 | 
| binary | 以二进制方式进行IO操作。 | 
默认情况下,打开一个ofstream时,文件内容会被丢弃。保留被ofstream打开的文件中已有数据的唯一方法是显示指定app或in模式ofstream app("file", ofstream::out | ofstream::app)。
string流
头文件sstream定义了三个类型来支持内存IO:
- istringstream从
string读取数据。 - ostringstream向
string写入数据。 - stringstream可以读写给定
string。 
stringstream特有的操作
| 操作 | 解释 | 
|---|---|
| sstream strm | 定义一个未绑定的stringstream对象 | 
| sstream strm(s) | 用s初始化对象 | 
| strm.str() | 返回strm所保存的string的拷贝 | 
| strm.str(s) | 将s拷贝到strm中,返回void | 
顺序容器
| 类型 | 说明 | 
|---|---|
| vector | 可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。 | 
| deque | 双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。 | 
| list | 双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除速度都很快。 | 
| forward_list | 单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。 | 
| array | 固定大小数组。支持快速随机访问。不能添加或删除元素。 | 
| string | 与vector相似的容器,但专门用于保存字符。随机访问快。在尾部插入/删除速度快。 | 
迭代器
迭代器范围:begin到end,即第一个元素到最后一个元素的后面一个位置(one past the last element)。
左闭合区间:$ [begin, end) $
左闭合范围蕴含的编程设定:
- 如果
begin和end相等,则范围为空。 - 如果二者不等,则范围至少包含一个元素,且
begin指向该范围中的第一个元素。 - 可以对
begin递增若干次,使得begin == end。 
获取迭代器
| 操作 | 解释 | 
|---|---|
| c.begin(), c.end() | 返回指向c的首元素和尾元素之后位置的迭代器 | 
| c.cbegin(), c.cend() | 返回const_iterator | 
| reverse_iterator | 按逆序寻址元素的迭代器 | 
| const_reverse_iterator | 不能修改元素的逆序迭代器 | 
| c.rbegin(), c.rend() | 返回指向c的尾元素和首元素之前位置的迭代器 | 
| c.crbegin(), c.crend() | 返回const_reverse_iterator | 
- 以
c开头的版本是C++11新标准引入的 - 当不需要写访问时,应该使用
cbegin和cend。 - 不支持
forward_list 
在向容器添加元素后:
- 如果容器是
vector或string,且存储空间被重新分配,则指向容器的迭代器、指针、引用都会失效。 - 对于
deque,插入到除首尾位置之外的任何位置都会导致指向容器的迭代器、指针、引用失效。如果在首尾位置添加元素,迭代器会失效,但指向存在元素的引用和指针不会失效。 - 对于
list和forward_list,指向容器的迭代器、指针和引用依然有效。 
在从一个容器中删除元素后:
- 对于
list和forward_list,指向容器其他位置的迭代器、引用和指针仍然有效。 - 对于
deque,如果在首尾之外的任何位置删除元素,那么指向被删除元素外其他元素的迭代器、指针、引用都会失效;如果是删除deque的尾元素,则尾后迭代器会失效,但其他不受影响;如果删除的是deque的头元素,这些也不会受影响。 - 对于
vector和string,指向被删元素之前的迭代器、引用、指针仍然有效。 - 注意:当我们删除元素时,尾后迭代器总是会失效。
 - 注意:使用失效的迭代器、指针、引用是严重的运行时错误!
 - 建议:将要求迭代器必须保持有效的程序片段最小化。
 - 建议:不要保存
end返回的迭代器。 
类型
| 操作 | 解释 | 
|---|---|
| iterator | 此容器类型的迭代器类型 | 
| const_iterator | 可以读取元素但不能修改元素的迭代器类型 | 
| size_type | 无符号整数类型,足够保存此种容器类型最大可能的大小 | 
| difference_type | 带符号整数类型,足够保存两个迭代器之间的距离 | 
| value_type | 元素类型 | 
| reference | 元素的左值类型;和value_type &含义相同 | 
| const_reference | 元素的const左值类型,即const value_type & | 
容器定义和初始化
| 操作 | 解释 | 
|---|---|
| C c; | 默认构造函数,构造空容器 | 
| C c1(c2);或C c1 = c2; | 构造c2的拷贝c1 | 
| C c(b, e) | 构造c,将迭代器b和e指定范围内的所有元素拷贝到c | 
| C c(a, b, c…) | 列表初始化c | 
| C c(n) | 只支持顺序容器,且不包括array,包含n个元素,这些元素进行了值初始化 | 
| C c(n, t) | 包含n个初始值为t的元素 | 
- 只有顺序容器的构造函数才接受大小参数,关联容器并不支持。
 array具有固定大小。- 和其他容器不同,默认构造的
array是非空的。 - 直接复制:将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。
 - 使用迭代器复制:不要求容器类型相同,容器内的元素类型也可以不同。
 
赋值和swap
| 操作 | 解释 | 
|---|---|
| c1 = c2; | 将c1中的元素替换成c2中的元素 | 
| c1 = {a, b, c…} | 将c1中的元素替换成列表中的元素(不适用于array) | 
| c1.swap(c2) | 交换c1和c2的元素 | 
| swap(c1, c2) | 等价于c1.swap(c2) | 
| c.assign(b, e) | 将c中的元素替换成迭代器b和e表示范围中的元素,b和e不能指向c中的元素 | 
| c.assign(il) | 将c中的元素替换成初始化列表il中的元素 | 
| c.assign(n, r) | 将c中的元素替换为n个值是t的元素 | 
- 使用非成员版本的
swap是一个好习惯(非成员版本的swap在泛型编程中非常重要)。 assign操作不适用于关联容器和array,仅顺序容器有效。- 除array外,swap不对任何元素进行拷贝,删除或插入操作。
 
大小
| 操作 | 解释 | 
|---|---|
| c.size() | 容器中元素的数目(不支持forward_list) | 
| c.max_size() | 容器中可保存的最大元素数目 | 
| c.empty() | 若容器中存储了元素,返回false,否则返回true | 
顺序容器添加元素
| 操作 | 解释 | 
|---|---|
| c.push_back(t) | 在容器尾部创建一个值为t的元素,返回void | 
| c.emplace_back(args) | 同上 | 
| c.push_front(t) | 在容器头部创建一个值为t的元素,返回void | 
| c.emplace_front(args) | 同上 | 
| c.insert(p, t) | 在迭代器p指向的元素之前创建一个值是t的元素,返回指向新元素的迭代器 | 
| c.emplace(p, args) | 同上 | 
| c.inset(p, n, t) | 在迭代器p指向的元素之前插入n个值为t的元素,返回指向第一个新元素的迭代器;如果n是0,则返回p | 
| c.insert(p, b, e) | 将迭代器b和e范围内的元素,插入到p指向的元素之前;如果范围为空,则返回p | 
| c.insert(p, il) | il是一个花括号包围中的元素值列表,将其插入到p指向的元素之前;如果il是空,则返回p | 
- 向vector,string或deque插入元素会使所有指向容器的迭代器,引用和指针失效。
 - 因为这些操作会改变大小,因此不适用于
array。 forward_list有自己专有版本的insert和emplace。forward_list不支持push_back和emplace_back。- 当我们用一个对象去初始化容器或者将对象插入到容器时,实际上放入的是对象的拷贝。
 emplace开头的函数是新标准引入的,这些操作是构造而不是拷贝元素。- 传递给
emplace的参数必须和元素类型的构造函数相匹配。 
访问元素
| 操作 | 解释 | 
|---|---|
| c.back() | 返回容器中尾元素的引用。若容器为空,函数行为未定义 | 
| c.front() | 返回容器中头元素的引用。若容器为空,函数行为未定义 | 
| c[n] | 返回容器中下标是n的元素的引用,n时候一个无符号证书。若n>=c.size(),则函数行为未定义 | 
| c.at(n) | 返回下标为n的元素引用。如果下标越界,则抛出out_of_range异常 | 
- 访问成员函数返回的是引用。
 at和下标操作只适用于string、vector、deque、array。back不适用于forward_list。- 如果希望下标是合法的,可以使用
at函数。 
删除元素
| 操作 | 解释 | 
|---|---|
| c.pop_back() | 删除容器中尾元素,若容器为空,则函数行为未定义。函数返回void | 
| c.pop_front() | 删除容器中首元素,若容器为空,则函数行为未定义。函数返回void | 
| c.erase(p) | 删除迭代器p指向的元素,返回一个指向被删除元素之后的元素的迭代器,若p本身是尾后迭代器,则函数行为未定义 | 
| c.erase(b, e) | 删除迭代器b和e范围内的元素,返回指向最后一个被删元素之后元素的迭代器,若e本身就是尾后迭代器,则返回尾后迭代器 | 
| c.clear() | 删除容器中所有元素,返回void | 
- 会改变容器大小,不适用于
array。 forward_list有特殊版本的eraseforward_list不支持pop_backvector和string不支持pop_front
特殊的forwad_list操作
- 链表在删除元素时需要修改前置节点的内容,双向链表会前驱的指针,但是单向链表没有保存,因此需要增加获取前置节点的方法。
 forward_list定义了before_begin,即首前(off-the-begining)迭代器,允许我们再在首元素之前添加或删除元素。
| 操作 | 解释 | 
|---|---|
| lst.before_begin() | 返回指向链表首元素之前不存在的元素的迭代器,此迭代器不能解引用。 | 
| lst.cbefore_begin() | 同上,但是返回的是常量迭代器。 | 
| lst.insert_after(p, t) | 在迭代器p之后插入元素。t是一个对象 | 
| lst.insert_after(p, n, t) | 在迭代器p之后插入元素。t是一个对象,n是数量。若n是0则函数行为未定义 | 
| lst.insert_after(p, b, e) | 在迭代器p之后插入元素。由迭代器b和e指定范围。 | 
| lst.insert_after(p, il) | 在迭代器p之后插入元素。由il指定初始化列表。 | 
| emplace_after(p, args) | 使用args在p之后的位置,创建一个元素,返回一个指向这个新元素的迭代器。若p为尾后迭代器,则函数行为未定义。 | 
| lst.erase_after(p) | 删除p指向位置之后的元素,返回一个指向被删元素之后的元素的迭代器,若p指向lst的尾元素或者是一个尾后迭代器,则函数行为未定义。 | 
| lst.erase_after(b, e) | 类似上面,删除对象换成从b到e指定的范围。 | 
改变容器大小
| 操作 | 解释 | 
|---|---|
| c.resize(n) | 调整容器的大小为n个元素,若n<c.size(),则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化 | 
| c.resize(n, t) | 调整容器的大小为n个元素,任何新添加的元素都初始化为值t | 
vector分配原理
vector和string在内存中是连续保存的,如果原先分配的内存位置已经使用完,则需要重新分配新空间,将已有元素从就位置移动到新空间中,然后添加新元素。
| 操作 | 解释 | 
|---|---|
| c.shrink_to_fit() | 将capacity()减少到和size()相同大小 | 
| c.capacity() | 不重新分配内存空间的话,c可以保存多少个元素 | 
| c.reverse(n) | 分配至少能容纳n个元素的内存空间 | 
- shrink_to_fit只适用于vector、string和deque。
 - capacity和reverse只适用于vector和string。
 
额外的string操作
构造string的其他方法
| 操作 | 解释 | 
|---|---|
| string s(cp, n) | s是cp指向的数组中前n个字符的拷贝,此数组 | 
| string s(s2, pos2) | s是string s2从下标pos2开始的字符的拷贝。若pos2 > s2.size(),则构造函数的行为未定义。 | 
| string s(s2, pos2, len2) | s是string s2从下标pos2开始的len2个字符的拷贝。 | 
- n,len,pos2都是无符号值。
 
substr操作
| 操作 | 解释 | 
|---|---|
| s.substr(pos, n) | 返回一个string,包含s中从pos开始的n个字符的拷贝。pos的默认值是0,n的默认值是s.size() - pos,即拷贝从pos开始的所有字符。 | 
改变string的其他方法
| 操作 | 解释 | 
|---|---|
| s.insert(pos, args) | 在pos之前插入args指定的字符。pos可以使是下标或者迭代器。接受下标的版本返回指向s的引用;接受迭代器的版本返回指向第一个插入字符的迭代器。 | 
| s.erase(pos, len) | 删除从pos开始的len个字符,如果len被省略,则删除后面所有字符,返回指向s的引用。 | 
| s.assign(args) | 将s中的字符替换成args指定的字符。返回一个指向s的引用。 | 
| s.append(args) | 将args指定的字符追加到s,返回一个指向s的引用。 | 
| s.replace(range, args) | 删除s中范围range中的字符,替换成args指定的字符。返回一个指向s的引用。 | 
string搜索操作
string类提供了6个不同的搜索函数,每个函数都有4个重载版本。- 每个搜索操作都返回一个
string::size_type值,表示匹配发生位置的下标。如果搜索失败则返回一个名为string::npos的static成员(类型是string::size_type,初始化值是-1,也就是string最大的可能大小)。 
| 搜索操作 | 解释 | 
|---|---|
| s.find(args) | 查找容器中args第一次出现的位置 | 
| s.rfind(args) | 查找容器中args最后一次出现的位置 | 
| s.find_first_of(args) | 在容器中查找args中任何一个字符第一次出现的位置 | 
| s.find_last_of(args) | 在容器中查找args中任何一个字符最后一次出现的位置 | 
| s.find_first_not_of(args) | 在容器中查找第一个不在args中的字符 | 
| s.find_first_not_of(args) | 在容器中查找最后一个不在args中的字符 | 
args必须是一下的形式之一:
args形式 | 
解释 | 
|---|---|
| c, pos | 从容器中位置pos开始查找字符c。pos默认是0 | 
| s2, pos | 从容器中位置pos开始查找字符串s。pos默认是0 | 
| cp, pos | 从容器中位置pos开始查找指针cp指向的以空字符结尾的C风格字符串。pos默认是0 | 
| cp, pos, n | 从容器中位置pos开始查找指针cp指向的前n个字符。pos和n无默认值。 | 
s.compare的几种参数形式
逻辑类似于C标准库的strcmp函数,根据s是等于、大于还是小于参数指定的字符串,s.compare返回0、正数或负数。
| 参数形式 | 解释 | 
|---|---|
| s2 | 比较s和s2 | 
| pos1, n1, s2 | 比较s从pos1开始的n1个字符和s2 | 
| pos1, n1, s2, pos2, n2 | 比较s从pos1开始的n1个字符和s2 | 
| cp | 比较s和cp指向的以空字符结尾的字符数组 | 
| pos1, n1, cp | 比较s从pos1开始的n1个字符和cp指向的以空字符结尾的字符数组 | 
| pos1, n1, cp, n2 | 比较s从pos1开始的n1个字符和cp指向的地址开始n2个字符 | 
string和数值转换
| 转换 | 解释 | 
|---|---|
| to_string(val) | 一组重载函数,返回数值val的string表示。val可以使任何算术类型。对每个浮点类型和int或更大的整型,都有相应版本的to_string()。和往常一样,小整型会被提升。 | 
| stoi(s, p, b) | 返回s起始子串(表示整数内容)的数值,p是s中第一个非数值字符的下标,默认是0,b是转换所用的基数。返回int | 
| stol(s, p, b) | 返回long | 
| stoul(s, p, b) | 返回unsigned long | 
| stoll(s, p, b) | 返回long long | 
| stoull(s, p, b) | 返回unsigned long long | 
| stof(s, p) | 返回s起始子串(表示浮点数内容)的数值,p是s中第一个非数值字符的下标,默认是0。返回float | 
| stod(s, p) | 返回double | 
| stold(s, p) | 返回long double | 
容器适配器(adapter)
- 适配器是使一事物的行为类似于另一事物的行为的一种机制,例如
stack可以使任何一种顺序容器以栈的方式工作。 - 初始化 
deque<int> deq; stack<int> stk(deq);从deq拷贝元素到stk。 - 创建适配器时,指定一个顺序容器,可以覆盖默认的基础容器: 
stack<string, vector<string> > str_stk;。 
适配器的通用操作和类型
| 操作 | 解释 | 
|---|---|
| size_type | 一种类型,须以保存当前类型的最大对象的大小 | 
| value_type | 元素类型 | 
| container_type | 实现适配器的底层容器类型 | 
| A a; | 创建一个名为a的空适配器 | 
| A a(c) | 创建一个名为a的适配器,带有容器c的一个拷贝 | 
| 关系运算符 | 每个适配器都支持所有关系运算符:==、!=、<、 <=、>、>=这些运算符返回底层容器的比较结果 | 
| a.empty() | 若a包含任何元素,返回false;否则返回true | 
| a.size() | 返回a中的元素数目 | 
| swap(a, b) | 交换a和b的内容,a和b必须有相同类型,包括底层容器类型也必须相同 | 
| a.swap(b) | 同上 | 
stack
| 操作 | 解释 | 
|---|---|
| s.pop() | 删除栈顶元素,不返回。 | 
| s.push(item) | 创建一个新元素,压入栈顶,该元素通过拷贝或移动item而来 | 
| s.emplace(args) | 同上,但元素由args来构造。 | 
| s.top() | 返回栈顶元素,不删除。 | 
- 定义在
stack头文件中。 stack默认基于deque实现,也可以在list或vector之上实现。
queue和priority_queue
| 操作 | 解释 | 
|---|---|
| q.pop() | 删除队首元素,但不返回。 | 
| q.front() | 返回队首元素的值,不删除。 | 
| q.back() | 返回队尾元素的值,不删除。只适用于queue | 
| q.top() | 返回具有最高优先级的元素值,不删除。 | 
| q.push(item) | 在队尾压入一个新元素。 | 
| q.emplace(args) | 
- 定义在
queue头文件中。 queue默认基于deque实现,priority_queue默认基于vector实现。queue可以在list或vector之上实现,priority_queue也可以用deque实现。
泛型算法
因为它们实现共同的操作,所以称之为“算法”;而“泛型”、指的是它们可以操作在多种容器类型上。
泛型算法本身不执行容器操作,只是单独依赖迭代器和迭代器操作实现。
1 |  | 
2 |  | 
大多数算法是通过遍历两个迭代器标记的一段元素来实现其功能。
必要的编程假定:算法永远不会改变底层容器的大小。算法可能改变容器中保存的元素的值,也可能在容器内移动元素,但不能直接添加或者删除元素。
1 | vector<int>::const_iterator result = find(vec.begin(), vec.end(), search_value); | 
输入:两个标记范围的迭代器和目标查找值。
返回:如果找到,返回对应的迭代器,否则返回第二个参数,即标记结尾的迭代器。
只读算法
- 只读取范围中的元素,不改变元素。
 - 如 
find和accumulate(在numeric中定义,求和)。 find_first_of,输入:两对迭代器标记两段范围,在第一段中找第二段中任意元素,返回第一个匹配的元素,找不到返回第一段的end迭代器。- 通常最好使用
cbegin和cend。 equal:确定两个序列是否保存相同的值。
写容器元素的算法
一些算法将新值赋予序列中的元素。算法不检查写操作(假定目的位置足够大)。
1 | fill(vec.begin(), vec.end(), 0);    //将每个元素重置为0 | 
2 | fill_n(vec.begin(), 10, 0); | 
插入迭代器back_inserter,接受一个指向容器的引用,返回一个与该容器绑定的插入迭代器。通过此迭代器赋值时,赋值运算符会调用push_back将一个具有给定值的元素添加到容器中。
1 |  | 
2 | |
3 | vector<int> vec; | 
4 | auto it = back_inserter(vec); | 
5 | *it = 42; | 
6 | fill_n(back_inserter(vec), 10, 0); | 
- 拷贝算法
copy: - 输入:前两个参数指定输入范围,第三个指向目标序列。
 copy (ilst.begin(), ilst.end(), back_inserter(ivec));copy时必须保证目标目的序列至少要包含与输入序列一样多的元素。
重排容器元素的算法
排序算法sort,接受两个迭代器,表示要排序的元素范围。
消除重复unique:
- 之前要先调用
sort - 返回的迭代器指向最后一个不重复元素之后的位置。
 - 顺序会变,重复的元素被“删除”。
 - 并没有真正删除,真正删除必须使用容器操作。
 
1 | void elimDups(vector<string> &words) | 
2 | { | 
3 |     sort(words.begin(), words.end()); | 
4 |     auto end_unique = unique(words.begin(), words.end()); | 
5 |     words.erase(end_unique, words.end()); | 
6 | } | 
定制操作
向算法传递函数:
谓词(predicate)是一个可调用的表达式,返回结果是一个能用作条件的值。一元谓词接受一个参数,二元谓词接受两个参数。
1 | bool isShorter(const string &s1, const string &s2) | 
2 | { | 
3 |     return s1.size() < s2.size(); | 
4 | } | 
5 | stable_sort(words.begin(), words.end(), isShorter); | 
lambda表达式
lambda表达式表示一个可调用的代码单元,可以理解成是一个未命名的内联函数。
1 | [capture list](parameter list) -> return type {function body} | 
- capture list捕获列表是一个lambda所在函数定义的局部变量的列表(通常为空),不可忽略。
 - return type是返回类型,lambda必须使用尾置返回形式,可忽略。
 - parameter是参数列表,可忽略。
 - function body是函数体,不可忽略。
 - 可以忽略参数列表和返回类型,但必须包含捕获列表和函数体。
 
1 | auto f = [] {return 42;} | 
2 | cout << f() <<endl; | 
lambda不能有默认参数,实参与形参类型必须匹配且数目相等。
lambda只有在其捕获列表中捕获一个它所在函数中的局部变量,才能在lambda函数体中使用该变量。
捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和在它所在函数之外声明的名字。
1 | void biggies(vector<string> &words, | 
2 |                 vector<string>::size_type sz) | 
3 | { | 
4 |     elimDups(words); | 
5 |     stable_sort(words.begin(), words.end(), | 
6 |             [](const string &a, const string &b) | 
7 |                 { return a.size() < b.size(); }); | 
8 | |
9 |     auto wc = find_if(words.begin(), words.end(), | 
10 |             [sz](const string &a) | 
11 |                 { return a.size() >= sz; }); | 
12 | |
13 |     auto count = words.end() - wc; | 
14 |     cout << count << " " << make_plural(count, "word", "s") | 
15 |         << " of length " << sz << " or longer" << endl; | 
16 | |
17 |     for_each(wc, words.end(), | 
18 |             [](const string &s) {const << s << " ";}); | 
19 |     cout << endl; | 
20 | } | 
lambda捕获和返回
定义lambda时,编译器会生成一个与lambda对应的新的(未命名的)类类型。当向一个函数传递一个lambda时,同时定义了一个新类型和该类型的一个对象:传递的参数就是此编译器生成的类类型的未命名对象。
默认情况下,从lambda生成的类都包含一个对应该lambda所捕获的变量的数据成员,在lambda对象创建时被初始化。
值捕获:被捕获的变量是可以拷贝,且被捕获的变量是在lambda创建时拷贝,而不是调用时拷贝。
1 | void fcn1() | 
2 | { | 
3 |     size_t v1 = 42; //局部变量 | 
4 |     auto f = [v1] { return v1; }; | 
5 |     v1 = 0; | 
6 |     auto j = f();   //j为42,f保存了我们创建它时v1的拷贝 | 
7 | } | 
引用捕获:必须保证在lambda执行时,变量是存在的,当函数返回一个lambda,此lambda也不能包含引用捕获。
1 | void fcn2() | 
2 | { | 
3 |     size_t v1 = 42; | 
4 |     auto f2 = [&v1] {return v1;}; | 
5 |     v1 = 0; | 
6 |     auto j = f2(); // j为0 | 
7 | } | 
尽量减少捕获的数据量,尽可能避免捕获指针或引用。
隐式捕获:让编译器推断捕获列表,在捕获列表中写一个&(引用捕获方式)或=(值捕获方式), 也可以混合使用隐式捕获和显示捕获。
1 | void biggies(vector<string> &words, vector<string>::size_type sz, | 
2 |         ostream &os = cout, char c = ' ') | 
3 | { | 
4 |     for_each(words.begin(), words.end(), | 
5 |         [&, c](const string &s) { os << s << c; }); | 
6 | |
7 |     for_each(words.begin(), words.end(), | 
8 |         [=, &os](const string &s) { os << s << c; }); | 
9 | } | 
当混合使用隐式和显示捕获时,捕获列表中的第一个元素必须是&或=。此符号指定了默认捕获方式。
lambda捕获列表:
| 捕获列表 | 解释 | 
|---|---|
| [] | 空捕获列表。lambda不能使用所在函数中的变量。一个lambda只有在捕获变量后才能使用它们。 | 
| [names] | names是一个逗号分隔的名字列表,这些名字都是在lambda所在函数的局部变量,捕获列表中的变量都被拷贝,名字前如果使用了&,则采用引用捕获方式。 | 
| [&] | 隐式捕获列表,采用引用捕获方式。lambda体中所使用的来自所在函数的实体都采用引用方式使用。 | 
| [=] | 隐式捕获列表,采用值捕获方式。 | 
| [&, identifier_list] | identifier_list是一个逗号分隔的列表,包含0个或多个来自所在函数的变量。这些变量采用值捕获方式,而任何隐式捕获的变量都采用引用方式捕获。identifier_list中的名字前面不能使用& | 
| [=, identifier_list] | identifier_list中的变量采用引用方式捕获,而任何隐式捕获的变量都采用值方式捕获。identifier_list中的名字不能包括this,且前面必须使用& | 
默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。如果我们希望能改变一个被捕获的变量的值,就必须在参数列表首加上关键字mutable。因此,可变lambda能省略参数列表。
1 | void fcn3() | 
2 | { | 
3 |     size_t v1 = 42; | 
4 |     auto f = [v1] () mutable { return ++v1; }; | 
5 |     v1 = 0; | 
6 |     auto j = f(); //j==43 | 
7 | } | 
默认情况下,如果一个lambda体包含return之外的任何语句,则编译器假定此lambda返回void。被推断为void的lambda不能返回值。当我们需要为一个lambda定义返回类型时,必须使用尾置返回类型。
1 | transform(v1.begin(), v1.end(), v1.begin(), | 
2 |         [](int i) ->int { if(i < 0) return -i; else return i; }); | 
参数绑定
lambda表达式更适合在一两个地方使用的简单操作。如果是很多地方使用相同的操作,还是需要定义函数。
标准库bind函数:定义在头文件functional中,可以看做为一个通用的函数适配器。接受一个可调用对象,生成一个新的可调用对象来”适应”原对象的参数列表。
1 | auto newCallable = bind(callable, arg_list); | 
调用newCallable的时候,newCallable会调用callable并传递给它arg_list中的参数。arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数表示占位符的意思, 表示newCallable的参数,占据了传递给newCallable的参数的”位置”。数值n表示生成的可调用对象中参数的位置: _1为newCallable的第一个参数, _2为第二个参数,以此类推。
名字_n定义在placeholders的命名空间中。
1 | using namespace std::placeholders; | 
1 | auto g = bind(f, a, b, _2, c, _1); | 
2 | //g(_1, _2) = f(a, b, _2, c, _1) | 
非占位符的参数要使用引用传参,必须使用标准库ref函数或者cref(const引用类)函数。
1 | for_each(words.begin(), words.end(), | 
2 |         bind(print, ref(os), _1, ' ')); | 
再探迭代器
插入迭代器
插入器是一种迭代器适配器,接受一个容器,生成一个迭代器,能实现向给定容器添加元素。
- back_inserter创建一个使用
push_back的迭代器。 - front_inserter创建一个使用
push_front的迭代器。 - inserter创建一个使用
insert的迭代器。接受第二个参数,即一个指向给定容器的迭代器,元素会被查到迭代器所指向的元素之前。 
插入迭代器操作
| 操作 | 解释 | 
|---|---|
| it=t | 在it指定的当前位置插入值t。假定c是it绑定的容器,依赖于插入迭代器的不同种类,此赋值会分别调用c.push_back(t)、c.push_front(t)、c.insert(t, p),其中p是传递给inserter的迭代器位置 | 
| *it, ++it, it++ | 这些操作虽然存在,但不会对it做任何事情,每个操作都返回it | 
iostream迭代器
迭代器可与输入或输出流绑定在一起,用于迭代遍历所关联的 IO 流。通过使用流迭代器,我们可以用泛型算法从流对象中读取数据以及向其写入数据。
istream_iterator的操作
| 操作 | 解释 | 
|---|---|
| istream_iterator | 
in从输入流is读取类型为T的值 | 
| istream_iterator | 
读取类型是T的值的istream_iterator迭代器,表示尾后位置 | 
| in1 == in2 | in1和in2必须读取相同类型。如果他们都是尾后迭代器,或绑定到相同的输入,则两者相等。 | 
| in1 != in2 | 类似上条 | 
| *in | 返回从流中读取的值 | 
| in->mem | 与*(in).mem含义相同 | 
| ++in, in++ | 使用元素类型所定义的>>运算符从流中读取下一个值。前置版本返回一个指向递增后迭代器的引用,后置版本返回旧值。 | 
ostream_iterator的操作:
| 操作 | 解释 | 
|---|---|
| ostream_iterator | 
out将类型为T的值写到输出流os中 | 
| ostream_iterator | 
out将类型为T的值写到输出流os中,每个值后面都输出一个d。d指向一个空字符结尾的字符数组。 | 
| out = val | 用<<运算符将val写入到out所绑定的ostream中。val的类型必须和out可写的类型兼容。 | 
| *out, ++out, out++ | 这些运算符是存在的,但不对out做任何事情。每个运算符都返回out。 | 
反向迭代器
反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器。
对于反向迭代器,递增和递减的操作含义会颠倒。
实现向后遍历,配合rbegin和rend。
泛型算法结构
5类迭代器
| 迭代器类别 | 解释 | 支持的操作 | 
|---|---|---|
| 输入迭代器 | 只读,不写;单遍扫描,只能递增 | ==,!=,++,*,-> | 
| 输出迭代器 | 只写,不读;单遍扫描,只能递增 | ++,* | 
| 前向迭代器 | 可读写;多遍扫描,只能递增 | ==,!=,++,*,-> | 
| 双向迭代器 | 可读写;多遍扫描,可递增递减 | ==,!=,++,--,*,-> | 
| 随机访问迭代器 | 可读写,多遍扫描,支持全部迭代器运算 | ==,!=,<,<=,>,>=,++,--,+,+=,-,-=,*,->,iter[n]==*(iter[n]) | 
算法的形参模式
1 | alg(beg, end, other args); | 
2 | alg(beg, end, dest, other args); | 
3 | alg(beg, end, beg2, other args); | 
4 | alg(beg, end, beg2, end2, other args); | 
其中,alg是算法名称,beg和end表示算法所操作的输入范围。dest、beg2、end2都是迭代器参数,是否使用要依赖于执行的操作。
算法命名规范
- 一些算法使用重载形式传递一个谓词。
 - 接受一个元素值的算法通常有一个不同名的版本:加
_if,接受一个谓词代替元素值。 - 区分拷贝元素的版本和不拷贝的版本:拷贝版本通常加
_copy。 
特定容器算法
- 对于
list和forward_list,优先使用成员函数版本的算法而不是通用算法。 
list和forward_list成员函数版本的算法
| 操作 | 解释 | 
|---|---|
| lst.merge(lst2) | 将来自lst2的元素合并入lst,二者都必须是有序的,元素将从lst2中删除。 | 
| lst.merge(lst2, comp) | 同上,给定比较操作。 | 
| lst.remove(val) | 调用erase删除掉与给定值相等(==)的每个元素 | 
| lst.remove_if(pred) | 调用erase删除掉令一元谓词为真的每个元素 | 
| lst.reverse() | 反转lst中元素的顺序 | 
| lst.sort() | 使用<排序元素 | 
| lst.sort(comp) | 使用给定比较操作排序元素 | 
| lst.unique() | 调用erase删除同一个值的连续拷贝。使用==。 | 
| lst.unique(pred) | 调用erase删除同一个值的连续拷贝。使用给定的二元谓词。 | 
上面的操作都返回void
list和forward_list的splice**成员函数版本的参数:
| 参数 | 解释 | 
|---|---|
| (p, lst2) | p是一个指向lst中元素的迭代器,或者一个指向flst首前位置的迭代器。函数将lst2中的所有元素移动到lst中p之前的位置或是flst中p之后的位置。将元素从lst2中删除。lst2的类型必须和lst相同,而且不能是同一个链表。 | 
| (p, lst2, p2) | 同上,p2是一个指向lst2中位置的有效的迭代器,将p2指向的元素移动到lst中,或将p2之后的元素移动到flst中。lst2可以是于lst或flst相同的链表。 | 
| (p, lst2, b, e) | b和e表示lst2中的合法范围。将给定范围中的元素从lst2移动到lst或first中。lst2与lst可以使相同的链表,但p不能指向给定范围中的元素。 | 
使用lst.splice(args)或flst.splice_after(args)
关联容器
关联容器和顺序容器的不同:关联容器中的元素时按照关键字来保存和访问的。
关联容器支持通过关键字来高效地查找和读取元素,基本的关联容器类型是 map和 set。
关联容器类型:
| 容器类型 | 解释 | 
|---|---|
| 按顺序存储 | |
| map | 关键数组:保存关键字-值对 | 
| set | 关键字即值,即只保存关键字的容器 | 
| multimap | 支持同一个键多次出现的map | 
| multiset | 支持同一个键多次出现的set | 
| 无序集合 | |
| unordered_map | 用哈希函数组织的map | 
| unordered_set | 用哈希函数组织的set | 
| unordered_multimap | 哈希组织的map,关键字可以重复出现 | 
| unordered_multiset | 哈希组织的set,关键字可以重复出现 | 
关联容器概述
定义关联容器
需要指定元素类型。
列表初始化:
关键字类型的要求
对于有序容器,关键字类型必须定义元素比较的方法。默认是<。
如果想传递一个比较的函数,可以这样定义:multiset<Sales_data, decltype(compareIsbn)*> bookstore(compareIsbn);
pair
在utility头文件中定义。
一个pair保存两个数据成员,两个类型不要求一样。
pair的操作:
| 操作 | 解释 | 
|---|---|
| pair<T1, T2> p; | p是一个pair,两个类型分别是T1和T2的成员都进行了值初始化。 | 
| pair<T1, T2> p(v1, v2); | first和second分别用v1和v2进行初始化。 | 
| pair<T1, T2>p = {v1, v2}; | 等价于`p(v1, v2) | 
| make_pair(v1, v2); | pair的类型从v1和v2的类型推断出来。 | 
| p.first | 返回p的名为first的数据成员。 | 
| p.second | 返回p的名为second的数据成员。 | 
| p1 relop p2 | 运算关系符按字典序定义。 | 
| p1 == p2 | 必须两对元素两两相等 | 
| p1 != p2 | 同上 | 
关联容器操作
关联容器额外的类型别名
| 类型别名 | 解释 | 
|---|---|
| key_type | 此容器类型的关键字类型 | 
| mapped_type | 每个关键字关联的类型,只适用于map | 
| value_type | 对于map,是pair<const key_type, mapped_type>; 对于set,和key_type相同。 | 
关联容器迭代器
解引用一个关联容器迭代器时,会得到一个类型为容器的value_type的值的引用。
set的迭代器是const的。
遍历关联容器:使用begin和end,遍历map、multimap、set、multiset时,迭代器按关键字升序遍历元素。
添加元素
关联容器insert操作
insert操作 | 
关联容器 | 
|---|---|
| c.insert(v) c.emplace(args) | v是value_type类型的对象;args用来构造一个元素。 对于map和set,只有元素的关键字不存在c中才插入或构造元素。函数返回一个pair,包含一个迭代器,指向具有指定关键字的元素,以及一个指示插入是否成功的bool值。对于multimap和multiset则会插入范围中的每个元素。 | 
| c.insert(b, e) c.insert(il) | b和e是迭代器,表示一个c::value_type类型值的范围;il是这种值的花括号列表。函数返回void。对于 map和set,只插入关键字不在c中的元素。 | 
| c.insert(p, v) c.emplace(p, args) | 类似insert(v),但将迭代器p作为一个提示,指出从哪里开始搜索新元素应该存储的位置。返回一个迭代器,指向具有给定关键字的元素。 | 
向map添加元素:
1 | word_count.insert({word, 1}); | 
2 | word_count.insert(make_pair(word, 1)); | 
3 | word_count.insert(pair<string, size_t>(word, 1)); | 
4 | word_count.insert(map<string, size_t>::value_type (word, 1)); | 
删除元素
从关联容器中删除元素
| 操作 | 解释 | 
|---|---|
| c.erase(k) | 从c中删除每个关键字为k的元素。返回一个size_type值,指出删除的元素的数量。 | 
| c.erase(p) | 从c中删除迭代器p指定的元素。p必须指向c中一个真实元素,不能等于c.end()。返回一个指向p之后元素的迭代器,若p指向c中的尾元素,则返回c.end() | 
| c.erase(b, e) | 删除迭代器对b和e所表示范围中的元素。返回e。 | 
下标操作
map和unordered_map的下标操作
| 操作 | 解释 | 
|---|---|
| c[k] | 返回关键字为k的元素;如果k不在c中,添加一个关键字为k的元素,对其值初始化。 | 
| c.at(k) | 访问关键字为k的元素,带参数检查;若k不存在在c中,抛出一个out_of_range异常。 | 
查找元素
在一个关联容器中查找元素
| 操作 | 解释 | 
|---|---|
| c.find(k) | 返回一个迭代器,指向第一个关键字为k的元素,若k不在容器中,则返回尾后迭代器 | 
| c.count(k) | 返回关键字等于k的元素的数量。对于不允许重复关键字的容器,返回值永远是0或1。 | 
| c.lower_bound(k) | 返回一个迭代器,指向第一个关键字不小于k的元素。 | 
| c.upper_bound(k) | 返回一个迭代器,指向第一个关键字大于k的元素。 | 
| c.equal_range(k) | 返回一个迭代器pair,表示关键字等于k的元素的范围。若k不存在,pair的两个成员均等于c.end()。 | 
lower_bound和upper_bound不适用于无序容器。
下标和at操作只适用于非const的map和unordered_map。
无序容器
有序容器使用比较运算符来组织元素;无序容器使用哈希函数和关键字类型的==运算符。
理论上哈希技术可以获得更好的性能。
无序容器在存储上组织为一组桶(bucket),每个桶保存零个或多个元素。无序容器使用一个哈希函数将元素映射到桶。
无序容器管理操作
| 操作 | 解释 | 
|---|---|
| 桶接口 | |
| c.bucket_count() | 正在使用的桶的数目 | 
| c.max_bucket_count() | 容器能容纳的最多的桶的数目 | 
| c.bucket_size(n) | 第n个桶中有多少个元素 | 
| c.bucket(k) | 关键字为k的元素在哪个桶中 | 
| 桶迭代 | |
| local_iterator | 可以用来访问桶中元素的迭代器类型 | 
| const_local_iterator | 桶迭代器的const版本 | 
| c.begin(n),c.end(n) | 桶n的首元素迭代器 | 
| c.cbegin(n),c.cend(n) | 与前两个函数类似,但返回const_local_iterator。 | 
| 哈希策略 | |
| c.load_factor() | 每个桶的平均元素数量,返回float值。 | 
| c.max_load_factor() | c试图维护的平均比桶大小,返回float值。c会在需要时添加新的桶,以使得load_factor<=max_load_factor | 
| c.rehash(n) | 重组存储,使得bucket_count>=n,且bucket_count>size/max_load_factor | 
| c.reverse(n) | 重组存储,使得c可以保存n个元素且不必rehash。 | 
动态内存与智能指针
对象的生命周期:
- 全局对象在程序启动时分配,结束时销毁。
 - 局部对象在进入程序块时创建,离开块时销毁。
 - 局部
static对象在第一次使用前分配,在程序结束时销毁。 - 动态分配对象:只能显式地被释放。
 
对象的内存位置:
- 静态内存用来保存局部
static对象、类static对象、定义在任何函数之外的变量。 - 栈内存用来保存定义在函数内的非
static对象。 - 堆内存,又称自由空间,用来存储动态分配的对象。
 
动态内存与智能指针
动态内存管理:
new:在动态内存中为对象分配空间并返回一个指向该对象的指针。delete:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。
智能指针:
- 管理动态对象。
 - 行为类似常规指针。
 - 负责自动释放所指向的对象。
 - 智能指针也是模板。
 
shared_ptr类
shared_ptr和unique_ptr都支持的操作
| 操作 | 解释 | 
|---|---|
| shared_ptr | 
空智能指针,可以指向类型是T的对象 | 
| p | 将p用作一个条件判断,若p指向一个对象,则为true | 
| *p | 解引用p,获得它指向的对象。 | 
| p->mem | 等价于(*p).mem | 
| p.get() | 返回p中保存的指针,要小心使用,若智能指针释放了对象,返回的指针所指向的对象也就消失了。 | 
| swap(p, q) p.swap(q) | 交换p和q中的指针 | 
shared_ptr独有的操作
| 操作 | 解释 | 
|---|---|
| make_shared | 
返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args初始化此对象。 | 
| shared_ptr | 
p是shared_ptr q的拷贝;此操作会递增q中的计数器。q中的指针必须能转换为T* | 
| p = q | p和q都是shared_ptr,所保存的指针必须能互相转换。此操作会递减p的引用计数,递增q的引用计数;若p的引用计数变为0,则将其管理的原内存释放。 | 
| p.unique() | 若p.use_count()是1,返回true;否则返回false | 
| p.use_count() | 返回与p共享对象的智能指针数量;可能很慢,主要用于调试。 | 
使用动态内存的三种原因:
- 程序不知道自己需要使用多少对象(比如容器类)。
 - 程序不知道所需要对象的准确类型。
 - 程序需要在多个对象间共享数据。
 
直接管理内存
用new动态分配和初始化对象。
new无法为分配的对象命名(因为自由空间分配的内存是无名的),因此是返回一个指向该对象的指针。int *pi = new int(123);- 一旦内存耗尽,会抛出类型是
bad_alloc的异常。 
用delete将动态内存归还给系统。
- 接受一个指针,指向要释放的对象。
 delete后的指针称为空悬指针(dangling pointer)。
使用new和delete管理动态内存存在三个常见问题:
- 1.忘记
delete内存。 - 2.使用已经释放掉的对象。
 - 3.同一块内存释放两次。
 
坚持只使用智能指针可以避免上述所有问题。
shared_ptr和new结合使用
定义和改变shared_ptr的其他方法
| 操作 | 解释 | 
|---|---|
| shared_ptr | 
p管理内置指针q所指向的对象;q必须指向new分配的内存,且能够转换为T*类型 | 
| shared_ptr | 
p从unique_ptr u那里接管了对象的所有权;将u置为空 | 
| shared_ptr | 
p接管了内置指针q所指向的对象的所有权。q必须能转换为T*类型。p将使用可调用对象d来代替delete。 | 
| shared_ptr | 
p是shared_ptr p2的拷贝,唯一的区别是p将可调用对象d来代替delete。 | 
| p.reset() | 若p是唯一指向其对象的shared_ptr,reset会释放此对象。若传递了可选的参数内置指针q,会令p指向q,否则会将p置空。若还传递了参数d,则会调用d而不是delete来释放q。 | 
| p.reset(q) | 同上 | 
| p.reset(q, d) | 同上 | 
智能指针和异常
如果使用智能指针,即使程序块由于异常过早结束,智能指针类也能确保在内存不需要的时候将其释放。
智能指针陷阱:
- 不用相同的内置指针初始化(或
reset)多个智能指针 - 不
delete get()返回的指针。 - 如果你使用
get()返回的指针,记得当最后一个对应的智能指针销毁后,你的指针就无效了。 - 如果你使用智能指针管理的资源不是
new分配的内存,记住传递给它一个删除器。 
unique_ptr
某一个时刻只能有一个unique_ptr指向一个给定的对象。
不支持拷贝或者赋值操作。
向后兼容:auto_ptr:老版本,具有unique_ptr的部分特性。特别是,不能在容器中保存auto_ptr,也不能从函数返回auto_ptr。
unique_ptr操作
| 操作 | 解释 | 
|---|---|
| unique_ptr | 
空unique_ptr,可以指向类型是T的对象。u1会使用delete来是释放它的指针。 | 
| unique_ptr<T, D> u2 | u2会使用一个类型为D的可调用对象来释放它的指针。 | 
| unique_ptr<T, D> u(d) | 空unique_ptr,指向类型为T的对象,用类型为D的对象d代替delete | 
| u = nullptr | 释放u指向的对象,将u置为空。 | 
| u.release() | u放弃对指针的控制权,返回指针,并将u置空。 | 
| u.reset() | 释放u指向的对象 | 
| u.reset(q) | 令u指向q指向的对象 | 
| u.reset(nullptr) | 将u置空 | 
weak_ptr
weak_ptr是一种不控制所指向对象生存期的智能指针。
指向一个由shared_ptr管理的对象,不改变shared_ptr的引用计数。
一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,不管有没有weak_ptr指向该对象。
weak_ptr操作
| 操作 | 解释 | 
|---|---|
| weak_ptr | 
空weak_ptr可以指向类型为T的对象 | 
| weak_ptr | 
与shared_ptr指向相同对象的weak_ptr。T必须能转换为sp指向的类型。 | 
| w = p | p可以是shared_ptr或一个weak_ptr。赋值后w和p共享对象。 | 
| w.reset() | 将w置为空。 | 
| w.use_count() | 与w共享对象的shared_ptr的数量。 | 
| w.expired() | 若w.use_count()为0,返回true,否则返回false | 
| w.lock() | 如果expired为true,则返回一个空shared_ptr;否则返回一个指向w的对象的shared_ptr。 | 
动态数组
new和数组
new一个动态数组:
- 类型名之后加一对方括号,指明分配的对象数目(必须是整型,不必是常量)。
 - 返回指向第一个对象的指针。
 int *p = new int[size];
delete一个动态数组:
delete [] p;
unique_ptr和数组:
- 指向数组的
unique_ptr不支持成员访问运算符(点和箭头)。 
| 操作 | 解释 | 
|---|---|
| unique_ptr<T[]> u | u可以指向一个动态分配的数组,整数元素类型为T | 
| unique_ptr<T[]> u(p) | u指向内置指针p所指向的动态分配的数组。p必须能转换为类型T*。 | 
| u[i] | 返回u拥有的数组中位置i处的对象。u必须指向一个数组。 | 
allocator类
- 标准库
allocator类定义在头文件memory中,帮助我们将内存分配和对象构造分离开。 - 分配的是原始的、未构造的内存。
 allocator是一个模板。allocator<string> alloc;
标准库allocator类及其算法:
| 操作 | 解释 | 
|---|---|
| allocator | 
定义了一个名为a的allocator对象,它可以为类型为T的对象分配内存 | 
| a.allocate(n) | 分配一段原始的、未构造的内存,保存n个类型为T的对象。 | 
| a.deallocate(p, n) | 释放从T*指针p中地址开始的内存,这块内存保存了n个类型为T的对象;p必须是一个先前由allocate返回的指针。且n必须是p创建时所要求的大小。在调用deallocate之前,用户必须对每个在这块内存中创建的对象调用destroy。 | 
| a.construct(p, args) | p必须是一个类型是T*的指针,指向一块原始内存;args被传递给类型为T的构造函数,用来在p指向的内存中构造一个对象。 | 
| a.destroy(p) | p为T*类型的指针,此算法对p指向的对象执行析构函数。 | 
allocator伴随算法
| 操作 | 解释 | 
|---|---|
| uninitialized_copy(b, e, b2) | 从迭代器b和e给定的输入范围中拷贝元素到迭代器b2指定的未构造的原始内存中。b2指向的内存必须足够大,能够容纳输入序列中元素的拷贝。 | 
| uninitialized_copy_n(b, n, b2) | 从迭代器b指向的元素开始,拷贝n个元素到b2开始的内存中。 | 
| uninitialized_fill(b, e, t) | 在迭代器b和e执行的原始内存范围中创建对象,对象的值均为t的拷贝。 | 
| uninitialized_fill_n(b, n, t) | 从迭代器b指向的内存地址开始创建n个对象。b必须指向足够大的未构造的原始内存,能够容纳给定数量的对象。 | 
- 定义在头文件
memory中。 - 在给定目的位置创建元素,而不是由系统分配内存给他们。